diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3142e0ff97749..53270e4517192 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -109,6 +109,9 @@ /x-pack/legacy/plugins/alerting @elastic/kibana-alerting-services /x-pack/legacy/plugins/actions @elastic/kibana-alerting-services /x-pack/legacy/plugins/task_manager @elastic/kibana-alerting-services +/x-pack/test/alerting_api_integration @elastic/kibana-alerting-services +/x-pack/test/plugin_api_integration/plugins/task_manager @elastic/kibana-alerting-services +/x-pack/test/plugin_api_integration/test_suites/task_manager @elastic/kibana-alerting-services # Design **/*.scss @elastic/kibana-design diff --git a/.gitignore b/.gitignore index e7391a5c292d0..02b20da297fc6 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,6 @@ disabledPlugins webpackstats.json /config/* !/config/kibana.yml -!/config/apm.js coverage selenium .babel_register_cache.json diff --git a/.i18nrc.json b/.i18nrc.json index ddde50b62a8f3..23f3d6ee33829 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -21,7 +21,7 @@ "kbn": "src/legacy/core_plugins/kibana", "kbnDocViews": "src/legacy/core_plugins/kbn_doc_views", "kbnVislibVisTypes": "src/legacy/core_plugins/kbn_vislib_vis_types", - "management": "src/legacy/core_plugins/management", + "management": ["src/legacy/core_plugins/management", "src/plugins/management"], "kibana_react": "src/legacy/core_plugins/kibana_react", "kibana-react": "src/plugins/kibana_react", "kibana_utils": "src/plugins/kibana_utils", diff --git a/.node-version b/.node-version index 95abd2ac49910..06c9b9d306348 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -10.15.2 +10.18.0 diff --git a/.nvmrc b/.nvmrc index 95abd2ac49910..06c9b9d306348 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -10.15.2 +10.18.0 diff --git a/config/apm.js b/config/apm.js deleted file mode 100644 index 0cfcd759f163b..0000000000000 --- a/config/apm.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * DO NOT EDIT THIS FILE! - * - * This file contains the configuration for the Elastic APM instrumentaion of - * Kibana itself and is only intented to be used during development of Kibana. - * - * Instrumentation is turned off by default. Once activated it will send APM - * data to an Elasticsearch cluster accessible by Elastic employees. - * - * To modify the configuration, either use environment variables, or create a - * file named `config/apm.dev.js`, which exports a config object as described - * in the docs. - * - * For an overview over the available configuration files, see: - * https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html - * - * For general information about Elastic APM, see: - * https://www.elastic.co/guide/en/apm/get-started/current/index.html - */ - -const { readFileSync } = require('fs'); -const { join } = require('path'); -const { execSync } = require('child_process'); -const merge = require('lodash.merge'); - -module.exports = merge( - { - active: false, - serverUrl: 'https://f1542b814f674090afd914960583265f.apm.us-central1.gcp.cloud.es.io:443', - // The secretToken below is intended to be hardcoded in this file even though - // it makes it public. This is not a security/privacy issue. Normally we'd - // instead disable the need for a secretToken in the APM Server config where - // the data is transmitted to, but due to how it's being hosted, it's easier, - // for now, to simply leave it in. - secretToken: 'R0Gjg46pE9K9wGestd', - globalLabels: {}, - centralConfig: false, - logUncaughtExceptions: true, - }, - devConfig() -); - -const rev = gitRev(); -if (rev !== null) module.exports.globalLabels.git_rev = rev; - -try { - const filename = join(__dirname, '..', 'data', 'uuid'); - module.exports.globalLabels.kibana_uuid = readFileSync(filename, 'utf-8'); -} catch (e) {} // eslint-disable-line no-empty - -function gitRev() { - try { - return execSync('git rev-parse --short HEAD', { - encoding: 'utf-8', - stdio: ['ignore', 'pipe', 'ignore'], - }).trim(); - } catch (e) { - return null; - } -} - -function devConfig() { - try { - return require('./apm.dev'); // eslint-disable-line import/no-unresolved - } catch (e) { - return {}; - } -} diff --git a/docs/discover/context.asciidoc b/docs/discover/context.asciidoc index 9049109d6124d..c402a734a16fa 100644 --- a/docs/discover/context.asciidoc +++ b/docs/discover/context.asciidoc @@ -1,90 +1,66 @@ [[document-context]] -== Viewing Document Context +== Viewing a document in context -For certain applications it can be useful to inspect a window of documents -surrounding a specific event. The context view enables you to do just that for -<> that are configured to contain time-based events. +Once you've narrowed your search to a specific event, +you might want to inspect the documents that occurred +immediately before and after the event. With the Context view, +you can do just that for index patterns that contain time-based events. -To show the context surrounding an anchor document, click the *Expand* button -image:images/ExpandButton.jpg[Expand Button] to the left of the document's -table entry and then click the *View surrounding documents* link. +To open the Context view, click the expand icon (<) in the document table, and then click +*View surrounding documents.* -image::images/Expanded-Document.png[Expanded Document] -{nbsp} +The documents are sorted +by the time field specified in the index pattern and displayed using the +same set of columns as the *Discover* view from which the context was opened. +The anchor document is highlighted in blue. -The context view displays a number of documents before and after the anchor -document. The anchor document itself is highlighted in blue. The view is sorted -by the time field specified in the index pattern configuration and uses the -same set of columns as the Discover view the context was opened from. If there -are multiple documents with the same time field value, the internal document -order is used as a secondary sorting criterion by default. - -[NOTE] --- -The field used for tiebreaking in case of equal time field values can be -configured using the advanced setting `context:tieBreakerFields` in -< Advanced Settings*>>, which defaults to the -`_doc` field. The value of this setting can be a comma-separated list of field -names, which will be checked in sequence for suitability when a context is -about to be displayed. The first suitable field is then used as the tiebreaking -field. A field is suitable if the field exists and is sortable in the index -pattern the context is based on. - -While not required, it is recommended to only -use fields which have {ref}/doc-values.html[doc values] enabled to achieve -good performance and avoid unnecessary {ref}/modules-fielddata.html[field -data] usage. Common examples for suitable fields include log line numbers, -monotonically increasing counters and high-precision timestamps. --- +[role="screenshot"] image::images/Discover-ContextView.png[Context View] -NOTE: The number of documents displayed by default can be configured -via the `context:defaultSize` setting in < -Advanced Settings*>>. - [float] -[[change-context-size]] -=== Changing the Context Size - -You can change the number documents displayed before and after the anchor -document independently. - -To increase the number of displayed documents that are newer than the anchor -document, click the *Load 5 more* button above the document list or enter the -desired number into the input box right of the button. - -image::images/Discover-ContextView-SizePicker-Newer.png[] -{nbsp} - -To increase the number of displayed documents that are older than the anchor -document, click the *Load 5 more* button below the document list or enter the -desired number into the input box right of the button. +[[filter-context]] +=== Filter the context -image::images/Discover-ContextView-SizePicker-Older.png[] -{nbsp} +The +filters you applied in *Discover* are carried over to the Context view. Pinned filters remain active, while normal +filters are copied in a disabled state. You can re-enable these filters to +refine your context view. -NOTE: The default number of documents loaded with each button click can be -configured via the `context:step` setting in < -Advanced Settings*>>. +If the Context view contains a large number of documents not related to the event under +investigation, you can use filters to restrict the documents to +display. [float] -[[filter-context]] -=== Filtering the Context - -Depending on how the documents are partitioned into index patterns, the context -view might contain a large number of documents not related to the event under -investigation. In order to adapt the focus of the context view to the task at -hand, you can use filters to restrict the documents considered by Kibana for -display in the context view. - -When switching from the discover view to the context view, the previously -applied filters are carried over. Pinned filters remain active while normal -filters are copied in a disabled state. You can selectively re-enabled them to -refine your context view. +[[change-context-size]] +=== Change the number of surrounding documents -New filters can be added via the *Add a filter* link in the filter bar, by -clicking the filter icons appearing when hovering a field, or by expanding -documents and clicking the filter icons in the table. +By default, the five newest and oldest +documents are listed. To increase the number of documents that surround the anchor document, +click *Load*. Five documents are added with each click. -image::images/Discover-ContextView-FilterMontage.png[] +[float] +[[configure-context-ContextView]] +=== Configure the context view + +To configure the Context view, use these settings in <>. + +[horizontal] +`context:defaultSize`:: The number of documents to display by default. +`context:step`:: The default number of documents to load with each button click. +`context:tieBreakerFields`:: The field to use for tiebreaking in case of equal time field values. +The default is the +`_doc` field. ++ +You can enter a comma-separated list of field +names, which is checked in sequence for suitability when a context is +displayed. The first suitable field is used as the tiebreaking +field. A field is suitable if the field exists and is sortable in the index +pattern the context is based on. ++ +Although not required, it is recommended to only +use fields that have {ref}/doc-values.html[doc values] enabled to achieve +good performance and avoid unnecessary {ref}/modules-fielddata.html[field +data] usage. Common examples for suitable fields include log line numbers, +monotonically increasing counters and high-precision timestamps. diff --git a/docs/discover/document-data.asciidoc b/docs/discover/document-data.asciidoc index dc6a45dc5ad7e..b45a31065aa9a 100644 --- a/docs/discover/document-data.asciidoc +++ b/docs/discover/document-data.asciidoc @@ -1,69 +1,55 @@ [[document-data]] -== Viewing Document Data +== Viewing document data -When you submit a search query, the 500 most recent documents that match the query -are listed in the Documents table. You can configure the number of documents shown -in the table by setting the `discover:sampleSize` property in <>. By default, the table shows the localized version of the time -field configured for the selected <> and the document `_source`. You can -<> from the Fields list. -You can <> by any indexed field that's included -in the table. - -To view a document's field data, click the *Expand* button -image:images/ExpandButton.jpg[Expand Button] to the left of the document's table -entry. - -image::images/Expanded-Document.png[] - -To view the original JSON document (pretty-printed), click the *JSON* tab. - -To view the document data as a separate page, click the *View single document* -link. You can bookmark and share this link to provide direct access to a -particular document. - -To display or hide a field's column in the Documents table, click the -image:images/add-column-button.png[Add Column] *Toggle column in table* button. - -To collapse the document details, click the *Collapse* button -image:images/CollapseButton.jpg[Collapse Button]. +When you submit a search query in *Discover*, the most recent documents that match the query +are listed in the documents table. +By default, the table includes columns for +the time field and the document `_source`, which shows all fields and values in the document. [float] [[sorting]] -=== Sorting the Document List -You can sort the documents in the Documents table by the values in any indexed -field. If a time field is configured for the current index pattern, the -documents are sorted in reverse chronological order by default. - -To change the sort order, hover over the name of the field you want to sort by -and click the sort button. Click again to reverse the sort order. +=== Modify the document table + +Use the following commands to +tailor the documents table to suit your needs. + +[horizontal] +Add a field column:: +Hover over the list of *Available fields* and then click *add* next to each field you want include as a column in the table. +The first field you add replaces the `_source` column. +Change sort order:: By default, columns are sorted by the values in the field. +If a time field is configured for the current index pattern, +the documents are sorted in reverse chronological order. ++ +To change the sort order, hover over the column +and click image:images/sort-icon.png[]. +The first click sorts by ascending order, the second click sorts by descending order, and the third +click removes the field from the sorted fields. + +Move a field column:: Hover over the column header and click the move left (<<) or move right icon (>>). +Remove a field column :: Hover over the list of *Specified fields* +and then click *remove*. +Or, use the (x) control in the column header. [float] -[[adding-columns]] -=== Adding Field Columns to the Documents Table -By default, the Documents table shows the localized version of the time field -that's configured for the selected index pattern and the document `_source`. -You can add fields to the table from the Fields list or from a document's -field data. - -To add a field column from the Fields list, hover over the field and click its -*add* button. +=== Drill down into field-level details +To view the document data in either table or JSON format, click the expand icon (>). +The expanded view provides these options for viewing your document: -To add a field column from a document's field data, expand the document -and click the field's -image:images/add-column-button.png[Add Column] *Toggle column in table* button. +* View the events that surround your document. +For example, you might want to see the 10 documents that occurred +immediately before and after your event. -Added field columns replace the `_source` column in the Documents table. The added -fields are also added to the *Selected Fields* list. +* View the document data as a separate page. You can bookmark and +share the link for direct access to a particular document. -To rearrange the field columns, hover over the header of the column you want to move -and click the *Move left* or *Move right* button. +[role="screenshot"] +image::images/Expanded-Document.png[] -image:images/Discover-MoveColumn.jpg[Move Column] [float] -[[removing-columns]] -=== Removing Field Columns from the Documents Table -To remove a field column from the Documents table, hover over the header of the -column you want to remove and click the *Remove* button -image:images/RemoveFieldButton.jpg[Remove Field Button]. \ No newline at end of file +=== Configure the number of documents to show + +By default, the documents table includes the 500 most recent documents that +match the query. To change this number, set the `discover:sampleSize` property in <>. diff --git a/docs/discover/field-filter.asciidoc b/docs/discover/field-filter.asciidoc index 5646fe079401e..49bb6078cdc58 100644 --- a/docs/discover/field-filter.asciidoc +++ b/docs/discover/field-filter.asciidoc @@ -1,127 +1,132 @@ [[field-filter]] -== Filtering by Field -You can filter the search results to display only those documents that contain -a particular value in a field. You can also create negative filters that -exclude documents that contain the specified field value. +== Filtering by field -You add field filters from the Fields list, the Documents table, or by manually -adding a filter. In addition to creating positive and negative filters, the -Documents table enables you to filter on whether or not a field is present. The -applied filters are shown below the Query bar. Negative filters are shown in red. +*Discover* offers +various types of filters, so you can restrict your documents to the exact data you want. +For example, you might look at the results for a +particular period of time. Or, you might include—or exclude— +all HTTP redirects that come from a specific IP and port. -To add a filter from the Fields list: +[float] +=== Add a filter + +A quick way to add a filter is from the fields list. -. Click the name of the field you want to filter on. This displays the top -five values for that field. +. Click the field to filter on. ++ +You'll see the number of documents that contain +the field, the top 5 values for the field, and the percentage of documents +that contain each value. + [role="screenshot"] image::images/filter-field.png[height=317] -. To add a positive filter, click the *Positive Filter* button -image:images/PositiveFilter.jpg[Positive Filter]. -This includes only those documents that contain that value in the field. -. To add a negative filter, click the *Negative Filter* button -image:images/NegativeFilter.jpg[Negative Filter]. -This excludes documents that contain that value in the field. - -To add a filter from the Documents table: - -. Expand a document in the Documents table by clicking the *Expand* button -image:images/ExpandButton.jpg[Expand Button] to the left of the document's -table entry. + +. Use the image:images/PositiveFilter.jpg[Positive Filter] icon to +show only documents that contain that value, +or image:images/NegativeFilter.jpg[Negative Filter] to exclude all documents with that value. + -image::images/Expanded-Document.png[] -. To add a positive filter, click the *Positive Filter* button -image:images/PositiveFilter.jpg[Positive Filter Button] to the right of the -field name. This includes only those documents that contain that value in the -field. -. To add a negative filter, click the *Negative Filter* button -image:images/NegativeFilter.jpg[Negative Filter Button] to the right of the -field name. This excludes documents that contain that value in the field. -. To filter on whether or not documents contain the field, click the -*Exists* button image:images/ExistsButton.jpg[Exists Button] to the right of the -field name. This includes only those documents that contain the field. - -To manually add a filter: - -. Click *Add Filter*. A popup will be displayed for you to create the filter. - -. Choose a field to filter by. This list of fields will include fields from the -index pattern you are currently querying against. +If there is no data to display, you might need to set a <>. +You can choose a time from the quick filter or choose your +own using absolute or relative times. + +. Try also these filtering options: ++ +* To limit the field +list to a particular data type, click *Filter by type*. +You can also filter for whether that type is +aggregatable or searchable. + -image::images/add_filter_field.png[] -. Choose an operation for your filter. +* To filter for whether a field is present, expand the document in +the document table, hover over the field, and click the *Filter for field present* icon. + +[float] +=== Filter by condition + +You can filter using advanced criteria, +such as if a value is equal to or in between certain values. + +. Click *Add Filter*. + +. Select a field. + +. Select an operation for your filter: + -image::images/add_filter_operator.png[] -The following operators can be selected: [horizontal] -`is`:: Filter where the value for the field matches the given value. -`is not`:: Filter where the value for the field does not match the given value. -`is one of`:: Filter where the value for the field matches one of the specified values. -`is not one of`:: Filter where the value for the field does not match any of the specified values. -`is between`:: Filter where the value for the field is in the given range. -`is not between`:: Filter where the value for the field is not in the given range. -`exists`:: Filter where any value is present for the field. -`does not exist`:: Filter where no value is present for the field. -. Choose the value(s) for your filter. Values from your indices may be suggested -as selections if you are filtering against an aggregatable field. +`is`:: The value for the field matches the given value. +`is not`:: The value for the field does not match the given value. +`is one of`:: The field matches one of the specified values. +`is not one of`:: The value for the field does not match any of the specified values. +`is between`:: The value for the field is in the given range. +`is not between`:: The value for the field is not in the given range. +`exists`:: Any value is present for the field. +`does not exist`:: No value is present for the field. +. Choose values for your filter. + -image::images/add_filter_value.png[] -. (Optional) Specify a label for the filter. If you specify a label, it will be -displayed below the query bar instead of the filter definition. -. Click *Save*. The filter will be applied to your search and be displayed below -the query bar. +Values from your indices may be suggested +as selections if you are filtering against an aggregatable field. +. (Optional) Specify a label for the filter. + +. Click *Save* to apply the filter to your search. ++ NOTE: If you are experiencing long-running queries as a result of the value suggestions, you can -turn off the suggestions by setting the advanced setting, `filterEditor:suggestValues`, to `false`. +turn off the suggestions by setting `filterEditor:suggestValues` to `false` +in <>. [float] [[filter-pinning]] -=== Managing Filters +=== Edit, disable, and delete filters + +To modify a filter, click its tag, and then select one of the following actions. + +*Pin across all apps*:: +Persist the filter +when you switch contexts in Kibana. For example, you can pin a filter +in *Discover* and it remains in place when you switch to *Visualize*. +A filter is based on a particular index field—if the indices being +searched do not contain the field in a pinned filter, it has no effect. -To modify a filter, click on it and click one of the action buttons. +*Edit filter*:: +Edit the +filter definition and label. -image::images/filter-allbuttons.png[] +*Exclude results*:: +Switch from a positive +filter to a negative filter, and vice versa. -  +*Temporarily disable*:: +Disable the filter without +removing it. Click again to reenable the filter. + +*Delete*:: +Delete the filter. + +To apply an action to all filters, +click the *Actions* icon, and then select the action. -Pin across all apps :: Pinned filters -persist when you switch contexts in Kibana. For example, you can pin a filter -in Discover and it remains in place when you switch to Visualize. -Note that a filter is based on a particular index field--if the indices being -searched don't contain the field in a pinned filter, it has no effect. -Edit Filter :: <> definition. Enables you to manually update the filter and -specify a label for the filter. -Exclude results :: Switch from a positive -filter to a negative filter and vice-versa. -Temporarily disable :: Disable the filter without -removing it. Click again to reenable the filter. Diagonal stripes indicate -that a filter is disabled. -Remove Filter :: Remove the filter. -To apply a filter action to all of the applied filters, -click *Actions* and select the action. [float] [[filter-edit]] -=== Editing a Filter -You can edit a filter by changing the field, operator, or value associated -with the filter (see the Add Filter section above), or by directly modifying -the filter query that is performed to filter your search results. This -enables you to create more complex filters that are based on multiple fields. - -. To edit the filter query, first click the edit button for the filter, then -click *Edit Query DSL*. -+ -image::images/edit_filter_query.png[] -. You can then edit the query for the filter. -+ -image::images/edit_filter_query_json.png[] +=== Modify the filter query -For example, you could use a -{ref}/query-dsl-bool-query.html[bool query] to create a filter for the -sample log data that displays the hits that originated from Canada or China that resulted in a 404 error: +You can directly modify +the query that filters your search results. This enables you +to create more complex filters using multiple fields. +. Click the filter tag, and then select *Edit > Edit Query DSL*. + +. Edit the query for the filter. ++ +//// +image::images/edit_filter_query_json.png[] ++ +//// +For example, if you are using the sample log data, you can use the +{ref}/query-dsl-bool-query.html[bool query] to create a filter +that displays the hits that originated from Canada or China that resulted in a 404 error: ++ ========== [source,json] { diff --git a/docs/discover/images/move-icon.png b/docs/discover/images/move-icon.png new file mode 100644 index 0000000000000..3fa1f9e2f1a59 Binary files /dev/null and b/docs/discover/images/move-icon.png differ diff --git a/docs/discover/images/sort-icon.png b/docs/discover/images/sort-icon.png new file mode 100644 index 0000000000000..7dd3719ec361b Binary files /dev/null and b/docs/discover/images/sort-icon.png differ diff --git a/docs/discover/viewing-field-stats.asciidoc b/docs/discover/viewing-field-stats.asciidoc index 96a26c78596e2..3631aba73fb20 100644 --- a/docs/discover/viewing-field-stats.asciidoc +++ b/docs/discover/viewing-field-stats.asciidoc @@ -1,14 +1,14 @@ [[viewing-field-stats]] == Viewing Field Data Statistics -From the Fields list, you can see how many of the documents in the Documents +From the fields list, you can see how many of the documents in the documents table contain a particular field, what the top 5 values are, and what percentage of documents contain each value. -Data can be visualized in various ways. The quick visualize can only be -applied to aggregatable fields. The keyword fields can be visualized and -they are available in the side bar if we uncheck "Hide missing fields". +You can visualize data in various ways. You can only apply the quick visualize +to aggregatable fields. You can visualize the keyword fields, and +they are available in the side bar if you uncheck "Hide missing fields". -To view field data statistics, click the name of a field in the Fields list. +To view field data statistics, click the name of a field in the fields list. -image:images/filter-field.png[Field Statistics,height=317] \ No newline at end of file +image:images/filter-field.png[Field Statistics,height=317] diff --git a/docs/images/Discover-ContextView-SizePicker-Newer.png b/docs/images/Discover-ContextView-SizePicker-Newer.png deleted file mode 100644 index 852cb22c1f27c..0000000000000 Binary files a/docs/images/Discover-ContextView-SizePicker-Newer.png and /dev/null differ diff --git a/docs/images/Discover-ContextView-SizePicker-Older.png b/docs/images/Discover-ContextView-SizePicker-Older.png deleted file mode 100644 index 38cd9acd1bee0..0000000000000 Binary files a/docs/images/Discover-ContextView-SizePicker-Older.png and /dev/null differ diff --git a/docs/images/Discover-ContextView.png b/docs/images/Discover-ContextView.png index 11d4a59c26e55..5c2de602f6b41 100644 Binary files a/docs/images/Discover-ContextView.png and b/docs/images/Discover-ContextView.png differ diff --git a/docs/images/Discover-Start.png b/docs/images/Discover-Start.png index 680aa3767bdf0..27e7a2c728597 100644 Binary files a/docs/images/Discover-Start.png and b/docs/images/Discover-Start.png differ diff --git a/docs/images/Expanded-Document.png b/docs/images/Expanded-Document.png index ad2f0db1a7ff9..4fa255e79a8ad 100644 Binary files a/docs/images/Expanded-Document.png and b/docs/images/Expanded-Document.png differ diff --git a/docs/setup/docker.asciidoc b/docs/setup/docker.asciidoc index f3e7273adedee..8fd7b0490e194 100644 --- a/docs/setup/docker.asciidoc +++ b/docs/setup/docker.asciidoc @@ -5,7 +5,7 @@ base image is https://hub.docker.com/_/centos/[centos:7]. A list of all published Docker images and tags is available at https://www.docker.elastic.co[www.docker.elastic.co]. The source code is in -https://github.com/elastic/kibana-docker/tree/{branch}[GitHub]. +https://github.com/elastic/dockerfiles/tree/{branch}/kibana[GitHub]. These images are free to use under the Elastic license. They contain open source and free commercial features and access to paid commercial features. diff --git a/docs/user/discover.asciidoc b/docs/user/discover.asciidoc index fa583918703f3..36d6b0a6e473a 100644 --- a/docs/user/discover.asciidoc +++ b/docs/user/discover.asciidoc @@ -3,15 +3,100 @@ [partintro] -- -*Discover* enables you to explore your data with {kib}'s data discovery functions. -You have access to every document in every index that matches the selected <>. -You can submit search queries, filter the search results, and view document data. -You can also see the number of documents that match the search query and get field value statistics. -If a time field is configured for the selected index pattern, the distribution of -documents over time is displayed in a histogram at the top of the page. +When you know what your data includes, you can create visualizations +that best display that data and build better dashboards. +*Discover* enables you to explore your data, find +hidden insights and relationships, and get answers to your questions. + +With *Discover*, you can: + +* Access every document in every index that matches your selected index pattern +* Search your data and filter the search results +* Get field-level details about the documents that match your search +* View the events that occurred just before and after a document [role="screenshot"] image::images/Discover-Start.png[Discover] + + +[float] +=== Set up your index pattern + +The first thing to do in *Discover* is to select an <>, which +defines the data you want to explore and visualize. The current index pattern is in the upper left. +If you haven't yet created an index pattern, you can add a <>, +which has a pre-built index pattern. + +[float] +=== Set a time filter + +By default, *Discover* shows data for the last 15 minutes. +If you have a time-based index, and no data displays, +you might need to increase the time range. Using the <> in the upper right, +you can specify a common or recently-used time range, a relative time +from now, or an absolute time range. + +[float] +=== Search your data + +Now that you have your data and set the time span, you can start asking your questions. +You can search your data using the <>, +which offers a simplified query syntax. +For example, if +you search for `day_of_week : Friday`, you'll get a list of all documents +in which `day_of_week` is set to `Friday`. If you prefer +<>, you can access it from the KQL menu. + +[float] +=== Filter your search results + +Next, you'll want narrow your search results to a more manageable data set. +When you click on a name in the field list, you'll see +the top five values for the field, the number of documents that contain the field, +and the percentage of documents that contain each value. From this view, you can +use the (+) magnifier icon to quickly find all +documents that have that value, or (-) to exclude all +documents with that value. For more filter options, see <>. + +[role="screenshot"] +image::images/filter-field.png[height=317] + + +[float] +=== Add and remove fields + +The sortable documents table +lists the documents that match your search. +By default, the table includes columns for the time field and the document `_source`. +To zero in on a specific field, click *add* next to the field name in the left sidebar. +For example, if you add the `currency`, `customer_last_name`, and `day_of_week` fields, +the document table includes columns for those three fields. + +[float] +=== Examine document contents + +From the documents table, you can expand a document to +examine its field data in either table or JSON format. +The table view provides yet another filtering option—filtering for whether the field +is present. See <> for details. + +[float] +=== View a document in context + +Suppose you're troubleshooting your data, and you've narrowed down your results to a single document. +Now you want to to see the events that occurred just before and after the +document that you are looking at. You can do that by expanding the document and +clicking <>. + +[float] +=== Save and share your search + +Finally, its time to save and share your data. You can export your data as a CSV file +or create a direct link to share. The *Save* and *Share* actions are in the menu bar. + + + + -- include::{kib-repo-dir}/discover/set-time-filter.asciidoc[] diff --git a/package.json b/package.json index 9ac3c89e14c8e..a771a130d08b1 100644 --- a/package.json +++ b/package.json @@ -110,8 +110,9 @@ ] }, "dependencies": { - "@babel/core": "^7.7.5", - "@babel/register": "^7.7.4", + "@babel/core": "^7.5.5", + "@babel/register": "^7.7.0", + "@elastic/apm-rum": "^4.6.0", "@elastic/charts": "^14.0.0", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "1.0.5", @@ -152,12 +153,12 @@ "cache-loader": "^4.1.0", "chalk": "^2.4.2", "check-disk-space": "^2.1.0", - "chokidar": "3.3.0", + "chokidar": "3.2.1", "color": "1.0.3", "commander": "3.0.2", "compare-versions": "3.5.1", - "core-js": "^3.5.0", - "css-loader": "3.3.2", + "core-js": "^3.2.1", + "css-loader": "2.1.1", "custom-event-polyfill": "^0.3.0", "d3": "3.5.17", "d3-cloud": "1.2.5", @@ -167,12 +168,12 @@ "elasticsearch": "^16.5.0", "elasticsearch-browser": "^16.5.0", "encode-uri-query": "1.0.1", - "execa": "^3.4.0", + "execa": "^3.2.0", "expiry-js": "0.1.7", "fast-deep-equal": "^3.1.1", - "file-loader": "5.0.2", + "file-loader": "4.2.0", "font-awesome": "4.7.0", - "getos": "^3.1.1", + "getos": "^3.1.0", "glob": "^7.1.2", "glob-all": "^3.1.0", "globby": "^8.0.1", @@ -221,7 +222,7 @@ "proxy-from-env": "1.0.0", "pug": "^2.0.4", "querystring-browser": "1.0.4", - "raw-loader": "4.0.0", + "raw-loader": "3.1.0", "react": "^16.12.0", "react-color": "^2.13.8", "react-dom": "^16.12.0", @@ -248,10 +249,10 @@ "script-loader": "0.7.2", "semver": "^5.5.0", "style-it": "^2.1.3", - "style-loader": "1.0.1", + "style-loader": "0.23.1", "symbol-observable": "^1.2.0", "tar": "4.4.13", - "terser-webpack-plugin": "^2.3.0", + "terser-webpack-plugin": "^2.1.2", "thread-loader": "^2.1.3", "tinygradient": "0.4.3", "tinymath": "1.2.1", @@ -259,25 +260,25 @@ "tslib": "^1.9.3", "type-detect": "^4.0.8", "ui-select": "0.19.8", - "url-loader": "3.0.0", + "url-loader": "2.2.0", "uuid": "3.3.2", - "val-loader": "^2.0.2", + "val-loader": "^1.1.1", "validate-npm-package-name": "2.2.2", "vega-lib": "4.3.0", "vega-lite": "^2.6.0", "vega-schema-url-parser": "1.0.0", "vega-tooltip": "^0.12.0", "vision": "^5.3.3", - "webpack": "4.41.2", + "webpack": "4.41.0", "webpack-merge": "4.2.2", "whatwg-fetch": "^3.0.0", "wrapper-webpack-plugin": "^2.1.0", "yauzl": "2.10.0" }, "devDependencies": { - "@babel/parser": "^7.7.5", - "@babel/plugin-syntax-dynamic-import": "^7.7.4", - "@babel/types": "^7.7.4", + "@babel/parser": "^7.5.5", + "@babel/plugin-syntax-dynamic-import": "^7.2.0", + "@babel/types": "^7.5.5", "@elastic/elasticsearch": "^7.4.0", "@elastic/eslint-config-kibana": "0.15.0", "@elastic/eslint-plugin-eui": "0.0.2", @@ -298,7 +299,7 @@ "@testing-library/react-hooks": "^3.2.1", "@types/angular": "^1.6.56", "@types/angular-mocks": "^1.7.0", - "@types/babel__core": "^7.1.3", + "@types/babel__core": "^7.1.2", "@types/bluebird": "^3.1.1", "@types/boom": "^7.2.0", "@types/chance": "^1.0.0", @@ -310,7 +311,7 @@ "@types/delete-empty": "^2.0.0", "@types/elasticsearch": "^5.0.33", "@types/enzyme": "^3.9.0", - "@types/eslint": "^6.1.3", + "@types/eslint": "^6.1.2", "@types/fetch-mock": "^7.3.1", "@types/getopts": "^2.0.1", "@types/glob": "^7.1.1", @@ -356,7 +357,7 @@ "@types/sinon": "^7.0.13", "@types/strip-ansi": "^3.0.0", "@types/styled-components": "^4.4.1", - "@types/supertest": "^2.0.8", + "@types/supertest": "^2.0.5", "@types/supertest-as-promised": "^2.0.38", "@types/testing-library__react": "^9.1.2", "@types/testing-library__react-hooks": "^3.1.0", @@ -364,8 +365,8 @@ "@types/uuid": "^3.4.4", "@types/vinyl-fs": "^2.4.11", "@types/zen-observable": "^0.8.0", - "@typescript-eslint/eslint-plugin": "^2.10.0", - "@typescript-eslint/parser": "^2.10.0", + "@typescript-eslint/eslint-plugin": "^2.12.0", + "@typescript-eslint/parser": "^2.12.0", "angular-mocks": "^1.7.8", "archiver": "^3.1.1", "axe-core": "^3.3.2", @@ -385,25 +386,25 @@ "enzyme-adapter-react-16": "^1.15.1", "enzyme-adapter-utils": "^1.12.1", "enzyme-to-json": "^3.4.3", - "eslint": "^6.7.2", - "eslint-config-prettier": "^6.7.0", + "eslint": "^6.5.1", + "eslint-config-prettier": "^6.4.0", "eslint-plugin-babel": "^5.3.0", "eslint-plugin-ban": "^1.3.0", - "eslint-plugin-cypress": "^2.8.0", - "eslint-plugin-import": "^2.19.1", + "eslint-plugin-cypress": "^2.7.0", + "eslint-plugin-import": "^2.18.2", "eslint-plugin-jest": "^22.19.0", "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-mocha": "^6.2.0", "eslint-plugin-no-unsanitized": "^3.0.2", "eslint-plugin-node": "^10.0.0", "eslint-plugin-prefer-object-spread": "^1.2.1", - "eslint-plugin-prettier": "^3.1.2", + "eslint-plugin-prettier": "^3.1.1", "eslint-plugin-react": "^7.16.0", "eslint-plugin-react-hooks": "^2.1.2", "exit-hook": "^2.2.0", "faker": "1.1.0", "fetch-mock": "^7.3.9", - "geckodriver": "^1.19.1", + "geckodriver": "^1.19.0", "getopts": "^2.2.4", "grunt": "1.0.4", "grunt-available-tasks": "^0.6.3", @@ -422,7 +423,7 @@ "jest": "^24.9.0", "jest-cli": "^24.9.0", "jest-raw-loader": "^1.0.1", - "jimp": "0.9.3", + "jimp": "0.8.4", "json5": "^1.0.1", "karma": "3.1.4", "karma-chrome-launcher": "2.2.0", @@ -453,10 +454,10 @@ "regenerate": "^1.4.0", "sass-lint": "^1.12.1", "selenium-webdriver": "^4.0.0-alpha.5", - "simple-git": "1.129.0", + "simple-git": "1.116.0", "sinon": "^7.4.2", "strip-ansi": "^3.0.1", - "supertest": "^4.0.2", + "supertest": "^3.1.0", "supertest-as-promised": "^4.0.2", "tree-kill": "^1.2.1", "typescript": "3.7.2", @@ -467,7 +468,7 @@ "zlib": "^1.0.5" }, "engines": { - "node": "10.15.2", + "node": "10.18.0", "yarn": "^1.21.1" } } diff --git a/packages/elastic-datemath/package.json b/packages/elastic-datemath/package.json index e41744311e3be..57873d28d372d 100644 --- a/packages/elastic-datemath/package.json +++ b/packages/elastic-datemath/package.json @@ -11,8 +11,8 @@ "kbn:watch": "yarn build --watch" }, "devDependencies": { - "@babel/cli": "^7.7.5", - "@babel/preset-env": "^7.7.6", + "@babel/cli": "^7.5.5", + "@babel/preset-env": "^7.5.5", "babel-plugin-add-module-exports": "^1.0.2", "moment": "^2.24.0" }, diff --git a/packages/eslint-config-kibana/package.json b/packages/eslint-config-kibana/package.json index 2e72066fcbc68..04602d196a7f3 100644 --- a/packages/eslint-config-kibana/package.json +++ b/packages/eslint-config-kibana/package.json @@ -15,14 +15,14 @@ }, "homepage": "https://github.com/elastic/eslint-config-kibana#readme", "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^2.10.0", - "@typescript-eslint/parser": "^2.10.0", + "@typescript-eslint/eslint-plugin": "^2.12.0", + "@typescript-eslint/parser": "^2.12.0", "babel-eslint": "^10.0.3", - "eslint": "^6.7.2", + "eslint": "^6.5.1", "eslint-plugin-babel": "^5.3.0", "eslint-plugin-ban": "^1.3.0", "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-import": "^2.19.1", + "eslint-plugin-import": "^2.18.2", "eslint-plugin-jest": "^22.19.0", "eslint-plugin-mocha": "^6.2.0", "eslint-plugin-no-unsanitized": "^3.0.2", diff --git a/packages/kbn-analytics/package.json b/packages/kbn-analytics/package.json index 73f19690d4c7b..9eefa16aaca01 100644 --- a/packages/kbn-analytics/package.json +++ b/packages/kbn-analytics/package.json @@ -14,7 +14,7 @@ "kbn:watch": "node scripts/build --source-maps --watch" }, "devDependencies": { - "@babel/cli": "^7.7.5", + "@babel/cli": "^7.5.5", "@kbn/dev-utils": "1.0.0", "@kbn/babel-preset": "1.0.0", "typescript": "3.7.2" diff --git a/packages/kbn-babel-code-parser/package.json b/packages/kbn-babel-code-parser/package.json index 8c7d51da791a5..a9d373d33ab38 100755 --- a/packages/kbn-babel-code-parser/package.json +++ b/packages/kbn-babel-code-parser/package.json @@ -15,12 +15,12 @@ "kbn:watch": "yarn build --watch" }, "devDependencies": { - "@babel/cli": "^7.7.5" + "@babel/cli": "^7.5.5" }, "dependencies": { "@kbn/babel-preset": "1.0.0", - "@babel/parser": "^7.7.5", - "@babel/traverse": "^7.7.4", + "@babel/parser": "^7.5.5", + "@babel/traverse": "^7.5.5", "lodash": "^4.17.15" } } diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index d617c287b4f89..0acafbae59afd 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -4,17 +4,17 @@ "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "@babel/plugin-proposal-class-properties": "^7.7.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.7.4", - "@babel/plugin-proposal-optional-chaining": "^7.7.5", - "@babel/plugin-syntax-dynamic-import": "^7.7.4", - "@babel/plugin-transform-modules-commonjs": "^7.7.5", - "@babel/preset-env": "^7.7.6", + "@babel/plugin-proposal-class-properties": "^7.5.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4", + "@babel/plugin-proposal-optional-chaining": "^7.6.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0", + "@babel/plugin-transform-modules-commonjs": "^7.5.0", + "@babel/preset-env": "^7.5.5", "@babel/preset-react": "^7.7.4", "@babel/preset-typescript": "^7.7.4", "babel-plugin-add-module-exports": "^1.0.2", - "babel-plugin-filter-imports": "^4.0.0", + "babel-plugin-filter-imports": "^3.0.0", "babel-plugin-styled-components": "^1.10.6", - "babel-plugin-transform-define": "^2.0.0" + "babel-plugin-transform-define": "^1.3.1" } } diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index 89b358c5b0b3e..09753afeb120f 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -12,7 +12,7 @@ "dependencies": { "chalk": "^2.4.2", "dedent": "^0.7.0", - "execa": "^3.4.0", + "execa": "^3.2.0", "exit-hook": "^2.2.0", "getopts": "^2.2.5", "moment": "^2.24.0", diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json index adc81e3a6cc36..cb501dab3ddb7 100644 --- a/packages/kbn-es/package.json +++ b/packages/kbn-es/package.json @@ -11,11 +11,11 @@ "chalk": "^2.4.2", "dedent": "^0.7.0", "del": "^5.1.0", - "execa": "^3.4.0", + "execa": "^3.2.0", "getopts": "^2.2.4", "glob": "^7.1.2", "node-fetch": "^2.6.0", - "simple-git": "^1.129.0", + "simple-git": "^1.91.0", "tar-fs": "^1.16.3", "tree-kill": "^1.2.1", "yauzl": "^2.10.0" diff --git a/packages/kbn-eslint-import-resolver-kibana/package.json b/packages/kbn-eslint-import-resolver-kibana/package.json index e6d185a9b5c3a..9fae27011767e 100755 --- a/packages/kbn-eslint-import-resolver-kibana/package.json +++ b/packages/kbn-eslint-import-resolver-kibana/package.json @@ -12,10 +12,10 @@ "dependencies": { "debug": "^2.6.9", "eslint-import-resolver-node": "0.3.2", - "eslint-import-resolver-webpack": "0.12.0", + "eslint-import-resolver-webpack": "0.11.1", "glob-all": "^3.1.0", "lru-cache": "^4.1.5", "resolve": "^1.7.1", - "webpack": "^4.41.2" + "webpack": "^4.41.0" } } diff --git a/packages/kbn-eslint-plugin-eslint/package.json b/packages/kbn-eslint-plugin-eslint/package.json index 70d6b0f017470..badcf13187caf 100644 --- a/packages/kbn-eslint-plugin-eslint/package.json +++ b/packages/kbn-eslint-plugin-eslint/package.json @@ -4,12 +4,12 @@ "private": true, "license": "Apache-2.0", "peerDependencies": { - "eslint": "6.7.2", + "eslint": "6.5.1", "babel-eslint": "^10.0.3" }, "dependencies": { "micromatch": "3.1.10", "dedent": "^0.7.0", - "eslint-module-utils": "2.5.0" + "eslint-module-utils": "2.4.1" } } diff --git a/packages/kbn-i18n/package.json b/packages/kbn-i18n/package.json index a3f5009f30dba..bbc5126da1dce 100644 --- a/packages/kbn-i18n/package.json +++ b/packages/kbn-i18n/package.json @@ -12,8 +12,8 @@ "kbn:watch": "node scripts/build --watch --source-maps" }, "devDependencies": { - "@babel/cli": "^7.7.5", - "@babel/core": "^7.7.5", + "@babel/cli": "^7.5.5", + "@babel/core": "^7.5.5", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@types/intl-relativeformat": "^2.1.0", diff --git a/packages/kbn-interpreter/package.json b/packages/kbn-interpreter/package.json index 26b8026ed381c..27ef70d871856 100644 --- a/packages/kbn-interpreter/package.json +++ b/packages/kbn-interpreter/package.json @@ -9,29 +9,29 @@ "kbn:watch": "node scripts/build --dev --watch" }, "dependencies": { - "@babel/runtime": "^7.7.6", + "@babel/runtime": "^7.5.5", "@kbn/i18n": "1.0.0", "lodash": "npm:@elastic/lodash@3.10.1-kibana3", "lodash.clone": "^4.5.0", "uuid": "3.3.2" }, "devDependencies": { - "@babel/cli": "^7.7.5", - "@babel/core": "^7.7.5", - "@babel/plugin-transform-runtime": "^7.7.6", + "@babel/cli": "^7.5.5", + "@babel/core": "^7.5.5", + "@babel/plugin-transform-runtime": "^7.5.5", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "babel-loader": "^8.0.6", - "copy-webpack-plugin": "^5.1.1", - "css-loader": "3.3.2", + "copy-webpack-plugin": "^5.0.4", + "css-loader": "2.1.1", "del": "^5.1.0", "getopts": "^2.2.4", "pegjs": "0.10.0", - "sass-loader": "^8.0.0", - "style-loader": "1.0.1", + "sass-loader": "^7.3.1", + "style-loader": "0.23.1", "supports-color": "^7.0.0", - "url-loader": "3.0.0", - "webpack": "4.41.2", - "webpack-cli": "^3.3.10" + "url-loader": "2.2.0", + "webpack": "4.41.0", + "webpack-cli": "^3.3.9" } } diff --git a/packages/kbn-plugin-generator/package.json b/packages/kbn-plugin-generator/package.json index 8dafa36199417..ac98a0e675fb1 100644 --- a/packages/kbn-plugin-generator/package.json +++ b/packages/kbn-plugin-generator/package.json @@ -6,7 +6,7 @@ "dependencies": { "chalk": "^2.4.2", "dedent": "^0.7.0", - "execa": "^3.4.0", + "execa": "^3.2.0", "getopts": "^2.2.4", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", diff --git a/packages/kbn-plugin-helpers/package.json b/packages/kbn-plugin-helpers/package.json index 68454a9ce8c1d..68af0aa791c8e 100644 --- a/packages/kbn-plugin-helpers/package.json +++ b/packages/kbn-plugin-helpers/package.json @@ -13,11 +13,11 @@ "@kbn/babel-preset": "1.0.0" }, "dependencies": { - "@babel/core": "^7.7.5", + "@babel/core": "^7.5.5", "argv-split": "^2.0.1", "commander": "^3.0.0", "del": "^5.1.0", - "execa": "^3.4.0", + "execa": "^3.2.0", "globby": "^8.0.1", "gulp-babel": "^8.0.0", "gulp-rename": "1.4.0", diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index a5638b50629ec..aea85c13d7f32 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -8023,7 +8023,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(120); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } @@ -19088,7 +19088,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } @@ -19198,8 +19198,8 @@ const handleArgs = (file, args, options = {}) => { reject: true, cleanup: true, all: false, - windowsHide: true, - ...options + ...options, + windowsHide: true }; options.env = getEnv(options); @@ -23296,10 +23296,11 @@ const CleanCommand = { const originalCwd = process.cwd(); try { - for (const { - pattern, - cwd - } of toDelete) { + for (const _ref of toDelete) { + const { + pattern, + cwd + } = _ref; process.chdir(cwd); const promise = del__WEBPACK_IMPORTED_MODULE_1___default()(pattern); ora__WEBPACK_IMPORTED_MODULE_2___default.a.promise(promise, Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(originalCwd, Object(path__WEBPACK_IMPORTED_MODULE_3__["join"])(cwd, String(pattern)))); @@ -45552,7 +45553,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(485); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); -/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(697); +/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(692); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); /* @@ -45732,8 +45733,8 @@ const EventEmitter = __webpack_require__(46); const path = __webpack_require__(16); const arrify = __webpack_require__(487); const globby = __webpack_require__(488); -const cpFile = __webpack_require__(687); -const CpyError = __webpack_require__(695); +const cpFile = __webpack_require__(682); +const CpyError = __webpack_require__(690); const preprocessSrcPath = (srcPath, options) => options.cwd ? path.resolve(options.cwd, srcPath) : srcPath; @@ -45862,8 +45863,8 @@ const fs = __webpack_require__(23); const arrayUnion = __webpack_require__(489); const glob = __webpack_require__(37); const fastGlob = __webpack_require__(491); -const dirGlob = __webpack_require__(680); -const gitignore = __webpack_require__(683); +const dirGlob = __webpack_require__(675); +const gitignore = __webpack_require__(678); const DEFAULT_FILTER = () => false; @@ -46114,11 +46115,11 @@ module.exports.generateTasks = pkg.generateTasks; Object.defineProperty(exports, "__esModule", { value: true }); var optionsManager = __webpack_require__(493); var taskManager = __webpack_require__(494); -var reader_async_1 = __webpack_require__(651); -var reader_stream_1 = __webpack_require__(675); -var reader_sync_1 = __webpack_require__(676); -var arrayUtils = __webpack_require__(678); -var streamUtils = __webpack_require__(679); +var reader_async_1 = __webpack_require__(646); +var reader_stream_1 = __webpack_require__(670); +var reader_sync_1 = __webpack_require__(671); +var arrayUtils = __webpack_require__(673); +var streamUtils = __webpack_require__(674); /** * Synchronous API. */ @@ -46758,9 +46759,9 @@ var extend = __webpack_require__(612); */ var compilers = __webpack_require__(615); -var parsers = __webpack_require__(647); -var cache = __webpack_require__(648); -var utils = __webpack_require__(649); +var parsers = __webpack_require__(642); +var cache = __webpack_require__(643); +var utils = __webpack_require__(644); var MAX_LENGTH = 1024 * 64; /** @@ -65299,9 +65300,9 @@ var toRegex = __webpack_require__(502); */ var compilers = __webpack_require__(632); -var parsers = __webpack_require__(643); -var Extglob = __webpack_require__(646); -var utils = __webpack_require__(645); +var parsers = __webpack_require__(638); +var Extglob = __webpack_require__(641); +var utils = __webpack_require__(640); var MAX_LENGTH = 1024 * 64; /** @@ -65811,7 +65812,7 @@ var parsers = __webpack_require__(636); * Module dependencies */ -var debug = __webpack_require__(638)('expand-brackets'); +var debug = __webpack_require__(574)('expand-brackets'); var extend = __webpack_require__(511); var Snapdragon = __webpack_require__(541); var toRegex = __webpack_require__(502); @@ -66405,839 +66406,12 @@ exports.createRegex = function(pattern, include) { /* 638 */ /***/ (function(module, exports, __webpack_require__) { -/** - * Detect Electron renderer process, which is node, but we should - * treat as a browser. - */ - -if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(639); -} else { - module.exports = __webpack_require__(642); -} - - -/***/ }), -/* 639 */ -/***/ (function(module, exports, __webpack_require__) { - -/** - * This is the web browser implementation of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = __webpack_require__(640); -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; -exports.storage = 'undefined' != typeof chrome - && 'undefined' != typeof chrome.storage - ? chrome.storage.local - : localstorage(); - -/** - * Colors. - */ - -exports.colors = [ - 'lightseagreen', - 'forestgreen', - 'goldenrod', - 'dodgerblue', - 'darkorchid', - 'crimson' -]; - -/** - * Currently only WebKit-based Web Inspectors, Firefox >= v31, - * and the Firebug extension (any Firefox version) are known - * to support "%c" CSS customizations. - * - * TODO: add a `localStorage` variable to explicitly enable/disable colors - */ - -function useColors() { - // NB: In an Electron preload script, document will be defined but not fully - // initialized. Since we know we're in Chrome, we'll just detect this case - // explicitly - if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { - return true; - } - - // is webkit? http://stackoverflow.com/a/16459606/376773 - // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 - return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || - // is firebug? http://stackoverflow.com/a/398120/376773 - (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || - // is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || - // double check webkit in userAgent just in case we are in a worker - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); -} - -/** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ - -exports.formatters.j = function(v) { - try { - return JSON.stringify(v); - } catch (err) { - return '[UnexpectedJSONParseError]: ' + err.message; - } -}; - - -/** - * Colorize log arguments if enabled. - * - * @api public - */ - -function formatArgs(args) { - var useColors = this.useColors; - - args[0] = (useColors ? '%c' : '') - + this.namespace - + (useColors ? ' %c' : ' ') - + args[0] - + (useColors ? '%c ' : ' ') - + '+' + exports.humanize(this.diff); - - if (!useColors) return; - - var c = 'color: ' + this.color; - args.splice(1, 0, c, 'color: inherit') - - // the final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - var index = 0; - var lastC = 0; - args[0].replace(/%[a-zA-Z%]/g, function(match) { - if ('%%' === match) return; - index++; - if ('%c' === match) { - // we only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; - } - }); - - args.splice(lastC, 0, c); -} - -/** - * Invokes `console.log()` when available. - * No-op when `console.log` is not a "function". - * - * @api public - */ - -function log() { - // this hackery is required for IE8/9, where - // the `console.log` function doesn't have 'apply' - return 'object' === typeof console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); -} - -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - -function save(namespaces) { - try { - if (null == namespaces) { - exports.storage.removeItem('debug'); - } else { - exports.storage.debug = namespaces; - } - } catch(e) {} -} - -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - -function load() { - var r; - try { - r = exports.storage.debug; - } catch(e) {} - - // If debug isn't set in LS, and we're in Electron, try to load $DEBUG - if (!r && typeof process !== 'undefined' && 'env' in process) { - r = process.env.DEBUG; - } - - return r; -} - -/** - * Enable namespaces listed in `localStorage.debug` initially. - */ - -exports.enable(load()); - -/** - * Localstorage attempts to return the localstorage. - * - * This is necessary because safari throws - * when a user disables cookies/localstorage - * and you attempt to access it. - * - * @return {LocalStorage} - * @api private - */ - -function localstorage() { - try { - return window.localStorage; - } catch (e) {} -} - - -/***/ }), -/* 640 */ -/***/ (function(module, exports, __webpack_require__) { - - -/** - * This is the common logic for both the Node.js and web browser - * implementations of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; -exports.coerce = coerce; -exports.disable = disable; -exports.enable = enable; -exports.enabled = enabled; -exports.humanize = __webpack_require__(641); - -/** - * The currently active debug mode names, and names to skip. - */ - -exports.names = []; -exports.skips = []; - -/** - * Map of special "%n" handling functions, for the debug "format" argument. - * - * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". - */ - -exports.formatters = {}; - -/** - * Previous log timestamp. - */ - -var prevTime; - -/** - * Select a color. - * @param {String} namespace - * @return {Number} - * @api private - */ - -function selectColor(namespace) { - var hash = 0, i; - - for (i in namespace) { - hash = ((hash << 5) - hash) + namespace.charCodeAt(i); - hash |= 0; // Convert to 32bit integer - } - - return exports.colors[Math.abs(hash) % exports.colors.length]; -} - -/** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public - */ - -function createDebug(namespace) { - - function debug() { - // disabled? - if (!debug.enabled) return; - - var self = debug; - - // set `diff` timestamp - var curr = +new Date(); - var ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; - - // turn the `arguments` into a proper Array - var args = new Array(arguments.length); - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i]; - } - - args[0] = exports.coerce(args[0]); - - if ('string' !== typeof args[0]) { - // anything else let's inspect with %O - args.unshift('%O'); - } - - // apply any `formatters` transformations - var index = 0; - args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { - // if we encounter an escaped % then don't increase the array index - if (match === '%%') return match; - index++; - var formatter = exports.formatters[format]; - if ('function' === typeof formatter) { - var val = args[index]; - match = formatter.call(self, val); - - // now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1); - index--; - } - return match; - }); - - // apply env-specific formatting (colors, etc.) - exports.formatArgs.call(self, args); - - var logFn = debug.log || exports.log || console.log.bind(console); - logFn.apply(self, args); - } - - debug.namespace = namespace; - debug.enabled = exports.enabled(namespace); - debug.useColors = exports.useColors(); - debug.color = selectColor(namespace); - - // env-specific initialization logic for debug instances - if ('function' === typeof exports.init) { - exports.init(debug); - } - - return debug; -} - -/** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - * - * @param {String} namespaces - * @api public - */ - -function enable(namespaces) { - exports.save(namespaces); - - exports.names = []; - exports.skips = []; - - var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - var len = split.length; - - for (var i = 0; i < len; i++) { - if (!split[i]) continue; // ignore empty strings - namespaces = split[i].replace(/\*/g, '.*?'); - if (namespaces[0] === '-') { - exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); - } else { - exports.names.push(new RegExp('^' + namespaces + '$')); - } - } -} - -/** - * Disable debug output. - * - * @api public - */ - -function disable() { - exports.enable(''); -} - -/** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ - -function enabled(name) { - var i, len; - for (i = 0, len = exports.skips.length; i < len; i++) { - if (exports.skips[i].test(name)) { - return false; - } - } - for (i = 0, len = exports.names.length; i < len; i++) { - if (exports.names[i].test(name)) { - return true; - } - } - return false; -} - -/** - * Coerce `val`. - * - * @param {Mixed} val - * @return {Mixed} - * @api private - */ - -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; -} - - -/***/ }), -/* 641 */ -/***/ (function(module, exports) { - -/** - * Helpers. - */ - -var s = 1000; -var m = s * 60; -var h = m * 60; -var d = h * 24; -var y = d * 365.25; - -/** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] - * - * @param {String|Number} val - * @param {Object} [options] - * @throws {Error} throw an error if val is not a non-empty string or a number - * @return {String|Number} - * @api public - */ - -module.exports = function(val, options) { - options = options || {}; - var type = typeof val; - if (type === 'string' && val.length > 0) { - return parse(val); - } else if (type === 'number' && isNaN(val) === false) { - return options.long ? fmtLong(val) : fmtShort(val); - } - throw new Error( - 'val is not a non-empty string or a valid number. val=' + - JSON.stringify(val) - ); -}; - -/** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ - -function parse(str) { - str = String(str); - if (str.length > 100) { - return; - } - var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( - str - ); - if (!match) { - return; - } - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'yrs': - case 'yr': - case 'y': - return n * y; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'hrs': - case 'hr': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'mins': - case 'min': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 'secs': - case 'sec': - case 's': - return n * s; - case 'milliseconds': - case 'millisecond': - case 'msecs': - case 'msec': - case 'ms': - return n; - default: - return undefined; - } -} - -/** - * Short format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function fmtShort(ms) { - if (ms >= d) { - return Math.round(ms / d) + 'd'; - } - if (ms >= h) { - return Math.round(ms / h) + 'h'; - } - if (ms >= m) { - return Math.round(ms / m) + 'm'; - } - if (ms >= s) { - return Math.round(ms / s) + 's'; - } - return ms + 'ms'; -} - -/** - * Long format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function fmtLong(ms) { - return plural(ms, d, 'day') || - plural(ms, h, 'hour') || - plural(ms, m, 'minute') || - plural(ms, s, 'second') || - ms + ' ms'; -} - -/** - * Pluralization helper. - */ - -function plural(ms, n, name) { - if (ms < n) { - return; - } - if (ms < n * 1.5) { - return Math.floor(ms / n) + ' ' + name; - } - return Math.ceil(ms / n) + ' ' + name + 's'; -} - - -/***/ }), -/* 642 */ -/***/ (function(module, exports, __webpack_require__) { - -/** - * Module dependencies. - */ - -var tty = __webpack_require__(579); -var util = __webpack_require__(29); - -/** - * This is the Node.js implementation of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = __webpack_require__(640); -exports.init = init; -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; - -/** - * Colors. - */ - -exports.colors = [6, 2, 3, 4, 5, 1]; - -/** - * Build up the default `inspectOpts` object from the environment variables. - * - * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js - */ - -exports.inspectOpts = Object.keys(process.env).filter(function (key) { - return /^debug_/i.test(key); -}).reduce(function (obj, key) { - // camel-case - var prop = key - .substring(6) - .toLowerCase() - .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); - - // coerce string value into JS value - var val = process.env[key]; - if (/^(yes|on|true|enabled)$/i.test(val)) val = true; - else if (/^(no|off|false|disabled)$/i.test(val)) val = false; - else if (val === 'null') val = null; - else val = Number(val); - - obj[prop] = val; - return obj; -}, {}); - -/** - * The file descriptor to write the `debug()` calls to. - * Set the `DEBUG_FD` env variable to override with another value. i.e.: - * - * $ DEBUG_FD=3 node script.js 3>debug.log - */ - -var fd = parseInt(process.env.DEBUG_FD, 10) || 2; - -if (1 !== fd && 2 !== fd) { - util.deprecate(function(){}, 'except for stderr(2) and stdout(1), any other usage of DEBUG_FD is deprecated. Override debug.log if you want to use a different log function (https://git.io/debug_fd)')() -} - -var stream = 1 === fd ? process.stdout : - 2 === fd ? process.stderr : - createWritableStdioStream(fd); - -/** - * Is stdout a TTY? Colored output is enabled when `true`. - */ - -function useColors() { - return 'colors' in exports.inspectOpts - ? Boolean(exports.inspectOpts.colors) - : tty.isatty(fd); -} - -/** - * Map %o to `util.inspect()`, all on a single line. - */ - -exports.formatters.o = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts) - .split('\n').map(function(str) { - return str.trim() - }).join(' '); -}; - -/** - * Map %o to `util.inspect()`, allowing multiple lines if needed. - */ - -exports.formatters.O = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts); -}; - -/** - * Adds ANSI color escape codes if enabled. - * - * @api public - */ - -function formatArgs(args) { - var name = this.namespace; - var useColors = this.useColors; - - if (useColors) { - var c = this.color; - var prefix = ' \u001b[3' + c + ';1m' + name + ' ' + '\u001b[0m'; - - args[0] = prefix + args[0].split('\n').join('\n' + prefix); - args.push('\u001b[3' + c + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); - } else { - args[0] = new Date().toUTCString() - + ' ' + name + ' ' + args[0]; - } -} - -/** - * Invokes `util.format()` with the specified arguments and writes to `stream`. - */ - -function log() { - return stream.write(util.format.apply(util, arguments) + '\n'); -} - -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - -function save(namespaces) { - if (null == namespaces) { - // If you set a process.env field to null or undefined, it gets cast to the - // string 'null' or 'undefined'. Just delete instead. - delete process.env.DEBUG; - } else { - process.env.DEBUG = namespaces; - } -} - -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - -function load() { - return process.env.DEBUG; -} - -/** - * Copied from `node/src/node.js`. - * - * XXX: It's lame that node doesn't expose this API out-of-the-box. It also - * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. - */ - -function createWritableStdioStream (fd) { - var stream; - var tty_wrap = process.binding('tty_wrap'); - - // Note stream._type is used for test-module-load-list.js - - switch (tty_wrap.guessHandleType(fd)) { - case 'TTY': - stream = new tty.WriteStream(fd); - stream._type = 'tty'; - - // Hack to have stream not keep the event loop alive. - // See https://github.com/joyent/node/issues/1726 - if (stream._handle && stream._handle.unref) { - stream._handle.unref(); - } - break; - - case 'FILE': - var fs = __webpack_require__(23); - stream = new fs.SyncWriteStream(fd, { autoClose: false }); - stream._type = 'fs'; - break; - - case 'PIPE': - case 'TCP': - var net = __webpack_require__(580); - stream = new net.Socket({ - fd: fd, - readable: false, - writable: true - }); - - // FIXME Should probably have an option in net.Socket to create a - // stream from an existing fd which is writable only. But for now - // we'll just add this hack and set the `readable` member to false. - // Test: ./node test/fixtures/echo.js < /etc/passwd - stream.readable = false; - stream.read = null; - stream._type = 'pipe'; - - // FIXME Hack to have stream not keep the event loop alive. - // See https://github.com/joyent/node/issues/1726 - if (stream._handle && stream._handle.unref) { - stream._handle.unref(); - } - break; - - default: - // Probably an error on in uv_guess_handle() - throw new Error('Implement me. Unknown stream file type!'); - } - - // For supporting legacy API we put the FD here. - stream.fd = fd; - - stream._isStdio = true; - - return stream; -} - -/** - * Init logic for `debug` instances. - * - * Create a new `inspectOpts` object in case `useColors` is set - * differently for a particular `debug` instance. - */ - -function init (debug) { - debug.inspectOpts = {}; - - var keys = Object.keys(exports.inspectOpts); - for (var i = 0; i < keys.length; i++) { - debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; - } -} - -/** - * Enable namespaces listed in `process.env.DEBUG` initially. - */ - -exports.enable(load()); - - -/***/ }), -/* 643 */ -/***/ (function(module, exports, __webpack_require__) { - "use strict"; var brackets = __webpack_require__(633); -var define = __webpack_require__(644); -var utils = __webpack_require__(645); +var define = __webpack_require__(639); +var utils = __webpack_require__(640); /** * Characters to use in text regex (we want to "not" match @@ -67392,7 +66566,7 @@ module.exports = parsers; /***/ }), -/* 644 */ +/* 639 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67430,7 +66604,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 645 */ +/* 640 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67506,7 +66680,7 @@ utils.createRegex = function(str) { /***/ }), -/* 646 */ +/* 641 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67517,7 +66691,7 @@ utils.createRegex = function(str) { */ var Snapdragon = __webpack_require__(541); -var define = __webpack_require__(644); +var define = __webpack_require__(639); var extend = __webpack_require__(511); /** @@ -67525,7 +66699,7 @@ var extend = __webpack_require__(511); */ var compilers = __webpack_require__(632); -var parsers = __webpack_require__(643); +var parsers = __webpack_require__(638); /** * Customize Snapdragon parser and renderer @@ -67591,7 +66765,7 @@ module.exports = Extglob; /***/ }), -/* 647 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67681,14 +66855,14 @@ function textRegex(pattern) { /***/ }), -/* 648 */ +/* 643 */ /***/ (function(module, exports, __webpack_require__) { module.exports = new (__webpack_require__(624))(); /***/ }), -/* 649 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67706,7 +66880,7 @@ utils.define = __webpack_require__(611); utils.diff = __webpack_require__(628); utils.extend = __webpack_require__(612); utils.pick = __webpack_require__(629); -utils.typeOf = __webpack_require__(650); +utils.typeOf = __webpack_require__(645); utils.unique = __webpack_require__(514); /** @@ -68004,7 +67178,7 @@ utils.unixify = function(options) { /***/ }), -/* 650 */ +/* 645 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -68139,7 +67313,7 @@ function isBuffer(val) { /***/ }), -/* 651 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68158,9 +67332,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(652); -var reader_1 = __webpack_require__(665); -var fs_stream_1 = __webpack_require__(669); +var readdir = __webpack_require__(647); +var reader_1 = __webpack_require__(660); +var fs_stream_1 = __webpack_require__(664); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -68221,15 +67395,15 @@ exports.default = ReaderAsync; /***/ }), -/* 652 */ +/* 647 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(653); -const readdirAsync = __webpack_require__(661); -const readdirStream = __webpack_require__(664); +const readdirSync = __webpack_require__(648); +const readdirAsync = __webpack_require__(656); +const readdirStream = __webpack_require__(659); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -68313,7 +67487,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 653 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68321,11 +67495,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(654); +const DirectoryReader = __webpack_require__(649); let syncFacade = { - fs: __webpack_require__(659), - forEach: __webpack_require__(660), + fs: __webpack_require__(654), + forEach: __webpack_require__(655), sync: true }; @@ -68354,7 +67528,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 654 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68363,9 +67537,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(28).Readable; const EventEmitter = __webpack_require__(46).EventEmitter; const path = __webpack_require__(16); -const normalizeOptions = __webpack_require__(655); -const stat = __webpack_require__(657); -const call = __webpack_require__(658); +const normalizeOptions = __webpack_require__(650); +const stat = __webpack_require__(652); +const call = __webpack_require__(653); /** * Asynchronously reads the contents of a directory and streams the results @@ -68741,14 +67915,14 @@ module.exports = DirectoryReader; /***/ }), -/* 655 */ +/* 650 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const globToRegExp = __webpack_require__(656); +const globToRegExp = __webpack_require__(651); module.exports = normalizeOptions; @@ -68925,7 +68099,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 656 */ +/* 651 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -69062,13 +68236,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 657 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(658); +const call = __webpack_require__(653); module.exports = stat; @@ -69143,7 +68317,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 658 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69204,14 +68378,14 @@ function callOnce (fn) { /***/ }), -/* 659 */ +/* 654 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const call = __webpack_require__(658); +const call = __webpack_require__(653); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -69275,7 +68449,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 660 */ +/* 655 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69304,7 +68478,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 661 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69312,12 +68486,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(662); -const DirectoryReader = __webpack_require__(654); +const maybe = __webpack_require__(657); +const DirectoryReader = __webpack_require__(649); let asyncFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(663), + forEach: __webpack_require__(658), async: true }; @@ -69359,7 +68533,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 662 */ +/* 657 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69386,7 +68560,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 663 */ +/* 658 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69422,7 +68596,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 664 */ +/* 659 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69430,11 +68604,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(654); +const DirectoryReader = __webpack_require__(649); let streamFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(663), + forEach: __webpack_require__(658), async: true }; @@ -69454,16 +68628,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 665 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(16); -var deep_1 = __webpack_require__(666); -var entry_1 = __webpack_require__(668); -var pathUtil = __webpack_require__(667); +var deep_1 = __webpack_require__(661); +var entry_1 = __webpack_require__(663); +var pathUtil = __webpack_require__(662); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -69529,13 +68703,13 @@ exports.default = Reader; /***/ }), -/* 666 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(667); +var pathUtils = __webpack_require__(662); var patternUtils = __webpack_require__(495); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { @@ -69619,7 +68793,7 @@ exports.default = DeepFilter; /***/ }), -/* 667 */ +/* 662 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69650,13 +68824,13 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 668 */ +/* 663 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(667); +var pathUtils = __webpack_require__(662); var patternUtils = __webpack_require__(495); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { @@ -69742,7 +68916,7 @@ exports.default = EntryFilter; /***/ }), -/* 669 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69762,8 +68936,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(28); -var fsStat = __webpack_require__(670); -var fs_1 = __webpack_require__(674); +var fsStat = __webpack_require__(665); +var fs_1 = __webpack_require__(669); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -69813,14 +68987,14 @@ exports.default = FileSystemStream; /***/ }), -/* 670 */ +/* 665 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(671); -const statProvider = __webpack_require__(673); +const optionsManager = __webpack_require__(666); +const statProvider = __webpack_require__(668); /** * Asynchronous API. */ @@ -69851,13 +69025,13 @@ exports.statSync = statSync; /***/ }), -/* 671 */ +/* 666 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(672); +const fsAdapter = __webpack_require__(667); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -69870,7 +69044,7 @@ exports.prepare = prepare; /***/ }), -/* 672 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69893,7 +69067,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 673 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69945,7 +69119,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 674 */ +/* 669 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69976,7 +69150,7 @@ exports.default = FileSystem; /***/ }), -/* 675 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69996,9 +69170,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(28); -var readdir = __webpack_require__(652); -var reader_1 = __webpack_require__(665); -var fs_stream_1 = __webpack_require__(669); +var readdir = __webpack_require__(647); +var reader_1 = __webpack_require__(660); +var fs_stream_1 = __webpack_require__(664); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -70066,7 +69240,7 @@ exports.default = ReaderStream; /***/ }), -/* 676 */ +/* 671 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70085,9 +69259,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(652); -var reader_1 = __webpack_require__(665); -var fs_sync_1 = __webpack_require__(677); +var readdir = __webpack_require__(647); +var reader_1 = __webpack_require__(660); +var fs_sync_1 = __webpack_require__(672); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -70147,7 +69321,7 @@ exports.default = ReaderSync; /***/ }), -/* 677 */ +/* 672 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70166,8 +69340,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(670); -var fs_1 = __webpack_require__(674); +var fsStat = __webpack_require__(665); +var fs_1 = __webpack_require__(669); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -70213,7 +69387,7 @@ exports.default = FileSystemSync; /***/ }), -/* 678 */ +/* 673 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70229,7 +69403,7 @@ exports.flatten = flatten; /***/ }), -/* 679 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70250,13 +69424,13 @@ exports.merge = merge; /***/ }), -/* 680 */ +/* 675 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const pathType = __webpack_require__(681); +const pathType = __webpack_require__(676); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -70322,13 +69496,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 681 */ +/* 676 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const pify = __webpack_require__(682); +const pify = __webpack_require__(677); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -70371,7 +69545,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 682 */ +/* 677 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70462,7 +69636,7 @@ module.exports = (obj, opts) => { /***/ }), -/* 683 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70470,9 +69644,9 @@ module.exports = (obj, opts) => { const fs = __webpack_require__(23); const path = __webpack_require__(16); const fastGlob = __webpack_require__(491); -const gitIgnore = __webpack_require__(684); -const pify = __webpack_require__(685); -const slash = __webpack_require__(686); +const gitIgnore = __webpack_require__(679); +const pify = __webpack_require__(680); +const slash = __webpack_require__(681); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -70570,7 +69744,7 @@ module.exports.sync = options => { /***/ }), -/* 684 */ +/* 679 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -71039,7 +70213,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 685 */ +/* 680 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71114,7 +70288,7 @@ module.exports = (input, options) => { /***/ }), -/* 686 */ +/* 681 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71132,17 +70306,17 @@ module.exports = input => { /***/ }), -/* 687 */ +/* 682 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); const {constants: fsConstants} = __webpack_require__(23); -const {Buffer} = __webpack_require__(688); -const CpFileError = __webpack_require__(690); -const fs = __webpack_require__(692); -const ProgressEmitter = __webpack_require__(694); +const {Buffer} = __webpack_require__(683); +const CpFileError = __webpack_require__(685); +const fs = __webpack_require__(687); +const ProgressEmitter = __webpack_require__(689); const cpFile = (source, destination, options) => { if (!source || !destination) { @@ -71296,11 +70470,11 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 688 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { /* eslint-disable node/no-deprecated-api */ -var buffer = __webpack_require__(689) +var buffer = __webpack_require__(684) var Buffer = buffer.Buffer // alternative to using Object.keys for old browsers @@ -71364,18 +70538,18 @@ SafeBuffer.allocUnsafeSlow = function (size) { /***/ }), -/* 689 */ +/* 684 */ /***/ (function(module, exports) { module.exports = require("buffer"); /***/ }), -/* 690 */ +/* 685 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(691); +const NestedError = __webpack_require__(686); class CpFileError extends NestedError { constructor(message, nested) { @@ -71389,7 +70563,7 @@ module.exports = CpFileError; /***/ }), -/* 691 */ +/* 686 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(44); @@ -71443,15 +70617,15 @@ module.exports = NestedError; /***/ }), -/* 692 */ +/* 687 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(22); const makeDir = __webpack_require__(115); -const pify = __webpack_require__(693); -const CpFileError = __webpack_require__(690); +const pify = __webpack_require__(688); +const CpFileError = __webpack_require__(685); const fsP = pify(fs); @@ -71596,7 +70770,7 @@ if (fs.copyFileSync) { /***/ }), -/* 693 */ +/* 688 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71671,7 +70845,7 @@ module.exports = (input, options) => { /***/ }), -/* 694 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71712,12 +70886,12 @@ module.exports = ProgressEmitter; /***/ }), -/* 695 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(696); +const NestedError = __webpack_require__(691); class CpyError extends NestedError { constructor(message, nested) { @@ -71731,7 +70905,7 @@ module.exports = CpyError; /***/ }), -/* 696 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(29).inherits; @@ -71787,7 +70961,7 @@ module.exports = NestedError; /***/ }), -/* 697 */ +/* 692 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 92b495a111b61..ead454410a8b3 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -10,10 +10,10 @@ "prettier": "prettier --write './src/**/*.ts'" }, "devDependencies": { - "@babel/core": "^7.7.5", - "@babel/plugin-proposal-class-properties": "^7.7.4", - "@babel/plugin-proposal-object-rest-spread": "^7.7.4", - "@babel/preset-env": "^7.7.6", + "@babel/core": "^7.5.5", + "@babel/plugin-proposal-class-properties": "^7.5.5", + "@babel/plugin-proposal-object-rest-spread": "^7.5.5", + "@babel/preset-env": "^7.5.5", "@babel/preset-typescript": "^7.7.4", "@types/cmd-shim": "^2.0.0", "@types/cpy": "^5.1.0", @@ -40,7 +40,7 @@ "cpy": "^7.3.0", "dedent": "^0.7.0", "del": "^5.1.0", - "execa": "^3.4.0", + "execa": "^3.2.0", "getopts": "^2.2.4", "glob": "^7.1.2", "globby": "^8.0.1", @@ -60,8 +60,8 @@ "tempy": "^0.3.0", "typescript": "3.7.2", "unlazy-loader": "^0.1.3", - "webpack": "^4.41.2", - "webpack-cli": "^3.3.10", + "webpack": "^4.41.0", + "webpack-cli": "^3.3.9", "wrap-ansi": "^3.0.1", "write-pkg": "^4.0.0" }, diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index ac35c583ec747..0cc54fa2a64c4 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -10,7 +10,7 @@ "kbn:watch": "yarn build --watch" }, "devDependencies": { - "@babel/cli": "^7.7.5", + "@babel/cli": "^7.5.5", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@types/parse-link-header": "^1.0.0", diff --git a/packages/kbn-test/src/functional_test_runner/lib/failure_metadata.test.ts b/packages/kbn-test/src/functional_test_runner/lib/failure_metadata.test.ts index 7ae46ef6fac1e..b1f57cee8c23c 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/failure_metadata.test.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/failure_metadata.test.ts @@ -25,7 +25,7 @@ it('collects metadata for the current test', async () => { const failureMetadata = new FailureMetadata(lifecycle); const test1 = {}; - await lifecycle.beforeEachTest.trigger(test1); + await lifecycle.beforeEachRunnable.trigger(test1); failureMetadata.add({ foo: 'bar' }); expect(failureMetadata.get(test1)).toMatchInlineSnapshot(` @@ -35,7 +35,7 @@ it('collects metadata for the current test', async () => { `); const test2 = {}; - await lifecycle.beforeEachTest.trigger(test2); + await lifecycle.beforeEachRunnable.trigger(test2); failureMetadata.add({ test: 2 }); expect(failureMetadata.get(test1)).toMatchInlineSnapshot(` @@ -55,7 +55,7 @@ it('adds messages to the messages state', () => { const failureMetadata = new FailureMetadata(lifecycle); const test1 = {}; - lifecycle.beforeEachTest.trigger(test1); + lifecycle.beforeEachRunnable.trigger(test1); failureMetadata.addMessages(['foo', 'bar']); failureMetadata.addMessages(['baz']); diff --git a/packages/kbn-test/src/functional_test_runner/lib/failure_metadata.ts b/packages/kbn-test/src/functional_test_runner/lib/failure_metadata.ts index 9dc58d5b0b21f..be033e063fb9d 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/failure_metadata.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/failure_metadata.ts @@ -29,7 +29,7 @@ interface Metadata { export class FailureMetadata { // mocha's global types mean we can't import Mocha or it will override the global jest types.............. - private currentTest?: any; + private currentRunnable?: any; private readonly allMetadata = new Map(); constructor(lifecycle: Lifecycle) { @@ -39,18 +39,18 @@ export class FailureMetadata { ); } - lifecycle.beforeEachTest.add(test => { - this.currentTest = test; + lifecycle.beforeEachRunnable.add(runnable => { + this.currentRunnable = runnable; }); } add(metadata: Metadata | ((current: Metadata) => Metadata)) { - if (!this.currentTest) { - throw new Error('no current test to associate metadata with'); + if (!this.currentRunnable) { + throw new Error('no current runnable to associate metadata with'); } - const current = this.allMetadata.get(this.currentTest); - this.allMetadata.set(this.currentTest, { + const current = this.allMetadata.get(this.currentRunnable); + this.allMetadata.set(this.currentRunnable, { ...current, ...(typeof metadata === 'function' ? metadata(current || {}) : metadata), }); @@ -98,7 +98,7 @@ export class FailureMetadata { return screenshot; } - get(test: any) { - return this.allMetadata.get(test); + get(runnable: any) { + return this.allMetadata.get(runnable); } } diff --git a/packages/kbn-test/src/functional_test_runner/lib/lifecycle.ts b/packages/kbn-test/src/functional_test_runner/lib/lifecycle.ts index 7f78bc28c6d3d..95843ae4dcff2 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/lifecycle.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/lifecycle.ts @@ -22,11 +22,13 @@ import { LifecyclePhase } from './lifecycle_phase'; // mocha's global types mean we can't import Mocha or it will override the global jest types.............. type ItsASuite = any; type ItsATest = any; +type ItsARunnable = any; export class Lifecycle { public readonly beforeTests = new LifecyclePhase<[]>({ singular: true, }); + public readonly beforeEachRunnable = new LifecyclePhase<[ItsARunnable]>(); public readonly beforeTestSuite = new LifecyclePhase<[ItsASuite]>(); public readonly beforeEachTest = new LifecyclePhase<[ItsATest]>(); public readonly afterTestSuite = new LifecyclePhase<[ItsASuite]>(); diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js index 4eb45229c2234..64fc51a04aac9 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js @@ -19,7 +19,7 @@ import { createAssignmentProxy } from './assignment_proxy'; import { wrapFunction } from './wrap_function'; -import { wrapRunnableArgsWithErrorHandler } from './wrap_runnable_args'; +import { wrapRunnableArgs } from './wrap_runnable_args'; export function decorateMochaUi(lifecycle, context) { // incremented at the start of each suite, decremented after @@ -93,7 +93,7 @@ export function decorateMochaUi(lifecycle, context) { function wrapTestFunction(name, fn) { return wrapNonSuiteFunction( name, - wrapRunnableArgsWithErrorHandler(fn, async (err, test) => { + wrapRunnableArgs(fn, lifecycle, async (err, test) => { await lifecycle.testFailure.trigger(err, test); }) ); @@ -111,7 +111,7 @@ export function decorateMochaUi(lifecycle, context) { function wrapTestHookFunction(name, fn) { return wrapNonSuiteFunction( name, - wrapRunnableArgsWithErrorHandler(fn, async (err, test) => { + wrapRunnableArgs(fn, lifecycle, async (err, test) => { await lifecycle.testHookFailure.trigger(err, test); }) ); diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/wrap_runnable_args.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/wrap_runnable_args.js index 5ee21e81e83cc..d312ad8079dc1 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/wrap_runnable_args.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/wrap_runnable_args.js @@ -23,28 +23,24 @@ import { wrapFunction, wrapAsyncFunction } from './wrap_function'; * Wraps a "runnable" defining function (it(), beforeEach(), etc.) * so that any "runnable" arguments passed to it are wrapped and will * trigger a lifecycle event if they throw an error. - * - * @param {Function} fn - * @param {String} eventName - * @return {Function} */ -export function wrapRunnableArgsWithErrorHandler(fn, handler) { +export function wrapRunnableArgs(fn, lifecycle, handler) { return wrapFunction(fn, { before(target, thisArg, argumentsList) { for (let i = 0; i < argumentsList.length; i++) { if (typeof argumentsList[i] === 'function') { - argumentsList[i] = wrapRunnableError(argumentsList[i], handler); + argumentsList[i] = wrapAsyncFunction(argumentsList[i], { + async before(target, thisArg) { + await lifecycle.beforeEachRunnable.trigger(thisArg); + }, + + async handleError(target, thisArg, argumentsList, err) { + await handler(err, thisArg.test); + throw err; + }, + }); } } }, }); } - -function wrapRunnableError(runnable, handler) { - return wrapAsyncFunction(runnable, { - async handleError(target, thisArg, argumentsList, err) { - await handler(err, thisArg.test); - throw err; - }, - }); -} diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json index e3a46811684b4..b4d9d3dfee03f 100644 --- a/packages/kbn-ui-framework/package.json +++ b/packages/kbn-ui-framework/package.json @@ -30,18 +30,18 @@ "enzyme-adapter-react-16": "^1.9.1" }, "devDependencies": { - "@babel/core": "^7.7.5", + "@babel/core": "^7.5.5", "@elastic/eui": "0.0.55", "@kbn/babel-preset": "1.0.0", "autoprefixer": "9.6.1", "babel-loader": "^8.0.6", "brace": "0.11.1", "chalk": "^2.4.2", - "chokidar": "3.3.0", - "core-js": "^3.5.0", - "css-loader": "^3.3.2", + "chokidar": "3.2.1", + "core-js": "^3.2.1", + "css-loader": "^2.1.1", "expose-loader": "^0.7.5", - "file-loader": "^5.0.2", + "file-loader": "^4.2.0", "grunt": "1.0.4", "grunt-babel": "^8.0.0", "grunt-contrib-clean": "^1.1.0", @@ -56,7 +56,7 @@ "node-sass": "^4.9.4", "postcss": "^7.0.5", "postcss-loader": "^3.0.0", - "raw-loader": "^4.0.0", + "raw-loader": "^3.1.0", "react-dom": "^16.12.0", "react-redux": "^5.1.2", "react-router": "^3.2.0", @@ -64,11 +64,11 @@ "redux": "3.7.2", "redux-thunk": "2.2.0", "regenerator-runtime": "^0.13.3", - "sass-loader": "^8.0.0", + "sass-loader": "^7.3.1", "sinon": "^7.4.2", - "style-loader": "^1.0.1", - "webpack": "^4.41.2", - "webpack-dev-server": "^3.9.0", + "style-loader": "^0.23.1", + "webpack": "^4.41.0", + "webpack-dev-server": "^3.8.2", "yeoman-generator": "1.1.1", "yo": "2.0.6" } diff --git a/rfcs/text/0007_lifecycle_unblocked.md b/rfcs/text/0007_lifecycle_unblocked.md new file mode 100644 index 0000000000000..cb978d3dcd7ba --- /dev/null +++ b/rfcs/text/0007_lifecycle_unblocked.md @@ -0,0 +1,374 @@ +- Start Date: 2019-09-11 +- RFC PR: (leave this empty) +- Kibana Issue: (leave this empty) + +## Table of contents +- [Summary](#summary) +- [Motivation](#motivation) +- [Detailed design](#detailed-design) + - [
  1. Synchronous lifecycle methods
](#ollisynchronous-lifecycle-methodsliol) + - [
  1. Synchronous Context Provider functions
](#ol-start2lisynchronous-context-provider-functionsliol) + - [
  1. Core should not expose API's as observables
](#ol-start3licore-should-not-expose-apis-as-observablesliol) + - [
  1. Complete example code
](#ol-start4licomplete-example-codeliol) + - [
  1. Core should expose a status signal for Core services & plugins
](#ol-start5licore-should-expose-a-status-signal-for-core-services-amp-pluginsliol) +- [Drawbacks](#drawbacks) +- [Alternatives](#alternatives) + - [
  1. Introduce a lifecycle/context provider timeout
](#olliintroduce-a-lifecyclecontext-provider-timeoutliol) + - [
  1. Treat anything that blocks Kibana from starting up as a bug
](#ol-start2litreat-anything-that-blocks-kibana-from-starting-up-as-a-bugliol) +- [Adoption strategy](#adoption-strategy) +- [How we teach this](#how-we-teach-this) +- [Unresolved questions](#unresolved-questions) +- [Footnotes](#footnotes) + +# Summary + +Prevent plugin lifecycle methods from blocking Kibana startup by making the +following changes: +1. Synchronous lifecycle methods +2. Synchronous context provider functions +3. Core should not expose API's as observables + +# Motivation +Plugin lifecycle methods and context provider functions are async +(promise-returning) functions. Core runs these functions in series and waits +for each plugin's lifecycle/context provider function to resolve before +calling the next. This allows plugins to depend on the API's returned from +other plugins. + +With the current design, a single lifecycle method that blocks will block all +of Kibana from starting up. Similarly, a blocking context provider will block +all the handlers that depend on that context. Plugins (including legacy +plugins) rely heavily on this blocking behaviour to ensure that all conditions +required for their plugin's operation are met before their plugin is started +and exposes it's API's. This means a single plugin with a network error that +isn't retried or a dependency on an external host that is down, could block +all of Kibana from starting up. + +We should make it impossible for a single plugin lifecycle function to stall +all of kibana. + +# Detailed design + +### 1. Synchronous lifecycle methods +Lifecycle methods are synchronous functions, they can perform async operations +but Core doesn't wait for these to complete. This guarantees that no plugin +lifecycle function can block other plugins or core from starting up [1]. + +Core will still expose special API's that are able block the setup lifecycle +such as registering Saved Object migrations, but this will be limited to +operations where the risk of blocking all of kibana starting up is limited. + +### 2. Synchronous Context Provider functions +Making context provider functions synchronous guarantees that a context +handler will never be blocked by registered context providers. They can expose +async API's which could potentially have blocking behaviour. + +```ts +export type IContextProvider< + THandler extends HandlerFunction, + TContextName extends keyof HandlerContextType +> = ( + context: Partial>, + ...rest: HandlerParameters +) => + | HandlerContextType[TContextName]; +``` + +### 3. Core should not expose API's as observables +All Core API's should be reactive: when internal state changes, their behaviour +should change accordingly. But, exposing these internal state changes as part +of the API contract leaks internal implementation details consumers can't do +anything useful with and don't care about. + +For example: Core currently exposes `core.elasticsearch.adminClient$`, an +Observable which emits a pre-configured elasticsearch client every time there's +a configuration change. This includes changes to the logging configuration and +might in the future include updating the authentication headers sent to +elasticsearch https://github.com/elastic/kibana/issues/19829. As a plugin +author who wants to make search requests against elasticsearch I shouldn't +have to care about, react to, or keep track of, how many times the underlying +configuration has changed. I want to use the `callAsInternalUser` method and I +expect Core to use the most up to date configuration to send this request. + +> Note: It would not be desirable for Core to dynamically load all +> configuration changes. Changing the Elasticsearch `hosts` could mean Kibana +> is pointing to a completely new Elasticsearch cluster. Since this is a risky +> change to make and would likely require core and almost all plugins to +> completely re-initialize, it's safer to require a complete Kibana restart. + +This does not mean we should remove all observables from Core's API's. When an +API consumer is interested in the *state changes itself* it absolutely makes +sense to expose this as an Observable. Good examples of this is exposing +plugin config as this is state that changes over time to which a plugin should +directly react to. + +This is important in the context of synchronous lifecycle methods and context +handlers since exposing convenient API's become very ugly: + +*(3.1): exposing Observable-based API's through the route handler context:* +```ts +// Before: Using an async context provider +coreSetup.http.registerRouteHandlerContext(coreId, 'core', async (context, req) => { + const adminClient = await coreSetup.elasticsearch.adminClient$.pipe(take(1)).toPromise(); + const dataClient = await coreSetup.elasticsearch.dataClient$.pipe(take(1)).toPromise(); + return { + elasticsearch: { + adminClient: adminClient.asScoped(req), + dataClient: dataClient.asScoped(req), + }, + }; +}); + +// After: Using a synchronous context provider +coreSetup.http.registerRouteHandlerContext(coreId, 'core', async (context, req) => { + return { + elasticsearch: { + // (3.1.1) We can expose a convenient API by doing a lot of work + adminClient: () => { + callAsInternalUser: async (...args) => { + const adminClient = await coreSetup.elasticsearch.adminClient$.pipe(take(1)).toPromise(); + return adminClient.asScoped(req).callAsinternalUser(args); + }, + callAsCurrentUser: async (...args) => { + adminClient = await coreSetup.elasticsearch.adminClient$.pipe(take(1)).toPromise(); + return adminClient.asScoped(req).callAsCurrentUser(args); + } + }, + // (3.1.2) Or a lazy approach which perpetuates the problem to consumers: + dataClient: async () => { + const dataClient = await coreSetup.elasticsearch.dataClient$.pipe(take(1)).toPromise(); + return dataClient.asScoped(req); + }, + }, + }; +}); +``` + +### 4. Complete example code +*(4.1) Doing async operations in a plugin's setup lifecycle* +```ts +export class Plugin { + public setup(core: CoreSetup) { + // Async setup is possible and any operations involving async API's + // will still block until these API's are ready, (savedObjects find only + // resolves once the elasticsearch client has established a connection to + // the cluster). The difference is that these details are now internal to + // the API. + (async () => { + const docs = await core.savedObjects.client.find({...}); + ... + await core.savedObjects.client.update(...); + })(); + } +} +``` + +*(4.2) Exposing an API from a plugin's setup lifecycle* +```ts +export class Plugin { + constructor(private readonly initializerContext: PluginInitializerContext) {} + private async initSavedConfig(core: CoreSetup) { + // Note: pulling a config value here means our code isn't reactive to + // changes, but this is equivalent to doing it in an async setup lifecycle. + const config = await this.initializerContext.config + .create>() + .pipe(first()) + .toPromise(); + try { + const savedConfig = await core.savedObjects.internalRepository.get({...}); + return Object.assign({}, config, savedConfig); + } catch (e) { + if (SavedObjectErrorHelpers.isNotFoundError(e)) { + return await core.savedObjects.internalRepository.create(config, {...}); + } + } + } + public setup(core: CoreSetup) { + // savedConfigPromise resolves with the same kind of "setup state" that a + // plugin would have constructed in an async setup lifecycle. + const savedConfigPromise = initSavedConfig(core); + return { + ping: async () => { + const savedConfig = await savedConfigPromise; + if (config.allowPing === false || savedConfig.allowPing === false) { + throw new Error('ping() has been disabled'); + } + // Note: the elasticsearch client no longer exposes an adminClient$ + // observable, improving the ergonomics of consuming the API. + return await core.elasticsearch.adminClient.callAsInternalUser('ping', ...); + } + }; + } +} +``` + +*(4.3) Exposing an observable free Elasticsearch API from the route context* +```ts +coreSetup.http.registerRouteHandlerContext(coreId, 'core', async (context, req) => { + return { + elasticsearch: { + adminClient: coreSetup.elasticsearch.adminClient.asScoped(req), + dataClient: coreSetup.elasticsearch.adminClient.asScoped(req), + }, + }; +}); +``` + +### 5. Core should expose a status signal for Core services & plugins +Core should expose a global mechanism for core services and plugins to signal +their status. This is equivalent to the legacy status API +`kibana.Plugin.status` which allowed plugins to set their status to e.g. 'red' +or 'green'. The exact design of this API is outside of the scope of this RFC. + +What is important, is that there is a global mechanism to signal status +changes which Core then makes visible to system administrators in the Kibana +logs and the `/status` HTTP API. Plugins should be able to inspect and +subscribe to status changes from any of their dependencies. + +This will provide an obvious mechanism for plugins to signal that the +conditions which are required for this plugin to operate are not currently +present and manual intervention might be required. Status changes can happen +in both setup and start lifecycles e.g.: + - [setup] a required remote host is down + - [start] a remote host which was up during setup, started returning + connection timeout errors. + +# Drawbacks +Not being able to block on a lifecycle method means plugins can no longer be +certain that all setup is "complete" before they expose their API's or reach +the start lifecycle. + +A plugin might want to poll an external host to ensure that the host is up in +its setup lifecycle before making network requests to this host in it's start +lifecycle. + +Even if Kibana was using a valid, but incorrect configuration for the remote +host, with synchronous lifecycles Kibana would still start up. Although the +status API and logs would indicate a problem, these might not be monitored +leading to the error only being discovered once someone tries to use it's +functionality. This is an acceptable drawback because it buys us isolation. +Some problems might go unnoticed, but no single plugin should affect the +availability of all other plugins. + +In effect, the plugin is polling the world to construct a snapshot +of state which drives future behaviour. Modeling this with lifecycle functions +is insufficient since it assumes that any state constructed in the setup +lifecycle is static and won't and can't be changed in the future. + +For example: a plugin's setup lifecycle might poll for the existence of a +custom Elasticsearch index and if it doesn't exist, create it. Should there be +an Elasticsearch restore which deletes the index, the plugin wouldn't be able +to gracefully recover by simply running it's setup lifecycle a second time. + +The once-off nature of lifecycle methods are incompatible with the real-world +dynamic conditions under which plugins run. Not being able to block a +lifecycle method is, therefore, only a drawback when plugins are authored under +the false illusion of stability. + +# Alternatives +## 1. Introduce a lifecycle/context provider timeout +Lifecycle methods and context providers would timeout after X seconds and any +API's they expose would not be available if the timeout had been reached. + +Drawbacks: +1. A blocking setup lifecycle makes it easy for plugin authors to fall into + the trap of assuming that their plugin's behaviour can continue to operate + based on the snapshot of conditions present during setup. + +2. For lifecycle methods: there would be no way to recover from a timeout, + once a timeout had been reached the API will remain unavailable. + + Context providers have the benefit of being re-created for each handler + call, so a single timeout would not permanently disable the API. + +3. Plugins have less control over their behaviour. When an upstream server + becomes unavailable, a plugin might prefer to keep retrying the request + indefinitely or only timeout after more than X seconds. It also isn't able + to expose detailed error information to downstream consumers such as + specifying which host or service is unavailable. + +4. (minor) Introduces an additional failure condition that needs to be handled. + Consumers should handle the API not being available in setup, as well as, + error responses from the API itself. Since remote hosts like Elasticsearch + could go down even after a successful setup, this effectively means API + consumers have to handle the same error condition in two places. + +## 2. Treat anything that blocks Kibana from starting up as a bug +Keep the existing New Platform blocking behaviour, but through strong +conventions and developer awareness minimize the risk of plugins blocking +Kibana's startup indefinetely. By logging detailed diagnostic info on any +plugins that appear to be blocking startup, we can aid system administrators +to recover a blocked Kibana. + +A parallel can be drawn between Kibana's async plugin initialization and the TC39 +proposal for [top-level await](https://github.com/tc39/proposal-top-level-await). +> enables modules to act as big async functions: With top-level await, +> ECMAScript Modules (ESM) can await resources, causing other modules who +> import them to wait before they start evaluating their body + +They believe the benefits outweigh the risk of modules blocking loading since: + - [developer education should result in correct usage](https://github.com/tc39/proposal-top-level-await#will-top-level-await-cause-developers-to-make-their-code-block-longer-than-it-should) + - [there are existing unavoidable ways in which modules could block loading such as infinite loops or recursion](https://github.com/tc39/proposal-top-level-await#does-top-level-await-increase-the-risk-of-deadlocks) + + +Drawbacks: +1. A blocking setup lifecycle makes it easy for plugin authors to fall into + the trap of assuming that their plugin's behaviour can continue to operate + based on the snapshot of conditions present during setup. +2. This opens up the potential for a bug in Elastic or third-party plugins to + effectively "break" kibana. Instead of a single plugin being disabled all + of kibana would be down requiring manual intervention by a system + administrator. + +# Adoption strategy +Although the eventual goal is to have sync-only lifecycles / providers, we +will start by deprecating async behaviour and implementing a 30s timeout as +per alternative (1). This will immediately lower the impact of plugin bugs +while at the same time enabling a more incremental rollout and the flexibility +to discover use cases that would require adopting Core API's to support sync +lifecycles / providers. + +Adoption and implementation should be handled as follows: + - Adopt Core API’s to make sync lifecycles easier (3) + - Update migration guide and other documentation examples. + - Deprecate async lifecycles / context providers with a warning. Add a + timeout of 30s after which a plugin and it's dependencies will be disabled. + - Refactor existing plugin lifecycles which are easily converted to sync + - Future: remove async timeout lifecycles / context providers + +The following New Platform plugins or shims currently rely on async lifecycle +functions and will be impacted: +1. [region_map](https://github.com/elastic/kibana/blob/6039709929caf0090a4130b8235f3a53bd04ed84/src/legacy/core_plugins/region_map/public/plugin.ts#L68) +2. [tile_map](https://github.com/elastic/kibana/blob/6039709929caf0090a4130b8235f3a53bd04ed84/src/legacy/core_plugins/tile_map/public/plugin.ts#L62) +3. [vis_type_table](https://github.com/elastic/kibana/blob/6039709929caf0090a4130b8235f3a53bd04ed84/src/legacy/core_plugins/vis_type_table/public/plugin.ts#L61) +4. [vis_type_vega](https://github.com/elastic/kibana/blob/6039709929caf0090a4130b8235f3a53bd04ed84/src/legacy/core_plugins/vis_type_vega/public/plugin.ts#L59) +5. [timelion](https://github.com/elastic/kibana/blob/9d69b72a5f200e58220231035b19da852fc6b0a5/src/plugins/timelion/server/plugin.ts#L40) +6. [code](https://github.com/elastic/kibana/blob/5049b460b47d4ae3432e1d9219263bb4be441392/x-pack/legacy/plugins/code/server/plugin.ts#L129-L149) +7. [spaces](https://github.com/elastic/kibana/blob/096c7ee51136327f778845c636d7c4f1188e5db2/x-pack/legacy/plugins/spaces/server/new_platform/plugin.ts#L95) +8. [licensing](https://github.com/elastic/kibana/blob/4667c46caef26f8f47714504879197708debae32/x-pack/plugins/licensing/server/plugin.ts) +9. [security](https://github.com/elastic/kibana/blob/0f2324e44566ce2cf083d89082841e57d2db6ef6/x-pack/plugins/security/server/plugin.ts#L96) + +# How we teach this + +Async Plugin lifecycle methods and async context provider functions have been +deprecated. In the future all lifecycle methods will by sync only. Plugins +should treat the setup lifecycle as a place in time to register functionality +with core or other plugins' API's and not as a mechanism to kick off and wait +for any initialization that's required for the plugin to be able to run. + +# Unresolved questions +1. ~~Are the drawbacks worth the benefits or can we live with Kibana potentially +being blocked for the sake of convenient async lifecycle stages?~~ + +2. Should core provide conventions or patterns for plugins to construct a + snapshot of state and reactively updating this state and the behaviour it + drives as the state of the world changes? + +3. Do plugins ever need to read config values and pass these as parameters to + Core API’s? If so we would have to expose synchronous config values to + support sync lifecycles. + +# Footnotes +[1] Synchronous lifecycles can still be blocked by e.g. an infine for loop, +but this would always be unintentional behaviour in contrast to intentional +async behaviour like blocking until an external service becomes available. diff --git a/src/apm.js b/src/apm.js index cea6f8fc072aa..e3f4d84d9b523 100644 --- a/src/apm.js +++ b/src/apm.js @@ -17,21 +17,81 @@ * under the License. */ -const { existsSync } = require('fs'); const { join } = require('path'); -const { name, version } = require('../package.json'); +const { readFileSync } = require('fs'); +const { execSync } = require('child_process'); +const merge = require('lodash.merge'); +const { name, version, build } = require('../package.json'); -module.exports = function(serviceName = name) { - if (process.env.kbnWorkerType === 'optmzr') return; +const ROOT_DIR = join(__dirname, '..'); + +function gitRev() { + try { + return execSync('git rev-parse --short HEAD', { + encoding: 'utf-8', + stdio: ['ignore', 'pipe', 'ignore'], + }).trim(); + } catch (e) { + return null; + } +} + +function devConfig() { + try { + const apmDevConfigPath = join(ROOT_DIR, 'config', 'apm.dev.js'); + return require(apmDevConfigPath); // eslint-disable-line import/no-dynamic-require + } catch (e) { + return {}; + } +} + +const apmConfig = merge( + { + active: false, + serverUrl: 'https://f1542b814f674090afd914960583265f.apm.us-central1.gcp.cloud.es.io:443', + // The secretToken below is intended to be hardcoded in this file even though + // it makes it public. This is not a security/privacy issue. Normally we'd + // instead disable the need for a secretToken in the APM Server config where + // the data is transmitted to, but due to how it's being hosted, it's easier, + // for now, to simply leave it in. + secretToken: 'R0Gjg46pE9K9wGestd', + globalLabels: {}, + breakdownMetrics: true, + centralConfig: false, + logUncaughtExceptions: true, + }, + devConfig() +); + +try { + const filename = join(ROOT_DIR, 'data', 'uuid'); + apmConfig.globalLabels.kibana_uuid = readFileSync(filename, 'utf-8'); +} catch (e) {} // eslint-disable-line no-empty - const conf = { - serviceName: `${serviceName}-${version.replace(/\./g, '_')}`, +const rev = gitRev(); +if (rev !== null) apmConfig.globalLabels.git_rev = rev; + +function getConfig(serviceName) { + return { + ...apmConfig, + ...{ + serviceName: `${serviceName}-${version.replace(/\./g, '_')}`, + }, }; +} + +/** + * Flag to disable APM RUM support on all kibana builds by default + */ +const isKibanaDistributable = Boolean(build && build.distributable === true); - const configFile = join(__dirname, '..', 'config', 'apm.js'); +module.exports = function(serviceName = name) { + if (process.env.kbnWorkerType === 'optmzr') return; - if (existsSync(configFile)) conf.configFile = configFile; - else conf.active = false; + const conf = getConfig(serviceName); require('elastic-apm-node').start(conf); }; + +module.exports.getConfig = getConfig; +module.exports.isKibanaDistributable = isKibanaDistributable; diff --git a/src/cli/cluster/__mocks__/cluster.js b/src/cli/cluster/cluster.mock.ts similarity index 85% rename from src/cli/cluster/__mocks__/cluster.js rename to src/cli/cluster/cluster.mock.ts index d653771136ae6..332f8aad53ba1 100644 --- a/src/cli/cluster/__mocks__/cluster.js +++ b/src/cli/cluster/cluster.mock.ts @@ -18,12 +18,15 @@ */ /* eslint-env jest */ +// eslint-disable-next-line max-classes-per-file import EventEmitter from 'events'; import { assign, random } from 'lodash'; import { delay } from 'bluebird'; class MockClusterFork extends EventEmitter { - constructor(cluster) { + public exitCode = 0; + + constructor(cluster: MockCluster) { super(); let dead = true; @@ -49,9 +52,9 @@ class MockClusterFork extends EventEmitter { send: jest.fn(), }); - jest.spyOn(this, 'on'); - jest.spyOn(this, 'off'); - jest.spyOn(this, 'emit'); + jest.spyOn(this as EventEmitter, 'on'); + jest.spyOn(this as EventEmitter, 'off'); + jest.spyOn(this as EventEmitter, 'emit'); (async () => { await wait(); @@ -61,11 +64,7 @@ class MockClusterFork extends EventEmitter { } } -class MockCluster extends EventEmitter { +export class MockCluster extends EventEmitter { fork = jest.fn(() => new MockClusterFork(this)); setupMaster = jest.fn(); } - -export function mockCluster() { - return new MockCluster(); -} diff --git a/src/cli/cluster/cluster_manager.test.mocks.ts b/src/cli/cluster/cluster_manager.test.mocks.ts new file mode 100644 index 0000000000000..53984fd12cbf1 --- /dev/null +++ b/src/cli/cluster/cluster_manager.test.mocks.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { MockCluster } from './cluster.mock'; +export const mockCluster = new MockCluster(); +jest.mock('cluster', () => mockCluster); diff --git a/src/cli/cluster/cluster_manager.test.js b/src/cli/cluster/cluster_manager.test.ts similarity index 84% rename from src/cli/cluster/cluster_manager.test.js rename to src/cli/cluster/cluster_manager.test.ts index be8a096db9a66..bd37e854e1691 100644 --- a/src/cli/cluster/cluster_manager.test.js +++ b/src/cli/cluster/cluster_manager.test.ts @@ -17,8 +17,7 @@ * under the License. */ -import { mockCluster } from './__mocks__/cluster'; -jest.mock('cluster', () => mockCluster()); +import { mockCluster } from './cluster_manager.test.mocks'; jest.mock('readline', () => ({ createInterface: jest.fn(() => ({ on: jest.fn(), @@ -27,15 +26,14 @@ jest.mock('readline', () => ({ })), })); -import cluster from 'cluster'; import { sample } from 'lodash'; -import ClusterManager from './cluster_manager'; -import Worker from './worker'; +import { ClusterManager } from './cluster_manager'; +import { Worker } from './worker'; describe('CLI cluster manager', () => { beforeEach(() => { - cluster.fork.mockImplementation(() => { + mockCluster.fork.mockImplementation(() => { return { process: { kill: jest.fn(), @@ -44,16 +42,16 @@ describe('CLI cluster manager', () => { off: jest.fn(), on: jest.fn(), send: jest.fn(), - }; + } as any; }); }); afterEach(() => { - cluster.fork.mockReset(); + mockCluster.fork.mockReset(); }); test('has two workers', () => { - const manager = ClusterManager.create({}); + const manager = new ClusterManager({}, {} as any); expect(manager.workers).toHaveLength(2); for (const worker of manager.workers) expect(worker).toBeInstanceOf(Worker); @@ -63,7 +61,7 @@ describe('CLI cluster manager', () => { }); test('delivers broadcast messages to other workers', () => { - const manager = ClusterManager.create({}); + const manager = new ClusterManager({}, {} as any); for (const worker of manager.workers) { Worker.prototype.start.call(worker); // bypass the debounced start method @@ -76,10 +74,10 @@ describe('CLI cluster manager', () => { messenger.emit('broadcast', football); for (const worker of manager.workers) { if (worker === messenger) { - expect(worker.fork.send).not.toHaveBeenCalled(); + expect(worker.fork!.send).not.toHaveBeenCalled(); } else { - expect(worker.fork.send).toHaveBeenCalledTimes(1); - expect(worker.fork.send).toHaveBeenCalledWith(football); + expect(worker.fork!.send).toHaveBeenCalledTimes(1); + expect(worker.fork!.send).toHaveBeenCalledWith(football); } } }); @@ -88,7 +86,7 @@ describe('CLI cluster manager', () => { test('correctly configures `BasePathProxy`.', async () => { const basePathProxyMock = { start: jest.fn() }; - ClusterManager.create({}, {}, basePathProxyMock); + new ClusterManager({}, {} as any, basePathProxyMock as any); expect(basePathProxyMock.start).toHaveBeenCalledWith({ shouldRedirectFromOldBasePath: expect.any(Function), @@ -97,13 +95,13 @@ describe('CLI cluster manager', () => { }); describe('proxy is configured with the correct `shouldRedirectFromOldBasePath` and `blockUntil` functions.', () => { - let clusterManager; - let shouldRedirectFromOldBasePath; - let blockUntil; + let clusterManager: ClusterManager; + let shouldRedirectFromOldBasePath: (path: string) => boolean; + let blockUntil: () => Promise; beforeEach(async () => { const basePathProxyMock = { start: jest.fn() }; - clusterManager = ClusterManager.create({}, {}, basePathProxyMock); + clusterManager = new ClusterManager({}, {} as any, basePathProxyMock as any); jest.spyOn(clusterManager.server, 'on'); jest.spyOn(clusterManager.server, 'off'); @@ -146,7 +144,7 @@ describe('CLI cluster manager', () => { expect(clusterManager.server.on).toHaveBeenCalledTimes(2); expect(clusterManager.server.on).toHaveBeenCalledWith('crashed', expect.any(Function)); - const [, [eventName, onCrashed]] = clusterManager.server.on.mock.calls; + const [, [eventName, onCrashed]] = (clusterManager.server.on as jest.Mock).mock.calls; // Check event name to make sure we call the right callback, // in Jest 23 we could use `toHaveBeenNthCalledWith` instead. expect(eventName).toBe('crashed'); @@ -164,7 +162,7 @@ describe('CLI cluster manager', () => { expect(clusterManager.server.on).toHaveBeenCalledTimes(2); expect(clusterManager.server.on).toHaveBeenCalledWith('listening', expect.any(Function)); - const [[eventName, onListening]] = clusterManager.server.on.mock.calls; + const [[eventName, onListening]] = (clusterManager.server.on as jest.Mock).mock.calls; // Check event name to make sure we call the right callback, // in Jest 23 we could use `toHaveBeenNthCalledWith` instead. expect(eventName).toBe('listening'); diff --git a/src/cli/cluster/cluster_manager.js b/src/cli/cluster/cluster_manager.ts similarity index 83% rename from src/cli/cluster/cluster_manager.js rename to src/cli/cluster/cluster_manager.ts index cd1b3a0dadfc6..d97f7485fb4d2 100644 --- a/src/cli/cluster/cluster_manager.js +++ b/src/cli/cluster/cluster_manager.ts @@ -20,26 +20,38 @@ import { resolve } from 'path'; import { format as formatUrl } from 'url'; import opn from 'opn'; - import { debounce, invoke, bindAll, once, uniq } from 'lodash'; import * as Rx from 'rxjs'; import { first, mapTo, filter, map, take } from 'rxjs/operators'; import { REPO_ROOT } from '@kbn/dev-utils'; +import { FSWatcher } from 'chokidar'; + +import { LegacyConfig } from '../../core/server/legacy/config'; +import { BasePathProxyServer } from '../../core/server/http'; +// @ts-ignore import Log from '../log'; -import Worker from './worker'; -import { Config } from '../../legacy/server/config/config'; +import { Worker } from './worker'; process.env.kbnWorkerType = 'managr'; -export default class ClusterManager { - static create(opts, settings = {}, basePathProxy) { - return new ClusterManager(opts, Config.withDefaultSchema(settings), basePathProxy); - } - - constructor(opts, config, basePathProxy) { +export class ClusterManager { + public optimizer: Worker; + public server: Worker; + public workers: Worker[]; + + private watcher: FSWatcher | null = null; + private basePathProxy: BasePathProxyServer | undefined; + private log: any; + private addedCount = 0; + private inReplMode: boolean; + + constructor( + opts: Record, + config: LegacyConfig, + basePathProxy?: BasePathProxyServer + ) { this.log = new Log(opts.quiet, opts.silent); - this.addedCount = 0; this.inReplMode = !!opts.repl; this.basePathProxy = basePathProxy; @@ -79,7 +91,7 @@ export default class ClusterManager { worker.on('broadcast', msg => { this.workers.forEach(to => { if (to !== worker && to.online) { - to.fork.send(msg); + to.fork!.send(msg); } }); }); @@ -90,10 +102,10 @@ export default class ClusterManager { // and all workers. This is only used by LogRotator service // when the cluster mode is enabled this.server.on('reloadLoggingConfigFromServerWorker', () => { - process.emit('message', { reloadLoggingConfig: true }); + process.emit('message' as any, { reloadLoggingConfig: true } as any); this.workers.forEach(worker => { - worker.fork.send({ reloadLoggingConfig: true }); + worker.fork!.send({ reloadLoggingConfig: true }); }); }); @@ -111,9 +123,9 @@ export default class ClusterManager { } if (opts.watch) { - const pluginPaths = config.get('plugins.paths'); + const pluginPaths = config.get('plugins.paths'); const scanDirs = [ - ...config.get('plugins.scanDirs'), + ...config.get('plugins.scanDirs'), resolve(REPO_ROOT, 'src/plugins'), resolve(REPO_ROOT, 'x-pack/plugins'), ]; @@ -131,7 +143,7 @@ export default class ClusterManager { resolve(path, 'scripts'), resolve(path, 'docs') ), - [] + [] as string[] ); this.setupWatching(extraPaths, pluginInternalDirsIgnore); @@ -149,7 +161,7 @@ export default class ClusterManager { } } - setupOpen(openUrl) { + setupOpen(openUrl: string) { const serverListening$ = Rx.merge( Rx.fromEvent(this.server, 'listening').pipe(mapTo(true)), Rx.fromEvent(this.server, 'fork:exit').pipe(mapTo(false)), @@ -157,7 +169,7 @@ export default class ClusterManager { ); const optimizeSuccess$ = Rx.fromEvent(this.optimizer, 'optimizeStatus').pipe( - map(msg => !!msg.success) + map((msg: any) => !!msg.success) ); Rx.combineLatest(serverListening$, optimizeSuccess$) @@ -169,8 +181,10 @@ export default class ClusterManager { .then(() => opn(openUrl)); } - setupWatching(extraPaths, pluginInternalDirsIgnore) { + setupWatching(extraPaths: string[], pluginInternalDirsIgnore: string[]) { + // eslint-disable-next-line @typescript-eslint/no-var-requires const chokidar = require('chokidar'); + // eslint-disable-next-line @typescript-eslint/no-var-requires const { fromRoot } = require('../../core/server/utils'); const watchPaths = [ @@ -204,7 +218,7 @@ export default class ClusterManager { ...ignorePaths, 'plugins/java_languageserver', ], - }); + }) as FSWatcher; this.watcher.on('add', this.onWatcherAdd); this.watcher.on('error', this.onWatcherError); @@ -213,8 +227,8 @@ export default class ClusterManager { 'ready', once(() => { // start sending changes to workers - this.watcher.removeListener('add', this.onWatcherAdd); - this.watcher.on('all', this.onWatcherChange); + this.watcher!.removeListener('add', this.onWatcherAdd); + this.watcher!.on('all', this.onWatcherChange); this.log.good('watching for changes', `(${this.addedCount} files)`); this.startCluster(); @@ -229,6 +243,7 @@ export default class ClusterManager { if (this.inReplMode) { return; } + // eslint-disable-next-line @typescript-eslint/no-var-requires const readline = require('readline'); const rl = readline.createInterface(process.stdin, process.stdout); @@ -263,16 +278,16 @@ export default class ClusterManager { this.addedCount += 1; } - onWatcherChange(e, path) { + onWatcherChange(e: any, path: string) { invoke(this.workers, 'onChange', path); } - onWatcherError(err) { + onWatcherError(err: any) { this.log.bad('failed to watch files!\n', err.stack); process.exit(1); // eslint-disable-line no-process-exit } - shouldRedirectFromOldBasePath(path) { + shouldRedirectFromOldBasePath(path: string) { // strip `s/{id}` prefix when checking for need to redirect if (path.startsWith('s/')) { path = path diff --git a/src/cli/cluster/worker.test.js b/src/cli/cluster/worker.test.ts similarity index 80% rename from src/cli/cluster/worker.test.js rename to src/cli/cluster/worker.test.ts index b43cc123abcbb..4f9337681e083 100644 --- a/src/cli/cluster/worker.test.js +++ b/src/cli/cluster/worker.test.ts @@ -17,22 +17,20 @@ * under the License. */ -import { mockCluster } from './__mocks__/cluster'; -jest.mock('cluster', () => mockCluster()); +import { mockCluster } from './cluster_manager.test.mocks'; -import cluster from 'cluster'; - -import Worker from './worker'; +import { Worker, ClusterWorker } from './worker'; +// @ts-ignore import Log from '../log'; -const workersToShutdown = []; +const workersToShutdown: Worker[] = []; -function assertListenerAdded(emitter, event) { +function assertListenerAdded(emitter: NodeJS.EventEmitter, event: any) { expect(emitter.on).toHaveBeenCalledWith(event, expect.any(Function)); } -function assertListenerRemoved(emitter, event) { - const [, onEventListener] = emitter.on.mock.calls.find(([eventName]) => { +function assertListenerRemoved(emitter: NodeJS.EventEmitter, event: any) { + const [, onEventListener] = (emitter.on as jest.Mock).mock.calls.find(([eventName]) => { return eventName === event; }); @@ -44,6 +42,7 @@ function setup(opts = {}) { log: new Log(false, true), ...opts, baseArgv: [], + type: 'test', }); workersToShutdown.push(worker); @@ -53,7 +52,7 @@ function setup(opts = {}) { describe('CLI cluster manager', () => { afterEach(async () => { while (workersToShutdown.length > 0) { - const worker = workersToShutdown.pop(); + const worker = workersToShutdown.pop() as Worker; // If `fork` exists we should set `exitCode` to the non-zero value to // prevent worker from auto restart. if (worker.fork) { @@ -63,14 +62,14 @@ describe('CLI cluster manager', () => { await worker.shutdown(); } - cluster.fork.mockClear(); + mockCluster.fork.mockClear(); }); describe('#onChange', () => { describe('opts.watch = true', () => { test('restarts the fork', () => { const worker = setup({ watch: true }); - jest.spyOn(worker, 'start').mockImplementation(() => {}); + jest.spyOn(worker, 'start').mockResolvedValue(); worker.onChange('/some/path'); expect(worker.changes).toEqual(['/some/path']); expect(worker.start).toHaveBeenCalledTimes(1); @@ -80,7 +79,7 @@ describe('CLI cluster manager', () => { describe('opts.watch = false', () => { test('does not restart the fork', () => { const worker = setup({ watch: false }); - jest.spyOn(worker, 'start').mockImplementation(() => {}); + jest.spyOn(worker, 'start').mockResolvedValue(); worker.onChange('/some/path'); expect(worker.changes).toEqual([]); expect(worker.start).not.toHaveBeenCalled(); @@ -94,13 +93,13 @@ describe('CLI cluster manager', () => { const worker = setup(); await worker.start(); expect(worker).toHaveProperty('online', true); - const fork = worker.fork; - expect(fork.process.kill).not.toHaveBeenCalled(); + const fork = worker.fork as ClusterWorker; + expect(fork!.process.kill).not.toHaveBeenCalled(); assertListenerAdded(fork, 'message'); assertListenerAdded(fork, 'online'); assertListenerAdded(fork, 'disconnect'); await worker.shutdown(); - expect(fork.process.kill).toHaveBeenCalledTimes(1); + expect(fork!.process.kill).toHaveBeenCalledTimes(1); assertListenerRemoved(fork, 'message'); assertListenerRemoved(fork, 'online'); assertListenerRemoved(fork, 'disconnect'); @@ -120,7 +119,7 @@ describe('CLI cluster manager', () => { test(`is bound to fork's message event`, async () => { const worker = setup(); await worker.start(); - expect(worker.fork.on).toHaveBeenCalledWith('message', expect.any(Function)); + expect(worker.fork!.on).toHaveBeenCalledWith('message', expect.any(Function)); }); }); @@ -138,8 +137,8 @@ describe('CLI cluster manager', () => { test('calls #onMessage with message parts', () => { const worker = setup(); jest.spyOn(worker, 'onMessage').mockImplementation(() => {}); - worker.parseIncomingMessage([10, 100, 1000, 10000]); - expect(worker.onMessage).toHaveBeenCalledWith(10, 100, 1000, 10000); + worker.parseIncomingMessage(['event', 'some-data']); + expect(worker.onMessage).toHaveBeenCalledWith('event', 'some-data'); }); }); }); @@ -149,7 +148,7 @@ describe('CLI cluster manager', () => { test('emits the data to be broadcasted', () => { const worker = setup(); const data = {}; - jest.spyOn(worker, 'emit').mockImplementation(() => {}); + jest.spyOn(worker, 'emit').mockImplementation(() => true); worker.onMessage('WORKER_BROADCAST', data); expect(worker.emit).toHaveBeenCalledWith('broadcast', data); }); @@ -158,7 +157,7 @@ describe('CLI cluster manager', () => { describe('when sent WORKER_LISTENING message', () => { test('sets the listening flag and emits the listening event', () => { const worker = setup(); - jest.spyOn(worker, 'emit').mockImplementation(() => {}); + jest.spyOn(worker, 'emit').mockImplementation(() => true); expect(worker).toHaveProperty('listening', false); worker.onMessage('WORKER_LISTENING'); expect(worker).toHaveProperty('listening', true); @@ -170,8 +169,6 @@ describe('CLI cluster manager', () => { test('does nothing', () => { const worker = setup(); worker.onMessage('asdlfkajsdfahsdfiohuasdofihsdoif'); - worker.onMessage({}); - worker.onMessage(23049283094); }); }); }); @@ -185,7 +182,7 @@ describe('CLI cluster manager', () => { await worker.start(); - expect(cluster.fork).toHaveBeenCalledTimes(1); + expect(mockCluster.fork).toHaveBeenCalledTimes(1); expect(worker.on).toHaveBeenCalledWith('fork:online', expect.any(Function)); }); @@ -193,12 +190,12 @@ describe('CLI cluster manager', () => { const worker = setup(); jest.spyOn(process, 'on'); - jest.spyOn(cluster, 'on'); + jest.spyOn(mockCluster, 'on'); await worker.start(); - expect(cluster.on).toHaveBeenCalledTimes(1); - expect(cluster.on).toHaveBeenCalledWith('exit', expect.any(Function)); + expect(mockCluster.on).toHaveBeenCalledTimes(1); + expect(mockCluster.on).toHaveBeenCalledWith('exit', expect.any(Function)); expect(process.on).toHaveBeenCalledTimes(1); expect(process.on).toHaveBeenCalledWith('exit', expect.any(Function)); }); diff --git a/src/cli/cluster/worker.js b/src/cli/cluster/worker.ts similarity index 75% rename from src/cli/cluster/worker.js rename to src/cli/cluster/worker.ts index 2250075f20a60..fb87f1a87654c 100644 --- a/src/cli/cluster/worker.js +++ b/src/cli/cluster/worker.ts @@ -21,25 +21,57 @@ import _ from 'lodash'; import cluster from 'cluster'; import { EventEmitter } from 'events'; -import { BinderFor } from '../../legacy/utils'; +import { BinderFor } from '../../legacy/utils/binder_for'; import { fromRoot } from '../../core/server/utils'; const cliPath = fromRoot('src/cli'); const baseArgs = _.difference(process.argv.slice(2), ['--no-watch']); const baseArgv = [process.execPath, cliPath].concat(baseArgs); +export type ClusterWorker = cluster.Worker & { + killed: boolean; + exitCode?: number; +}; + cluster.setupMaster({ exec: cliPath, silent: false, }); -const dead = fork => { +const dead = (fork: ClusterWorker) => { return fork.isDead() || fork.killed; }; -export default class Worker extends EventEmitter { - constructor(opts) { - opts = opts || {}; +interface WorkerOptions { + type: string; + log: any; // src/cli/log.js + argv?: string[]; + title?: string; + watch?: boolean; + baseArgv?: string[]; +} + +export class Worker extends EventEmitter { + private readonly clusterBinder: BinderFor; + private readonly processBinder: BinderFor; + + private type: string; + private title: string; + private log: any; + private forkBinder: BinderFor | null = null; + private startCount: number; + private watch: boolean; + private env: Record; + + public fork: ClusterWorker | null = null; + public changes: string[]; + + // status flags + public online = false; // the fork can accept messages + public listening = false; // the fork is listening for connections + public crashed = false; // the fork crashed + + constructor(opts: WorkerOptions) { super(); this.log = opts.log; @@ -48,15 +80,9 @@ export default class Worker extends EventEmitter { this.watch = opts.watch !== false; this.startCount = 0; - // status flags - this.online = false; // the fork can accept messages - this.listening = false; // the fork is listening for connections - this.crashed = false; // the fork crashed - this.changes = []; - this.forkBinder = null; // defined when the fork is - this.clusterBinder = new BinderFor(cluster); + this.clusterBinder = new BinderFor(cluster as any); // lack the 'off' method this.processBinder = new BinderFor(process); this.env = { @@ -66,7 +92,7 @@ export default class Worker extends EventEmitter { }; } - onExit(fork, code) { + onExit(fork: ClusterWorker, code: number) { if (this.fork !== fork) return; // we have our fork's exit, so stop listening for others @@ -91,7 +117,7 @@ export default class Worker extends EventEmitter { } } - onChange(path) { + onChange(path: string) { if (!this.watch) return; this.changes.push(path); this.start(); @@ -104,7 +130,7 @@ export default class Worker extends EventEmitter { this.fork.killed = true; // stop listening to the fork, it's just going to die - this.forkBinder.destroy(); + this.forkBinder!.destroy(); // we don't need to react to process.exit anymore this.processBinder.destroy(); @@ -114,12 +140,14 @@ export default class Worker extends EventEmitter { } } - parseIncomingMessage(msg) { - if (!Array.isArray(msg)) return; - this.onMessage(...msg); + parseIncomingMessage(msg: any) { + if (!Array.isArray(msg)) { + return; + } + this.onMessage(msg[0], msg[1]); } - onMessage(type, data) { + onMessage(type: string, data?: any) { switch (type) { case 'WORKER_BROADCAST': this.emit('broadcast', data); @@ -170,16 +198,16 @@ export default class Worker extends EventEmitter { this.log.warn(`restarting ${this.title}...`); } - this.fork = cluster.fork(this.env); + this.fork = cluster.fork(this.env) as ClusterWorker; this.forkBinder = new BinderFor(this.fork); // when the fork sends a message, comes online, or loses its connection, then react - this.forkBinder.on('message', msg => this.parseIncomingMessage(msg)); + this.forkBinder.on('message', (msg: any) => this.parseIncomingMessage(msg)); this.forkBinder.on('online', () => this.onOnline()); this.forkBinder.on('disconnect', () => this.onDisconnect()); // when the cluster says a fork has exited, check if it is ours - this.clusterBinder.on('exit', (fork, code) => this.onExit(fork, code)); + this.clusterBinder.on('exit', (fork: ClusterWorker, code: number) => this.onExit(fork, code)); // when the process exits, make sure we kill our workers this.processBinder.on('exit', () => this.shutdown()); diff --git a/src/core/public/injected_metadata/injected_metadata_service.ts b/src/core/public/injected_metadata/injected_metadata_service.ts index 0bde1b68e1876..6a44000bf617e 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.ts @@ -80,6 +80,9 @@ export interface InjectedMetadataParams { user?: Record; }; }; + apm: { + [key: string]: unknown; + }; }; } diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index 17ec1e9756432..7025e96d9ecb4 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -27,7 +27,7 @@ import { findLegacyPluginSpecsMock } from './legacy_service.test.mocks'; import { BehaviorSubject, throwError } from 'rxjs'; import { LegacyService, LegacyServiceSetupDeps, LegacyServiceStartDeps } from '.'; // @ts-ignore: implicit any for JS file -import MockClusterManager from '../../../cli/cluster/cluster_manager'; +import { ClusterManager as MockClusterManager } from '../../../cli/cluster/cluster_manager'; import KbnServer from '../../../legacy/server/kbn_server'; import { Config, Env, ObjectToConfigAdapter } from '../config'; import { getEnvOptions } from '../config/__mocks__/env'; @@ -354,9 +354,15 @@ describe('once LegacyService is set up in `devClusterMaster` mode', () => { await devClusterLegacyService.setup(setupDeps); await devClusterLegacyService.start(startDeps); - const [[cliArgs, , basePathProxy]] = MockClusterManager.create.mock.calls; - expect(cliArgs.basePath).toBe(false); - expect(basePathProxy).not.toBeDefined(); + expect(MockClusterManager).toHaveBeenCalledTimes(1); + expect(MockClusterManager).toHaveBeenCalledWith( + expect.objectContaining({ silent: true, basePath: false }), + expect.objectContaining({ + get: expect.any(Function), + set: expect.any(Function), + }), + undefined + ); }); test('creates ClusterManager with base path proxy.', async () => { @@ -376,11 +382,15 @@ describe('once LegacyService is set up in `devClusterMaster` mode', () => { await devClusterLegacyService.setup(setupDeps); await devClusterLegacyService.start(startDeps); - expect(MockClusterManager.create).toBeCalledTimes(1); - - const [[cliArgs, , basePathProxy]] = MockClusterManager.create.mock.calls; - expect(cliArgs.basePath).toEqual(true); - expect(basePathProxy).toBeInstanceOf(BasePathProxyServer); + expect(MockClusterManager).toHaveBeenCalledTimes(1); + expect(MockClusterManager).toHaveBeenCalledWith( + expect.objectContaining({ quiet: true, basePath: true }), + expect.objectContaining({ + get: expect.any(Function), + set: expect.any(Function), + }), + expect.any(BasePathProxyServer) + ); }); }); diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index 1bba38433d7f4..412f4570887a4 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -244,7 +244,7 @@ export class LegacyService implements CoreService { private async createClusterManager(config: LegacyConfig) { const basePathProxy$ = this.coreContext.env.cliArgs.basePath - ? combineLatest(this.devConfig$, this.httpConfig$).pipe( + ? combineLatest([this.devConfig$, this.httpConfig$]).pipe( first(), map( ([dev, http]) => @@ -253,7 +253,9 @@ export class LegacyService implements CoreService { ) : EMPTY; - require('../../../cli/cluster/cluster_manager').create( + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { ClusterManager } = require('../../../cli/cluster/cluster_manager'); + return new ClusterManager( this.coreContext.env.cliArgs, config, await basePathProxy$.toPromise() diff --git a/src/dev/build/tasks/copy_source_task.js b/src/dev/build/tasks/copy_source_task.js index e5698c37ba16f..ee9dc159de47f 100644 --- a/src/dev/build/tasks/copy_source_task.js +++ b/src/dev/build/tasks/copy_source_task.js @@ -46,7 +46,6 @@ export const CopySourceTask = { 'typings/**', 'webpackShims/**', 'config/kibana.yml', - 'config/apm.js', 'tsconfig*.json', '.i18nrc.json', 'kibana.d.ts', diff --git a/src/dev/build/tasks/os_packages/create_os_package_tasks.js b/src/dev/build/tasks/os_packages/create_os_package_tasks.js index 3a328971538db..0416dac0aad8c 100644 --- a/src/dev/build/tasks/os_packages/create_os_package_tasks.js +++ b/src/dev/build/tasks/os_packages/create_os_package_tasks.js @@ -18,7 +18,7 @@ */ import { runFpm } from './run_fpm'; -import { runDockerGenerator } from './docker_generator'; +import { runDockerGenerator, runDockerGeneratorForUBI } from './docker_generator'; export const CreateDebPackageTask = { description: 'Creating deb package', @@ -45,6 +45,10 @@ export const CreateDockerPackageTask = { description: 'Creating docker package', async run(config, log, build) { + // Builds Docker targets for default and oss await runDockerGenerator(config, log, build); + + // Builds Docker target default with ubi7 base image + await runDockerGeneratorForUBI(config, log, build); }, }; diff --git a/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.js b/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.js index 65b0ffdfa5e81..bbcb6dfeeb109 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.js +++ b/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.js @@ -22,9 +22,11 @@ import { compress, copyAll, mkdirp, write } from '../../../lib'; import { dockerfileTemplate } from './templates'; export async function bundleDockerFiles(config, log, build, scope) { - log.info(`Generating kibana${scope.imageFlavor} docker build context bundle`); + log.info( + `Generating kibana${scope.imageFlavor}${scope.ubiImageFlavor} docker build context bundle` + ); - const dockerFilesDirName = `kibana${scope.imageFlavor}-${scope.versionTag}-docker-build-context`; + const dockerFilesDirName = `kibana${scope.imageFlavor}${scope.ubiImageFlavor}-${scope.versionTag}-docker-build-context`; const dockerFilesBuildDir = resolve(scope.dockerBuildDir, dockerFilesDirName); const dockerFilesOutputDir = config.resolveFromTarget(`${dockerFilesDirName}.tar.gz`); diff --git a/src/dev/build/tasks/os_packages/docker_generator/run.js b/src/dev/build/tasks/os_packages/docker_generator/run.js index fcd95357790e5..7424b6f01b82b 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.js +++ b/src/dev/build/tasks/os_packages/docker_generator/run.js @@ -29,20 +29,27 @@ const linkAsync = promisify(link); const unlinkAsync = promisify(unlink); const chmodAsync = promisify(chmod); -export async function runDockerGenerator(config, log, build) { +export async function runDockerGenerator(config, log, build, ubi = false) { + // UBI var config + const baseOSImage = ubi ? 'registry.access.redhat.com/ubi7/ubi-minimal:7.7' : 'centos:7'; + const ubiVersionTag = 'ubi7'; + const ubiImageFlavor = ubi ? `-${ubiVersionTag}` : ''; + + // General docker var config const license = build.isOss() ? 'ASL 2.0' : 'Elastic License'; const imageFlavor = build.isOss() ? '-oss' : ''; const imageTag = 'docker.elastic.co/kibana/kibana'; const versionTag = config.getBuildVersion(); const artifactTarball = `kibana${imageFlavor}-${versionTag}-linux-x86_64.tar.gz`; const artifactsDir = config.resolveFromTarget('.'); + // That would produce oss, default and default-ubi7 const dockerBuildDir = config.resolveFromRepo( 'build', 'kibana-docker', - build.isOss() ? 'oss' : 'default' + build.isOss() ? `oss` : `default${ubiImageFlavor}` ); const dockerOutputDir = config.resolveFromTarget( - `kibana${imageFlavor}-${versionTag}-docker.tar.gz` + `kibana${imageFlavor}${ubiImageFlavor}-${versionTag}-docker.tar.gz` ); const scope = { artifactTarball, @@ -53,6 +60,8 @@ export async function runDockerGenerator(config, log, build) { imageTag, dockerBuildDir, dockerOutputDir, + baseOSImage, + ubiImageFlavor, }; // Verify if we have the needed kibana target in order @@ -103,3 +112,12 @@ export async function runDockerGenerator(config, log, build) { // Pack Dockerfiles and create a target for them await bundleDockerFiles(config, log, build, scope); } + +export async function runDockerGeneratorForUBI(config, log, build) { + // Only run ubi docker image build for default distribution + if (build.isOss()) { + return; + } + + await runDockerGenerator(config, log, build, true); +} diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.js b/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.js index 66f2fd65832e4..4e8dfe188b867 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.js +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.js @@ -19,21 +19,28 @@ import dedent from 'dedent'; -function generator({ imageTag, imageFlavor, versionTag, dockerOutputDir }) { +function generator({ + imageTag, + imageFlavor, + versionTag, + dockerOutputDir, + baseOSImage, + ubiImageFlavor, +}) { return dedent(` #!/usr/bin/env bash # # ** THIS IS AN AUTO-GENERATED FILE ** # set -euo pipefail - - docker pull centos:7 - - echo "Building: kibana${imageFlavor}-docker"; \\ - docker build -t ${imageTag}${imageFlavor}:${versionTag} -f Dockerfile . || exit 1; - docker save ${imageTag}${imageFlavor}:${versionTag} | gzip -c > ${dockerOutputDir} - + docker pull ${baseOSImage} + + echo "Building: kibana${imageFlavor}${ubiImageFlavor}-docker"; \\ + docker build -t ${imageTag}${imageFlavor}${ubiImageFlavor}:${versionTag} -f Dockerfile . || exit 1; + + docker save ${imageTag}${imageFlavor}${ubiImageFlavor}:${versionTag} | gzip -c > ${dockerOutputDir} + exit 0 `); } diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.js b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.js index 5228eed84cd59..25ae71856c12d 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.js +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.js @@ -19,7 +19,14 @@ import dedent from 'dedent'; -function generator({ artifactTarball, versionTag, license, usePublicArtifact }) { +function generator({ + artifactTarball, + versionTag, + license, + usePublicArtifact, + baseOSImage, + ubiImageFlavor, +}) { const copyArtifactTarballInsideDockerOptFolder = () => { if (usePublicArtifact) { return `RUN cd /opt && curl --retry 8 -s -L -O https://artifacts.elastic.co/downloads/kibana/${artifactTarball} && cd -`; @@ -28,6 +35,14 @@ function generator({ artifactTarball, versionTag, license, usePublicArtifact }) return `COPY ${artifactTarball} /opt`; }; + const packageManager = () => { + if (ubiImageFlavor) { + return 'microdnf'; + } + + return 'yum'; + }; + return dedent(` # # ** THIS IS AN AUTO-GENERATED FILE ** @@ -37,7 +52,9 @@ function generator({ artifactTarball, versionTag, license, usePublicArtifact }) # Build stage 0 # Extract Kibana and make various file manipulations. ################################################################################ - FROM centos:7 AS prep_files + FROM ${baseOSImage} AS prep_files + # Add tar and gzip + RUN ${packageManager()} update -y && ${packageManager()} install -y tar gzip && ${packageManager()} clean all ${copyArtifactTarballInsideDockerOptFolder()} RUN mkdir /usr/share/kibana WORKDIR /usr/share/kibana @@ -53,11 +70,11 @@ function generator({ artifactTarball, versionTag, license, usePublicArtifact }) # Build stage 1 # Copy prepared files from the previous stage and complete the image. ################################################################################ - FROM centos:7 + FROM ${baseOSImage} EXPOSE 5601 # Add Reporting dependencies. - RUN yum update -y && yum install -y fontconfig freetype && yum clean all + RUN ${packageManager()} update -y && ${packageManager()} install -y fontconfig freetype shadow-utils && ${packageManager()} clean all # Add an init process, check the checksum to make sure it's a match RUN curl -L -o /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64 diff --git a/src/docs/docs_repo.js b/src/docs/docs_repo.js index 63fcd2a6de5ec..51ae0d495b9ff 100644 --- a/src/docs/docs_repo.js +++ b/src/docs/docs_repo.js @@ -27,7 +27,7 @@ export function buildDocsScript(cmd) { export function buildDocsArgs(cmd) { const docsIndexFile = resolve(kibanaDir, 'docs', 'index.asciidoc'); - let args = ['--doc', docsIndexFile, '--chunk=1']; + let args = ['--doc', docsIndexFile, '--direct_html', '--chunk=1']; if (cmd.open) { args = [...args, '--open']; } diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 8dc470e20c619..dd74119737657 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -27,7 +27,6 @@ import { exportApi } from './server/routes/api/export'; import { homeApi } from './server/routes/api/home'; import { managementApi } from './server/routes/api/management'; import { scriptsApi } from './server/routes/api/scripts'; -import { registerSuggestionsApi } from './server/routes/api/suggestions'; import { registerKqlTelemetryApi } from './server/routes/api/kql_telemetry'; import { registerFieldFormats } from './server/field_formats/register'; import { registerTutorials } from './server/tutorials/register'; @@ -331,7 +330,6 @@ export default function(kibana) { exportApi(server); homeApi(server); managementApi(server); - registerSuggestionsApi(server); registerKqlTelemetryApi(server); registerFieldFormats(server); registerTutorials(server); diff --git a/src/legacy/core_plugins/kibana/server/routes/api/suggestions/register_value_suggestions.js b/src/legacy/core_plugins/kibana/server/routes/api/suggestions/register_value_suggestions.js deleted file mode 100644 index 6a6276234e550..0000000000000 --- a/src/legacy/core_plugins/kibana/server/routes/api/suggestions/register_value_suggestions.js +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { get, map } from 'lodash'; -import { abortableRequestHandler } from '../../../../../elasticsearch/lib/abortable_request_handler'; - -export function registerValueSuggestions(server) { - const serverConfig = server.config(); - const autocompleteTerminateAfter = serverConfig.get('kibana.autocompleteTerminateAfter'); - const autocompleteTimeout = serverConfig.get('kibana.autocompleteTimeout'); - server.route({ - path: '/api/kibana/suggestions/values/{index}', - method: ['POST'], - handler: abortableRequestHandler(async function(signal, req) { - const { index } = req.params; - const { field: fieldName, query, boolFilter } = req.payload; - const { callWithRequest } = server.plugins.elasticsearch.getCluster('data'); - - const savedObjectsClient = req.getSavedObjectsClient(); - const savedObjectsResponse = await savedObjectsClient.find({ - type: 'index-pattern', - fields: ['fields'], - search: `"${index}"`, - searchFields: ['title'], - }); - const indexPattern = - savedObjectsResponse.total > 0 ? savedObjectsResponse.saved_objects[0] : null; - const fields = indexPattern ? JSON.parse(indexPattern.attributes.fields) : null; - const field = fields ? fields.find(field => field.name === fieldName) : fieldName; - - const body = getBody( - { field, query, boolFilter }, - autocompleteTerminateAfter, - autocompleteTimeout - ); - - try { - const response = await callWithRequest(req, 'search', { index, body }, { signal }); - const buckets = - get(response, 'aggregations.suggestions.buckets') || - get(response, 'aggregations.nestedSuggestions.suggestions.buckets') || - []; - const suggestions = map(buckets, 'key'); - return suggestions; - } catch (error) { - throw server.plugins.elasticsearch.handleESError(error); - } - }), - }); -} - -function getBody({ field, query, boolFilter = [] }, terminateAfter, timeout) { - // Helps ensure that the regex is not evaluated eagerly against the terms dictionary - const executionHint = 'map'; - - // We don't care about the accuracy of the counts, just the content of the terms, so this reduces - // the amount of information that needs to be transmitted to the coordinating node - const shardSize = 10; - - const body = { - size: 0, - timeout: `${timeout}ms`, - terminate_after: terminateAfter, - query: { - bool: { - filter: boolFilter, - }, - }, - aggs: { - suggestions: { - terms: { - field: field.name || field, - include: `${getEscapedQuery(query)}.*`, - execution_hint: executionHint, - shard_size: shardSize, - }, - }, - }, - }; - - if (field.subType && field.subType.nested) { - return { - ...body, - aggs: { - nestedSuggestions: { - nested: { - path: field.subType.nested.path, - }, - aggs: body.aggs, - }, - }, - }; - } - - return body; -} - -function getEscapedQuery(query = '') { - // https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#_standard_operators - return query.replace(/[.?+*|{}[\]()"\\#@&<>~]/g, match => `\\${match}`); -} diff --git a/src/legacy/core_plugins/telemetry/public/components/telemetry_form.js b/src/legacy/core_plugins/telemetry/public/components/telemetry_form.js index 94a387565f38d..aed8477057165 100644 --- a/src/legacy/core_plugins/telemetry/public/components/telemetry_form.js +++ b/src/legacy/core_plugins/telemetry/public/components/telemetry_form.js @@ -31,7 +31,7 @@ import { } from '@elastic/eui'; import { PRIVACY_STATEMENT_URL } from '../../common/constants'; import { OptInExampleFlyout } from './opt_in_details_component'; -import { Field } from 'ui/management'; +import { Field } from '../../../kibana/public/management/sections/settings/components/field/field'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; diff --git a/src/legacy/core_plugins/timelion/public/app.js b/src/legacy/core_plugins/timelion/public/app.js index 82f6d56f8f22a..bff847becb7a8 100644 --- a/src/legacy/core_plugins/timelion/public/app.js +++ b/src/legacy/core_plugins/timelion/public/app.js @@ -40,6 +40,9 @@ import 'ui/directives/saved_object_finder'; import 'ui/directives/listen'; import 'ui/kbn_top_nav'; import 'ui/saved_objects/ui/saved_object_save_as_checkbox'; +import './services/saved_sheets'; +import './services/_saved_sheet'; +import './services/saved_sheet_register'; import rootTemplate from 'plugins/timelion/index.html'; @@ -57,13 +60,8 @@ document.title = 'Timelion - Kibana'; const app = require('ui/modules').get('apps/timelion', []); -require('plugins/timelion/services/saved_sheets'); -require('plugins/timelion/services/_saved_sheet'); - require('./vis'); -SavedObjectRegistryProvider.register(require('plugins/timelion/services/saved_sheet_register')); - require('ui/routes').enable(); require('ui/routes').when('/:id?', { diff --git a/src/legacy/core_plugins/timelion/public/services/_saved_sheet.js b/src/legacy/core_plugins/timelion/public/services/_saved_sheet.js deleted file mode 100644 index 8875a84d4d3a1..0000000000000 --- a/src/legacy/core_plugins/timelion/public/services/_saved_sheet.js +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { uiModules } from 'ui/modules'; -import { createLegacyClass } from 'ui/utils/legacy_class'; -import { SavedObjectProvider } from 'ui/saved_objects/saved_object'; -const module = uiModules.get('app/timelion'); - -// Used only by the savedSheets service, usually no reason to change this -module.factory('SavedSheet', function(Private, config) { - // SavedSheet constructor. Usually you'd interact with an instance of this. - // ID is option, without it one will be generated on save. - const SavedObject = Private(SavedObjectProvider); - createLegacyClass(SavedSheet).inherits(SavedObject); - function SavedSheet(id) { - // Gives our SavedSheet the properties of a SavedObject - SavedObject.call(this, { - type: SavedSheet.type, - mapping: SavedSheet.mapping, - - // if this is null/undefined then the SavedObject will be assigned the defaults - id: id, - - // default values that will get assigned if the doc is new - defaults: { - title: 'New TimeLion Sheet', - hits: 0, - description: '', - timelion_sheet: ['.es(*)'], - timelion_interval: 'auto', - timelion_chart_height: 275, - timelion_columns: config.get('timelion:default_columns') || 2, - timelion_rows: config.get('timelion:default_rows') || 2, - version: 1, - }, - }); - - this.showInRecentlyAccessed = true; - } - - // save these objects with the 'sheet' type - SavedSheet.type = 'timelion-sheet'; - - // if type:sheet has no mapping, we push this mapping into ES - SavedSheet.mapping = { - title: 'text', - hits: 'integer', - description: 'text', - timelion_sheet: 'text', - timelion_interval: 'keyword', - timelion_other_interval: 'keyword', - timelion_chart_height: 'integer', - timelion_columns: 'integer', - timelion_rows: 'integer', - version: 'integer', - }; - - // Order these fields to the top, the rest are alphabetical - SavedSheet.fieldOrder = ['title', 'description']; - - SavedSheet.prototype.getFullPath = function() { - return `/app/timelion#/${this.id}`; - }; - - return SavedSheet; -}); diff --git a/src/legacy/core_plugins/timelion/public/services/_saved_sheet.ts b/src/legacy/core_plugins/timelion/public/services/_saved_sheet.ts new file mode 100644 index 0000000000000..1e956cbd3e5ac --- /dev/null +++ b/src/legacy/core_plugins/timelion/public/services/_saved_sheet.ts @@ -0,0 +1,79 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { createSavedObjectClass } from 'ui/saved_objects/saved_object'; +import { SavedObjectKibanaServices } from 'ui/saved_objects/types'; +import { IUiSettingsClient } from 'kibana/public'; + +// Used only by the savedSheets service, usually no reason to change this +export function createSavedSheetClass( + services: SavedObjectKibanaServices, + config: IUiSettingsClient +) { + const SavedObjectClass = createSavedObjectClass(services); + + class SavedSheet extends SavedObjectClass { + static type = 'timelion-sheet'; + + // if type:sheet has no mapping, we push this mapping into ES + static mapping = { + title: 'text', + hits: 'integer', + description: 'text', + timelion_sheet: 'text', + timelion_interval: 'keyword', + timelion_other_interval: 'keyword', + timelion_chart_height: 'integer', + timelion_columns: 'integer', + timelion_rows: 'integer', + version: 'integer', + }; + + // Order these fields to the top, the rest are alphabetical + static fieldOrder = ['title', 'description']; + // SavedSheet constructor. Usually you'd interact with an instance of this. + // ID is option, without it one will be generated on save. + constructor(id: string) { + super({ + type: SavedSheet.type, + mapping: SavedSheet.mapping, + + // if this is null/undefined then the SavedObject will be assigned the defaults + id, + + // default values that will get assigned if the doc is new + defaults: { + title: 'New TimeLion Sheet', + hits: 0, + description: '', + timelion_sheet: ['.es(*)'], + timelion_interval: 'auto', + timelion_chart_height: 275, + timelion_columns: config.get('timelion:default_columns') || 2, + timelion_rows: config.get('timelion:default_rows') || 2, + version: 1, + }, + }); + this.showInRecentlyAccessed = true; + this.getFullPath = () => `/app/timelion#/${this.id}`; + } + } + + return SavedSheet; +} diff --git a/src/legacy/ui/public/management/__tests__/index.js b/src/legacy/core_plugins/timelion/public/services/saved_sheet_register.ts similarity index 76% rename from src/legacy/ui/public/management/__tests__/index.js rename to src/legacy/core_plugins/timelion/public/services/saved_sheet_register.ts index 6c794b3c189f0..6bfbacb95b62e 100644 --- a/src/legacy/ui/public/management/__tests__/index.js +++ b/src/legacy/core_plugins/timelion/public/services/saved_sheet_register.ts @@ -16,14 +16,9 @@ * specific language governing permissions and limitations * under the License. */ +import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; +import './saved_sheets'; -import expect from '@kbn/expect'; - -import { management } from '..'; -import { ManagementSection } from '../section'; - -describe('Management', () => { - it('provides ManagementSection', () => { - expect(management).to.be.a(ManagementSection); - }); +SavedObjectRegistryProvider.register((savedSheets: any) => { + return savedSheets; }); diff --git a/src/legacy/core_plugins/timelion/public/services/saved_sheets.js b/src/legacy/core_plugins/timelion/public/services/saved_sheets.ts similarity index 71% rename from src/legacy/core_plugins/timelion/public/services/saved_sheets.js rename to src/legacy/core_plugins/timelion/public/services/saved_sheets.ts index 064b59a0d45d9..d851b5a863658 100644 --- a/src/legacy/core_plugins/timelion/public/services/saved_sheets.js +++ b/src/legacy/core_plugins/timelion/public/services/saved_sheets.ts @@ -16,12 +16,13 @@ * specific language governing permissions and limitations * under the License. */ - -import { SavedObjectLoader, SavedObjectsClientProvider } from 'ui/saved_objects'; +import { npStart } from 'ui/new_platform'; +import { SavedObjectLoader } from 'ui/saved_objects'; +// @ts-ignore import { savedObjectManagementRegistry } from 'plugins/kibana/management/saved_object_registry'; +// @ts-ignore import { uiModules } from 'ui/modules'; -import './_saved_sheet.js'; -import { npStart } from '../../../../ui/public/new_platform'; +import { createSavedSheetClass } from './_saved_sheet'; const module = uiModules.get('app/sheet'); @@ -33,17 +34,23 @@ savedObjectManagementRegistry.register({ }); // This is the only thing that gets injected into controllers -module.service('savedSheets', function(Private, SavedSheet, kbnUrl) { - const savedObjectClient = Private(SavedObjectsClientProvider); +module.service('savedSheets', function() { + const savedObjectsClient = npStart.core.savedObjects.client; + const services = { + savedObjectsClient, + indexPatterns: npStart.plugins.data.indexPatterns, + chrome: npStart.core.chrome, + overlays: npStart.core.overlays, + }; + + const SavedSheet = createSavedSheetClass(services, npStart.core.uiSettings); + const savedSheetLoader = new SavedObjectLoader( SavedSheet, - savedObjectClient, + savedObjectsClient, npStart.core.chrome ); - savedSheetLoader.urlFor = function(id) { - return kbnUrl.eval('#/{{id}}', { id: id }); - }; - + savedSheetLoader.urlFor = id => `#/${encodeURIComponent(id)}`; // Customize loader properties since adding an 's' on type doesn't work for type 'timelion-sheet'. savedSheetLoader.loaderProperties = { name: 'timelion-sheet', diff --git a/src/legacy/ui/apm/index.js b/src/legacy/ui/apm/index.js new file mode 100644 index 0000000000000..e2ff415865b56 --- /dev/null +++ b/src/legacy/ui/apm/index.js @@ -0,0 +1,69 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { getConfig, isKibanaDistributable } from '../../../apm'; +import agent from 'elastic-apm-node'; + +const apmEnabled = !isKibanaDistributable && process.env.ELASTIC_APM_ACTIVE === 'true'; + +export function apmImport() { + return apmEnabled ? 'import { init } from "@elastic/apm-rum"' : ''; +} + +export function apmInit(config) { + return apmEnabled ? `init(${config})` : ''; +} + +export function getApmConfig(appMetadata) { + if (!apmEnabled) { + return {}; + } + /** + * we use the injected app metadata from the server to extract the + * app URL path to be used for page-load transaction + */ + const navLink = appMetadata.getNavLink(); + const pageUrl = navLink ? navLink.toJSON().url : appMetadata._url; + + const config = { + ...getConfig('kibana-frontend'), + ...{ + active: true, + pageLoadTransactionName: pageUrl, + }, + }; + /** + * Get current active backend transaction to make distrubuted tracing + * work for rendering the app + */ + const backendTransaction = agent.currentTransaction; + + if (backendTransaction) { + const { sampled, traceId } = backendTransaction; + return { + ...config, + ...{ + pageLoadTraceId: traceId, + pageLoadSampled: sampled, + pageLoadSpanId: backendTransaction.ensureParentId(), + }, + }; + } + return config; +} diff --git a/src/legacy/ui/public/management/index.js b/src/legacy/ui/public/management/index.js index a27ff493b317e..ed8ddb65315e2 100644 --- a/src/legacy/ui/public/management/index.js +++ b/src/legacy/ui/public/management/index.js @@ -23,7 +23,8 @@ export { PAGE_FOOTER_COMPONENT, } from '../../../core_plugins/kibana/public/management/sections/settings/components/default_component_registry'; export { registerSettingsComponent } from '../../../core_plugins/kibana/public/management/sections/settings/components/component_registry'; -export { Field } from '../../../core_plugins/kibana/public/management/sections/settings/components/field/field'; -export { management } from './sections_register'; export { SidebarNav } from './components'; export { MANAGEMENT_BREADCRUMB } from './breadcrumbs'; + +import { npStart } from 'ui/new_platform'; +export const management = npStart.plugins.management.legacy; diff --git a/src/legacy/ui/public/new_platform/__mocks__/helpers.ts b/src/legacy/ui/public/new_platform/__mocks__/helpers.ts index a0a917bc48049..d1d7603bd82ed 100644 --- a/src/legacy/ui/public/new_platform/__mocks__/helpers.ts +++ b/src/legacy/ui/public/new_platform/__mocks__/helpers.ts @@ -25,6 +25,7 @@ import { navigationPluginMock } from '../../../../../plugins/navigation/public/m import { expressionsPluginMock } from '../../../../../plugins/expressions/public/mocks'; import { inspectorPluginMock } from '../../../../../plugins/inspector/public/mocks'; import { uiActionsPluginMock } from '../../../../../plugins/ui_actions/public/mocks'; +import { managementPluginMock } from '../../../../../plugins/management/public/mocks'; import { usageCollectionPluginMock } from '../../../../../plugins/usage_collection/public/mocks'; /* eslint-enable @kbn/eslint/no-restricted-paths */ @@ -45,6 +46,7 @@ export const pluginsMock = { inspector: inspectorPluginMock.createStartContract(), expressions: expressionsPluginMock.createStartContract(), uiActions: uiActionsPluginMock.createStartContract(), + management: managementPluginMock.createStartContract(), }), }; diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 20a7e0d5dde8d..3b2a77cefb730 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -137,6 +137,13 @@ export const npStart = { chrome: {}, }, plugins: { + management: { + legacy: { + getSection: () => ({ + register: sinon.fake(), + }), + }, + }, embeddable: { getEmbeddableFactory: sinon.fake(), getEmbeddableFactories: sinon.fake(), diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index 138bb4135c507..df3fa7c6ea466 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -32,6 +32,7 @@ import { DevToolsSetup, DevToolsStart } from '../../../../plugins/dev_tools/publ import { KibanaLegacySetup, KibanaLegacyStart } from '../../../../plugins/kibana_legacy/public'; import { HomePublicPluginSetup, HomePublicPluginStart } from '../../../../plugins/home/public'; import { SharePluginSetup, SharePluginStart } from '../../../../plugins/share/public'; +import { ManagementStart } from '../../../../plugins/management/public'; import { BfetchPublicSetup, BfetchPublicStart } from '../../../../plugins/bfetch/public'; import { UsageCollectionSetup } from '../../../../plugins/usage_collection/public'; import { @@ -67,6 +68,7 @@ export interface PluginsStart { dev_tools: DevToolsStart; kibana_legacy: KibanaLegacyStart; share: SharePluginStart; + management: ManagementStart; } export const npSetup = { diff --git a/src/legacy/ui/public/routes/route_manager.js b/src/legacy/ui/public/routes/route_manager.js index cf6413fb5ba7e..1cf2a5fc5a64a 100644 --- a/src/legacy/ui/public/routes/route_manager.js +++ b/src/legacy/ui/public/routes/route_manager.js @@ -56,7 +56,23 @@ export default function RouteManager() { } }; - self.run = function($location, $route, $injector) { + self.run = function($location, $route, $injector, $rootScope) { + if (window.elasticApm && typeof window.elasticApm.startTransaction === 'function') { + /** + * capture route-change events as transactions which happens after + * the browser's on load event. + * + * In Kibana app, this logic would run after the boostrap js files gets + * downloaded and get associated with the page-load transaction + */ + $rootScope.$on('$routeChangeStart', (_, nextRoute) => { + if (nextRoute.$$route) { + const name = nextRoute.$$route.originalPath; + window.elasticApm.startTransaction(name, 'route-change'); + } + }); + } + self.getBreadcrumbs = () => { const breadcrumbs = parsePathToBreadcrumbs($location.path()); const map = $route.current.mapBreadcrumbs; diff --git a/src/legacy/ui/ui_bundles/app_entry_template.js b/src/legacy/ui/ui_bundles/app_entry_template.js index 6e270b24ff4a9..2a95b48e83603 100644 --- a/src/legacy/ui/ui_bundles/app_entry_template.js +++ b/src/legacy/ui/ui_bundles/app_entry_template.js @@ -17,6 +17,8 @@ * under the License. */ +import { apmImport, apmInit } from '../apm'; + export const appEntryTemplate = bundle => ` /** * Kibana entry file @@ -34,12 +36,14 @@ import 'custom-event-polyfill'; import 'whatwg-fetch'; import 'abortcontroller-polyfill'; import 'childnode-remove-polyfill'; - +${apmImport()} import { i18n } from '@kbn/i18n'; import { CoreSystem } from '__kibanaCore__' const injectedMetadata = JSON.parse(document.querySelector('kbn-injected-metadata').getAttribute('data')); +${apmInit('injectedMetadata.apm')} + i18n.load(injectedMetadata.i18n.translationsUrl) .catch(e => e) .then((i18nError) => { diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index e3189f02bbbf2..549d4fb167e11 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -28,6 +28,7 @@ import { AppBootstrap } from './bootstrap'; import { mergeVariables } from './lib'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { fromRoot } from '../../../core/server/utils'; +import { getApmConfig } from '../apm'; export function uiRenderMixin(kbnServer, server, config) { function replaceInjectedVars(request, injectedVars) { @@ -282,6 +283,8 @@ export function uiRenderMixin(kbnServer, server, config) { uiPlugins, legacyMetadata, + + apm: getApmConfig(legacyMetadata.app), }, }); diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 0a1ba48342244..98cdd20ea4b84 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -20,6 +20,7 @@ import { IFieldType } from './fields'; export interface IIndexPattern { + [key: string]: any; fields: IFieldType[]; title: string; id?: string; diff --git a/src/plugins/data/server/autocomplete/autocomplete_service.ts b/src/plugins/data/server/autocomplete/autocomplete_service.ts new file mode 100644 index 0000000000000..1b85321aa2185 --- /dev/null +++ b/src/plugins/data/server/autocomplete/autocomplete_service.ts @@ -0,0 +1,29 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CoreSetup, Plugin } from 'kibana/server'; +import { registerRoutes } from './routes'; + +export class AutocompleteService implements Plugin { + public setup(core: CoreSetup) { + registerRoutes(core); + } + + public start() {} +} diff --git a/src/plugins/data/server/autocomplete/index.ts b/src/plugins/data/server/autocomplete/index.ts new file mode 100644 index 0000000000000..6c10a8c98bdbf --- /dev/null +++ b/src/plugins/data/server/autocomplete/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { AutocompleteService } from './autocomplete_service'; diff --git a/src/plugins/data/server/autocomplete/routes.ts b/src/plugins/data/server/autocomplete/routes.ts new file mode 100644 index 0000000000000..9134287d2b8ff --- /dev/null +++ b/src/plugins/data/server/autocomplete/routes.ts @@ -0,0 +1,27 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CoreSetup } from 'kibana/server'; +import { registerValueSuggestionsRoute } from './value_suggestions_route'; + +export function registerRoutes({ http }: CoreSetup): void { + const router = http.createRouter(); + + registerValueSuggestionsRoute(router); +} diff --git a/src/plugins/data/server/autocomplete/value_suggestions_route.ts b/src/plugins/data/server/autocomplete/value_suggestions_route.ts new file mode 100644 index 0000000000000..b415e83becf93 --- /dev/null +++ b/src/plugins/data/server/autocomplete/value_suggestions_route.ts @@ -0,0 +1,135 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { get, map } from 'lodash'; +import { schema } from '@kbn/config-schema'; +import { IRouter } from 'kibana/server'; + +import { IFieldType, indexPatterns, esFilters } from '../index'; + +export function registerValueSuggestionsRoute(router: IRouter) { + router.post( + { + path: '/api/kibana/suggestions/values/{index}', + validate: { + params: schema.object( + { + index: schema.string(), + }, + { allowUnknowns: false } + ), + body: schema.object( + { + field: schema.string(), + query: schema.string(), + boolFilter: schema.maybe(schema.any()), + }, + { allowUnknowns: false } + ), + }, + }, + async (context, request, response) => { + const { client: uiSettings } = context.core.uiSettings; + const { field: fieldName, query, boolFilter } = request.body; + const { index } = request.params; + const { dataClient } = context.core.elasticsearch; + + const autocompleteSearchOptions = { + timeout: await uiSettings.get('kibana.autocompleteTimeout'), + terminate_after: await uiSettings.get('kibana.autocompleteTerminateAfter'), + }; + + const indexPattern = await indexPatterns.findIndexPatternById( + context.core.savedObjects.client, + index + ); + + const field = indexPattern && indexPatterns.getFieldByName(fieldName, indexPattern); + const body = await getBody(autocompleteSearchOptions, field || fieldName, query, boolFilter); + + try { + const result = await dataClient.callAsCurrentUser('search', { index, body }); + + const buckets: any[] = + get(result, 'aggregations.suggestions.buckets') || + get(result, 'aggregations.nestedSuggestions.suggestions.buckets'); + + return response.ok({ body: map(buckets || [], 'key') }); + } catch (error) { + return response.internalError({ body: error }); + } + } + ); +} + +async function getBody( + { timeout, terminate_after }: Record, + field: IFieldType | string, + query: string, + boolFilter: esFilters.Filter[] = [] +) { + const isFieldObject = (f: any): f is IFieldType => Boolean(f && f.name); + + // https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#_standard_operators + const getEscapedQuery = (q: string = '') => + q.replace(/[.?+*|{}[\]()"\\#@&<>~]/g, match => `\\${match}`); + + // Helps ensure that the regex is not evaluated eagerly against the terms dictionary + const executionHint = 'map'; + + // We don't care about the accuracy of the counts, just the content of the terms, so this reduces + // the amount of information that needs to be transmitted to the coordinating node + const shardSize = 10; + const body = { + size: 0, + timeout, + terminate_after, + query: { + bool: { + filter: boolFilter, + }, + }, + aggs: { + suggestions: { + terms: { + field: isFieldObject(field) ? field.name : field, + include: `${getEscapedQuery(query)}.*`, + execution_hint: executionHint, + shard_size: shardSize, + }, + }, + }, + }; + + if (isFieldObject(field) && field.subType && field.subType.nested) { + return { + ...body, + aggs: { + nestedSuggestions: { + nested: { + path: field.subType.nested.path, + }, + aggs: body.aggs, + }, + }, + }; + } + + return body; +} diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 022eb0ae50295..fe96c494bd9ff 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -57,6 +57,7 @@ export { IndexPatternsFetcher, FieldDescriptor, shouldReadFieldFromDocValues, + indexPatterns, } from './index_patterns'; export * from './search'; export { diff --git a/src/plugins/data/server/index_patterns/index.ts b/src/plugins/data/server/index_patterns/index.ts index 6937fa22c4e5d..b303ae30ea810 100644 --- a/src/plugins/data/server/index_patterns/index.ts +++ b/src/plugins/data/server/index_patterns/index.ts @@ -16,6 +16,8 @@ * specific language governing permissions and limitations * under the License. */ +import * as indexPatterns from './utils'; export { IndexPatternsFetcher, FieldDescriptor, shouldReadFieldFromDocValues } from './fetcher'; export { IndexPatternsService } from './index_patterns_service'; +export { indexPatterns }; diff --git a/src/plugins/data/server/index_patterns/utils.ts b/src/plugins/data/server/index_patterns/utils.ts new file mode 100644 index 0000000000000..b7adafaeb3e94 --- /dev/null +++ b/src/plugins/data/server/index_patterns/utils.ts @@ -0,0 +1,47 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; +import { IIndexPattern, IFieldType } from '../../common'; + +export const getFieldByName = ( + fieldName: string, + indexPattern: IIndexPattern +): IFieldType | undefined => { + const fields: IFieldType[] = indexPattern && JSON.parse(indexPattern.attributes.fields); + const field = fields && fields.find(f => f.name === fieldName); + + return field; +}; + +export const findIndexPatternById = async ( + savedObjectsClient: SavedObjectsClientContract, + index: string +): Promise => { + const savedObjectsResponse = await savedObjectsClient.find({ + type: 'index-pattern', + fields: ['fields'], + search: `"${index}"`, + searchFields: ['title'], + }); + + if (savedObjectsResponse.total > 0) { + return (savedObjectsResponse.saved_objects[0] as unknown) as IIndexPattern; + } +}; diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index e81250e653ebd..6df0a11e7538d 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -21,18 +21,25 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../.. import { IndexPatternsService } from './index_patterns'; import { ISearchSetup } from './search'; import { SearchService } from './search/search_service'; +import { AutocompleteService } from './autocomplete'; export interface DataPluginSetup { search: ISearchSetup; } + export class DataServerPlugin implements Plugin { private readonly searchService: SearchService; + private readonly autocompleteService = new AutocompleteService(); private readonly indexPatterns = new IndexPatternsService(); + constructor(initializerContext: PluginInitializerContext) { this.searchService = new SearchService(initializerContext); } + public setup(core: CoreSetup) { this.indexPatterns.setup(core); + this.autocompleteService.setup(core); + return { search: this.searchService.setup(core), }; diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts index 81ef707ed0eba..258b0e94ef955 100644 --- a/src/plugins/kibana_react/public/index.ts +++ b/src/plugins/kibana_react/public/index.ts @@ -25,4 +25,4 @@ export * from './overlays'; export * from './ui_settings'; export * from './field_icon'; export * from './table_list_view'; -export { toMountPoint, useShallowCompareEffect } from './util'; +export { toMountPoint, useObservable, useShallowCompareEffect } from './util'; diff --git a/src/plugins/management/kibana.json b/src/plugins/management/kibana.json new file mode 100644 index 0000000000000..755a387afbd05 --- /dev/null +++ b/src/plugins/management/kibana.json @@ -0,0 +1,7 @@ +{ + "id": "management", + "version": "kibana", + "server": false, + "ui": true, + "requiredPlugins": [] +} diff --git a/src/plugins/management/public/index.ts b/src/plugins/management/public/index.ts new file mode 100644 index 0000000000000..ee3866c734f19 --- /dev/null +++ b/src/plugins/management/public/index.ts @@ -0,0 +1,27 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { PluginInitializerContext } from 'kibana/public'; +import { ManagementPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new ManagementPlugin(); +} + +export { ManagementStart } from './types'; diff --git a/src/plugins/management/public/legacy/index.js b/src/plugins/management/public/legacy/index.js new file mode 100644 index 0000000000000..63b9d2c6b27d7 --- /dev/null +++ b/src/plugins/management/public/legacy/index.js @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { management } from './sections_register'; diff --git a/src/legacy/ui/public/management/section.js b/src/plugins/management/public/legacy/section.js similarity index 92% rename from src/legacy/ui/public/management/section.js rename to src/plugins/management/public/legacy/section.js index b6952695cd910..f269e3fe295b7 100644 --- a/src/legacy/ui/public/management/section.js +++ b/src/plugins/management/public/legacy/section.js @@ -18,8 +18,7 @@ */ import { assign } from 'lodash'; -import { IndexedArray } from '../indexed_array'; -import { capabilities } from '../capabilities'; +import { IndexedArray } from '../../../../legacy/ui/public/indexed_array'; const listeners = []; @@ -37,7 +36,7 @@ export class ManagementSection { * @returns {ManagementSection} */ - constructor(id, options = {}) { + constructor(id, options = {}, capabilities) { this.display = id; this.id = id; this.items = new IndexedArray({ @@ -49,13 +48,14 @@ export class ManagementSection { this.tooltip = ''; this.icon = ''; this.url = ''; + this.capabilities = capabilities; assign(this, options); } get visibleItems() { return this.items.inOrder.filter(item => { - const capabilityManagementSection = capabilities.get().management[this.id]; + const capabilityManagementSection = this.capabilities.management[this.id]; const itemCapability = capabilityManagementSection ? capabilityManagementSection[item.id] : null; @@ -83,7 +83,7 @@ export class ManagementSection { */ register(id, options = {}) { - const item = new ManagementSection(id, assign(options, { parent: this })); + const item = new ManagementSection(id, assign(options, { parent: this }), this.capabilities); if (this.hasItem(id)) { throw new Error(`'${id}' is already registered`); diff --git a/src/legacy/ui/public/management/section.test.js b/src/plugins/management/public/legacy/section.test.js similarity index 83% rename from src/legacy/ui/public/management/section.test.js rename to src/plugins/management/public/legacy/section.test.js index e3e0dd3a427cb..61bafd298afb3 100644 --- a/src/legacy/ui/public/management/section.test.js +++ b/src/plugins/management/public/legacy/section.test.js @@ -16,62 +16,59 @@ * specific language governing permissions and limitations * under the License. */ -jest.mock('ui/capabilities', () => ({ - capabilities: { - get: () => ({ - navLinks: {}, - management: { - kibana: { - sampleFeature1: true, - sampleFeature2: false, - }, - }, - }), - }, -})); import { ManagementSection } from './section'; -import { IndexedArray } from '../indexed_array'; +import { IndexedArray } from '../../../../legacy/ui/public/indexed_array'; + +const capabilitiesMock = { + management: { + kibana: { sampleFeature2: false }, + }, +}; describe('ManagementSection', () => { describe('constructor', () => { it('defaults display to id', () => { - const section = new ManagementSection('kibana'); + const section = new ManagementSection('kibana', {}, capabilitiesMock); expect(section.display).toBe('kibana'); }); it('defaults visible to true', () => { - const section = new ManagementSection('kibana'); + const section = new ManagementSection('kibana', {}, capabilitiesMock); expect(section.visible).toBe(true); }); it('defaults disabled to false', () => { - const section = new ManagementSection('kibana'); + const section = new ManagementSection('kibana', {}, capabilitiesMock); expect(section.disabled).toBe(false); }); it('defaults tooltip to empty string', () => { - const section = new ManagementSection('kibana'); + const section = new ManagementSection('kibana', {}, capabilitiesMock); expect(section.tooltip).toBe(''); }); it('defaults url to empty string', () => { - const section = new ManagementSection('kibana'); + const section = new ManagementSection('kibana', {}, capabilitiesMock); expect(section.url).toBe(''); }); it('exposes items', () => { - const section = new ManagementSection('kibana'); + const section = new ManagementSection('kibana', {}, capabilitiesMock); expect(section.items).toHaveLength(0); }); it('exposes visibleItems', () => { - const section = new ManagementSection('kibana'); + const section = new ManagementSection('kibana', {}, capabilitiesMock); expect(section.visibleItems).toHaveLength(0); }); it('assigns all options', () => { - const section = new ManagementSection('kibana', { description: 'test', url: 'foobar' }); + const section = new ManagementSection( + 'kibana', + { description: 'test', url: 'foobar' }, + capabilitiesMock + ); expect(section.description).toBe('test'); expect(section.url).toBe('foobar'); }); @@ -81,7 +78,7 @@ describe('ManagementSection', () => { let section; beforeEach(() => { - section = new ManagementSection('kibana'); + section = new ManagementSection('kibana', {}, capabilitiesMock); }); it('returns a ManagementSection', () => { @@ -129,7 +126,7 @@ describe('ManagementSection', () => { let section; beforeEach(() => { - section = new ManagementSection('kibana'); + section = new ManagementSection('kibana', {}, capabilitiesMock); section.register('about'); }); @@ -160,7 +157,7 @@ describe('ManagementSection', () => { let section; beforeEach(() => { - section = new ManagementSection('kibana'); + section = new ManagementSection('kibana', {}, capabilitiesMock); section.register('about'); }); @@ -187,7 +184,7 @@ describe('ManagementSection', () => { let section; beforeEach(() => { - section = new ManagementSection('kibana'); + section = new ManagementSection('kibana', {}, capabilitiesMock); section.register('three', { order: 3 }); section.register('one', { order: 1 }); @@ -217,7 +214,7 @@ describe('ManagementSection', () => { let section; beforeEach(() => { - section = new ManagementSection('kibana'); + section = new ManagementSection('kibana', {}, capabilitiesMock); }); it('hide sets visible to false', () => { @@ -236,7 +233,7 @@ describe('ManagementSection', () => { let section; beforeEach(() => { - section = new ManagementSection('kibana'); + section = new ManagementSection('kibana', {}, capabilitiesMock); }); it('disable sets disabled to true', () => { @@ -254,7 +251,7 @@ describe('ManagementSection', () => { let section; beforeEach(() => { - section = new ManagementSection('kibana'); + section = new ManagementSection('kibana', {}, capabilitiesMock); section.register('three', { order: 3 }); section.register('one', { order: 1 }); diff --git a/src/legacy/ui/public/management/sections_register.js b/src/plugins/management/public/legacy/sections_register.js similarity index 54% rename from src/legacy/ui/public/management/sections_register.js rename to src/plugins/management/public/legacy/sections_register.js index a63fd390ea3ca..888b2c5bc3aeb 100644 --- a/src/legacy/ui/public/management/sections_register.js +++ b/src/plugins/management/public/legacy/sections_register.js @@ -20,33 +20,41 @@ import { ManagementSection } from './section'; import { i18n } from '@kbn/i18n'; -export const management = new ManagementSection('management', { - display: i18n.translate('common.ui.management.displayName', { - defaultMessage: 'Management', - }), -}); +export const management = capabilities => { + const main = new ManagementSection( + 'management', + { + display: i18n.translate('management.displayName', { + defaultMessage: 'Management', + }), + }, + capabilities + ); -management.register('data', { - display: i18n.translate('common.ui.management.connectDataDisplayName', { - defaultMessage: 'Connect Data', - }), - order: 0, -}); + main.register('data', { + display: i18n.translate('management.connectDataDisplayName', { + defaultMessage: 'Connect Data', + }), + order: 0, + }); -management.register('elasticsearch', { - display: 'Elasticsearch', - order: 20, - icon: 'logoElasticsearch', -}); + main.register('elasticsearch', { + display: 'Elasticsearch', + order: 20, + icon: 'logoElasticsearch', + }); -management.register('kibana', { - display: 'Kibana', - order: 30, - icon: 'logoKibana', -}); + main.register('kibana', { + display: 'Kibana', + order: 30, + icon: 'logoKibana', + }); -management.register('logstash', { - display: 'Logstash', - order: 30, - icon: 'logoLogstash', -}); + main.register('logstash', { + display: 'Logstash', + order: 30, + icon: 'logoLogstash', + }); + + return main; +}; diff --git a/src/legacy/core_plugins/kibana/server/routes/api/suggestions/index.js b/src/plugins/management/public/mocks/index.ts similarity index 83% rename from src/legacy/core_plugins/kibana/server/routes/api/suggestions/index.js rename to src/plugins/management/public/mocks/index.ts index 15110b192ee33..cc56928e8e529 100644 --- a/src/legacy/core_plugins/kibana/server/routes/api/suggestions/index.js +++ b/src/plugins/management/public/mocks/index.ts @@ -17,8 +17,10 @@ * under the License. */ -import { registerValueSuggestions } from './register_value_suggestions'; +const createStartContract = () => ({ + legacy: {}, +}); -export function registerSuggestionsApi(server) { - registerValueSuggestions(server); -} +export const managementPluginMock = { + createStartContract, +}; diff --git a/src/plugins/management/public/plugin.ts b/src/plugins/management/public/plugin.ts new file mode 100644 index 0000000000000..c65dfd1dc7bb4 --- /dev/null +++ b/src/plugins/management/public/plugin.ts @@ -0,0 +1,35 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; +import { ManagementStart } from './types'; +// @ts-ignore +import { management } from './legacy'; + +export class ManagementPlugin implements Plugin<{}, ManagementStart> { + public setup(core: CoreSetup) { + return {}; + } + + public start(core: CoreStart) { + return { + legacy: management(core.application.capabilities), + }; + } +} diff --git a/src/legacy/core_plugins/timelion/public/services/saved_sheet_register.js b/src/plugins/management/public/types.ts similarity index 88% rename from src/legacy/core_plugins/timelion/public/services/saved_sheet_register.js rename to src/plugins/management/public/types.ts index 0353a4d742dd3..6ca1faf338c39 100644 --- a/src/legacy/core_plugins/timelion/public/services/saved_sheet_register.js +++ b/src/plugins/management/public/types.ts @@ -17,8 +17,6 @@ * under the License. */ -import './saved_sheets'; - -export default function savedSearchObjectFn(savedSheets) { - return savedSheets; +export interface ManagementStart { + legacy: any; } diff --git a/test/plugin_functional/test_suites/core_plugins/ui_settings.ts b/test/plugin_functional/test_suites/core_plugins/ui_settings.ts index 205dbc548725a..2b4227ee798e3 100644 --- a/test/plugin_functional/test_suites/core_plugins/ui_settings.ts +++ b/test/plugin_functional/test_suites/core_plugins/ui_settings.ts @@ -46,7 +46,7 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider await supertest .get('/api/ui-settings-plugin') .expect(200) - .expect({ uiSettingsValue: '2' }); + .expect({ uiSettingsValue: 2 }); }); }); } diff --git a/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap b/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap index 9d82cd6b5455c..e345ca3552e5a 100644 --- a/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap +++ b/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap @@ -68,6 +68,8 @@ exports[`Error SERVICE_NAME 1`] = `"service name"`; exports[`Error SERVICE_NODE_NAME 1`] = `undefined`; +exports[`Error SERVICE_VERSION 1`] = `undefined`; + exports[`Error SPAN_ACTION 1`] = `undefined`; exports[`Error SPAN_DURATION 1`] = `undefined`; @@ -174,6 +176,8 @@ exports[`Span SERVICE_NAME 1`] = `"service name"`; exports[`Span SERVICE_NODE_NAME 1`] = `undefined`; +exports[`Span SERVICE_VERSION 1`] = `undefined`; + exports[`Span SPAN_ACTION 1`] = `"my action"`; exports[`Span SPAN_DURATION 1`] = `1337`; @@ -280,6 +284,8 @@ exports[`Transaction SERVICE_NAME 1`] = `"service name"`; exports[`Transaction SERVICE_NODE_NAME 1`] = `undefined`; +exports[`Transaction SERVICE_VERSION 1`] = `undefined`; + exports[`Transaction SPAN_ACTION 1`] = `undefined`; exports[`Transaction SPAN_DURATION 1`] = `undefined`; diff --git a/x-pack/legacy/plugins/apm/common/annotations.ts b/x-pack/legacy/plugins/apm/common/annotations.ts new file mode 100644 index 0000000000000..33122f55d8800 --- /dev/null +++ b/x-pack/legacy/plugins/apm/common/annotations.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export enum AnnotationType { + VERSION = 'version' +} + +export interface Annotation { + type: AnnotationType; + id: string; + time: number; + text: string; +} diff --git a/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts b/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts index b8b35e79a9908..0d7ff3114e73f 100644 --- a/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts +++ b/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts @@ -8,6 +8,7 @@ export const SERVICE_NAME = 'service.name'; export const SERVICE_ENVIRONMENT = 'service.environment'; export const SERVICE_AGENT_NAME = 'agent.name'; export const SERVICE_NODE_NAME = 'service.node.name'; +export const SERVICE_VERSION = 'service.version'; export const URL_FULL = 'url.full'; export const HTTP_REQUEST_METHOD = 'http.request.method'; export const USER_ID = 'user.id'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx index f2d2f28fc975a..2e1efabf47b2c 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx @@ -80,7 +80,7 @@ const ErrorGroupOverview: React.FC = () => { const localUIFiltersConfig = useMemo(() => { const config: React.ComponentProps = { - filterNames: ['host', 'containerId', 'podName'], + filterNames: ['host', 'containerId', 'podName', 'serviceVersion'], params: { serviceName }, diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx index 4158bb877e459..91483b4b52d90 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx @@ -148,7 +148,9 @@ export class ServiceIntegrations extends React.Component { panels={[ { id: 0, - items: this.getPanelItems(license.features.ml?.is_available) + items: this.getPanelItems( + license?.getFeature('ml').isAvailable + ) } ]} /> diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/PlatinumLicensePrompt.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/PlatinumLicensePrompt.tsx new file mode 100644 index 0000000000000..c5771995daa24 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/PlatinumLicensePrompt.tsx @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { + EuiEmptyPrompt, + EuiButton, + EuiPanel, + EuiFlexGroup, + EuiFlexItem +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { useKibanaUrl } from '../../../hooks/useKibanaUrl'; + +export function PlatinumLicensePrompt() { + // Set the height to give it some top margin + const style = { height: '60vh' }; + + const licensePageUrl = useKibanaUrl( + '/app/kibana', + '/management/elasticsearch/license_management/home' + ); + + return ( + + + + + {i18n.translate( + 'xpack.apm.serviceMap.licensePromptButtonText', + { + defaultMessage: 'Start 30-day Platinum trial' + } + )} + + ]} + body={ +

+ {i18n.translate('xpack.apm.serviceMap.licensePromptBody', { + defaultMessage: + "In order to access Service Maps, you must be subscribed to an Elastic Platinum license. With it, you'll have the ability to visualize your entire application stack along with your APM data." + })} +

+ } + title={ +

+ {i18n.translate('xpack.apm.serviceMap.licensePromptTitle', { + defaultMessage: 'Service maps is available in Platinum.' + })} +

+ } + /> +
+
+
+ ); +} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx index 9b0668028c42f..16a91116ae762 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx @@ -4,12 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; import theme from '@elastic/eui/dist/eui_theme_light.json'; -import { useUrlParams } from '../../../hooks/useUrlParams'; +import React from 'react'; import { useFetcher } from '../../../hooks/useFetcher'; -import { Cytoscape } from './Cytoscape'; +import { useLicense } from '../../../hooks/useLicense'; +import { useUrlParams } from '../../../hooks/useUrlParams'; import { Controls } from './Controls'; +import { Cytoscape } from './Cytoscape'; +import { PlatinumLicensePrompt } from './PlatinumLicensePrompt'; interface ServiceMapProps { serviceName?: string; @@ -53,8 +55,11 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { ); const elements = Array.isArray(data) ? data : []; + const license = useLicense(); + const isValidPlatinumLicense = + license?.isActive && license?.type === 'platinum'; - return ( + return isValidPlatinumLicense ? ( + ) : ( + ); } diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx index 8005fc17f2a20..d01093be801a2 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx @@ -31,7 +31,7 @@ export function ServiceMetrics({ agentName }: ServiceMetricsProps) { const localFiltersConfig: React.ComponentProps = useMemo( () => ({ - filterNames: ['host', 'containerId', 'podName'], + filterNames: ['host', 'containerId', 'podName', 'serviceVersion'], params: { serviceName, serviceNodeName diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx index dbd0e9d3b9d22..9d6639b000762 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx @@ -50,7 +50,7 @@ export function TransactionDetails() { const localUIFiltersConfig = useMemo(() => { const config: React.ComponentProps = { - filterNames: ['transactionResult'], + filterNames: ['transactionResult', 'serviceVersion'], projection: PROJECTION.TRANSACTIONS, params: { transactionName, diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx index de356b5812e9a..439e3d80eef4f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx @@ -96,7 +96,13 @@ export function TransactionOverview() { const localFiltersConfig: React.ComponentProps = useMemo( () => ({ - filterNames: ['transactionResult', 'host', 'containerId', 'podName'], + filterNames: [ + 'transactionResult', + 'host', + 'containerId', + 'podName', + 'serviceVersion' + ], params: { serviceName, transactionType diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx index c99c7f12062e2..fcc0dc7d26695 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx @@ -21,7 +21,8 @@ const ErrorOverviewLink = ({ serviceName, query, ...rest }: Props) => { urlParams, 'host', 'containerId', - 'podName' + 'podName', + 'serviceVersion' ); return ( diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx index dd988f3e3720d..7d21e1efa44f2 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx @@ -19,7 +19,8 @@ const MetricOverviewLink = ({ serviceName, ...rest }: Props) => { urlParams, 'host', 'containerId', - 'podName' + 'podName', + 'serviceVersion' ); return ( diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeMetricOverviewLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeMetricOverviewLink.tsx index 925d85fa0663e..527c3da9e7e1c 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeMetricOverviewLink.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeMetricOverviewLink.tsx @@ -24,7 +24,8 @@ const ServiceNodeMetricOverviewLink = ({ urlParams, 'host', 'containerId', - 'podName' + 'podName', + 'serviceVersion' ); return ( diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx index 10e55d3382448..db1b6ec117bf4 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx @@ -19,7 +19,8 @@ const ServiceNodeOverviewLink = ({ serviceName, ...rest }: Props) => { urlParams, 'host', 'containerId', - 'podName' + 'podName', + 'serviceVersion' ); return ( diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TransactionDetailLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TransactionDetailLink.tsx index a4ac05379615a..784f9b36ff621 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TransactionDetailLink.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TransactionDetailLink.tsx @@ -27,7 +27,11 @@ export const TransactionDetailLink = ({ }: Props) => { const { urlParams } = useUrlParams(); - const persistedFilters = pickKeys(urlParams, 'transactionResult'); + const persistedFilters = pickKeys( + urlParams, + 'transactionResult', + 'serviceVersion' + ); return ( { 'transactionResult', 'host', 'containerId', - 'podName' + 'podName', + 'serviceVersion' ); return ( diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx new file mode 100644 index 0000000000000..fb087612f8e3d --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { VerticalGridLines } from 'react-vis'; +import theme from '@elastic/eui/dist/eui_theme_light.json'; +import { + EuiIcon, + EuiToolTip, + EuiFlexGroup, + EuiFlexItem, + EuiText +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { Maybe } from '../../../../../typings/common'; +import { Annotation } from '../../../../../common/annotations'; +import { PlotValues, SharedPlot } from './plotUtils'; +import { asAbsoluteDateTime } from '../../../../utils/formatters'; + +interface Props { + annotations: Annotation[]; + plotValues: PlotValues; + width: number; + overlay: Maybe; +} + +const style = { + stroke: theme.euiColorSecondary, + strokeDasharray: 'none' +}; + +export function AnnotationsPlot(props: Props) { + const { plotValues, annotations } = props; + + const tickValues = annotations.map(annotation => annotation.time); + + return ( + <> + + + + {annotations.map(annotation => ( +
+ + + + {i18n.translate('xpack.apm.version', { + defaultMessage: 'Version' + })} + + + {annotation.text} + + } + > + + +
+ ))} + + ); +} diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js index 11755e13bfdd6..848c975942ff6 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js @@ -16,6 +16,8 @@ import { truncate } from '../../../../style/variables'; import theme from '@elastic/eui/dist/eui_theme_light.json'; +import { i18n } from '@kbn/i18n'; +import { EuiIcon } from '@elastic/eui'; const Container = styled.div` display: flex; @@ -73,9 +75,12 @@ export default function Legends({ noHits, series, seriesEnabledState, - truncateLegends + truncateLegends, + hasAnnotations, + showAnnotations, + onAnnotationsToggle }) { - if (noHits) { + if (noHits && !hasAnnotations) { return null; } @@ -107,6 +112,30 @@ export default function Legends({ /> ); })} + {hasAnnotations && ( + { + if (onAnnotationsToggle) { + onAnnotationsToggle(); + } + }} + text={ + + {i18n.translate('xpack.apm.serviceVersion', { + defaultMessage: 'Service version' + })} + + } + indicator={() => ( +
+ +
+ )} + disabled={!showAnnotations} + color={theme.euiColorSecondary} + /> + )} ); @@ -118,5 +147,8 @@ Legends.propTypes = { noHits: PropTypes.bool.isRequired, series: PropTypes.array.isRequired, seriesEnabledState: PropTypes.array.isRequired, - truncateLegends: PropTypes.bool.isRequired + truncateLegends: PropTypes.bool.isRequired, + hasAnnotations: PropTypes.bool, + showAnnotations: PropTypes.bool, + onAnnotationsToggle: PropTypes.func }; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/index.js b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/index.js index e87d60c9b3fe8..f59c30d2f4d2f 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/index.js +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/index.js @@ -13,6 +13,7 @@ import Legends from './Legends'; import StaticPlot from './StaticPlot'; import InteractivePlot from './InteractivePlot'; import VoronoiPlot from './VoronoiPlot'; +import { AnnotationsPlot } from './AnnotationsPlot'; import { createSelector } from 'reselect'; import { getPlotValues } from './plotUtils'; import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue'; @@ -28,7 +29,8 @@ export class InnerCustomPlot extends PureComponent { seriesEnabledState: [], isDrawing: false, selectionStart: null, - selectionEnd: null + selectionEnd: null, + showAnnotations: true }; getEnabledSeries = createSelector( @@ -122,7 +124,7 @@ export class InnerCustomPlot extends PureComponent { } render() { - const { series, truncateLegends, width } = this.props; + const { series, truncateLegends, width, annotations } = this.props; if (!width) { return null; @@ -166,6 +168,14 @@ export class InnerCustomPlot extends PureComponent { tickFormatX={this.props.tickFormatX} /> + {this.state.showAnnotations && !isEmpty(annotations) && ( + + )} + { + this.setState(({ showAnnotations }) => ({ + showAnnotations: !showAnnotations + })); + }} /> ); @@ -209,7 +226,14 @@ InnerCustomPlot.propTypes = { truncateLegends: PropTypes.bool, width: PropTypes.number.isRequired, height: PropTypes.number, - stackBy: PropTypes.string + stackBy: PropTypes.string, + annotations: PropTypes.arrayOf( + PropTypes.shape({ + type: PropTypes.string, + id: PropTypes.string, + firstSeen: PropTypes.number + }) + ) }; InnerCustomPlot.defaultProps = { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts index 55bfb490e8588..b130deed7f098 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts @@ -6,6 +6,7 @@ // @ts-ignore import * as plotUtils from './plotUtils'; +import { TimeSeries, Coordinate } from '../../../../../typings/timeseries'; describe('plotUtils', () => { describe('getPlotValues', () => { @@ -34,7 +35,10 @@ describe('plotUtils', () => { expect( plotUtils .getPlotValues( - [{ data: { x: 0, y: 200 } }, { data: { x: 0, y: 300 } }], + [ + { data: [{ x: 0, y: 200 }] }, + { data: [{ x: 0, y: 300 }] } + ] as Array>, [], { height: 1, diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.js b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx similarity index 74% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.js rename to x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx index 4186f6c899750..10eb4659ea695 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.js +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx @@ -11,6 +11,7 @@ import d3 from 'd3'; import PropTypes from 'prop-types'; import React from 'react'; +import { TimeSeries, Coordinate } from '../../../../../typings/timeseries'; import { unit } from '../../../../style/variables'; import { getTimezoneOffsetInMs } from './getTimezoneOffsetInMs'; @@ -22,20 +23,23 @@ const XY_MARGIN = { bottom: unit * 2 }; -const getXScale = (xMin, xMax, width) => { +const getXScale = (xMin: number, xMax: number, width: number) => { return scaleLinear() .domain([xMin, xMax]) .range([XY_MARGIN.left, width - XY_MARGIN.right]); }; -const getYScale = (yMin, yMax) => { +const getYScale = (yMin: number, yMax: number) => { return scaleLinear() .domain([yMin, yMax]) .range([XY_HEIGHT, 0]) .nice(); }; -function getFlattenedCoordinates(visibleSeries, enabledSeries) { +function getFlattenedCoordinates( + visibleSeries: Array>, + enabledSeries: Array> +) { const enabledCoordinates = flatten(enabledSeries.map(serie => serie.data)); if (!isEmpty(enabledCoordinates)) { return enabledCoordinates; @@ -44,10 +48,24 @@ function getFlattenedCoordinates(visibleSeries, enabledSeries) { return flatten(visibleSeries.map(serie => serie.data)); } +export type PlotValues = ReturnType; + export function getPlotValues( - visibleSeries, - enabledSeries, - { width, yMin = 0, yMax = 'max', height, stackBy } + visibleSeries: Array>, + enabledSeries: Array>, + { + width, + yMin = 0, + yMax = 'max', + height, + stackBy + }: { + width: number; + yMin?: number | 'min'; + yMax?: number | 'max'; + height: number; + stackBy?: 'x' | 'y'; + } ) { const flattenedCoordinates = getFlattenedCoordinates( visibleSeries, @@ -59,10 +77,10 @@ export function getPlotValues( const xMax = d3.max(flattenedCoordinates, d => d.x); if (yMax === 'max') { - yMax = d3.max(flattenedCoordinates, d => d.y); + yMax = d3.max(flattenedCoordinates, d => d.y ?? 0); } if (yMin === 'min') { - yMin = d3.min(flattenedCoordinates, d => d.y); + yMin = d3.min(flattenedCoordinates, d => d.y ?? 0); } const [xMinZone, xMaxZone] = [xMin, xMax].map(x => { @@ -101,11 +119,19 @@ export function getPlotValues( }; } -export function SharedPlot({ plotValues, ...props }) { +export function SharedPlot({ + plotValues, + ...props +}: { + plotValues: PlotValues; + children: React.ReactNode; +}) { const { XY_HEIGHT: height, XY_MARGIN: margin, XY_WIDTH: width } = plotValues; return ( -
+
- + {indicator ? indicator() : } {text} ); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx index 97794bf66687b..b0555da705a30 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx @@ -170,7 +170,7 @@ export class TransactionCharts extends Component { {license => - this.renderMLHeader(license.features.ml?.is_available) + this.renderMLHeader(license?.getFeature('ml').isAvailable) } diff --git a/x-pack/legacy/plugins/apm/public/context/ChartsSyncContext.tsx b/x-pack/legacy/plugins/apm/public/context/ChartsSyncContext.tsx index c2676a35d8e78..afce0811b48f6 100644 --- a/x-pack/legacy/plugins/apm/public/context/ChartsSyncContext.tsx +++ b/x-pack/legacy/plugins/apm/public/context/ChartsSyncContext.tsx @@ -7,6 +7,8 @@ import React, { useMemo, useState } from 'react'; import { toQuery, fromQuery } from '../components/shared/Links/url_helpers'; import { history } from '../utils/history'; +import { useUrlParams } from '../hooks/useUrlParams'; +import { useFetcher } from '../hooks/useFetcher'; const ChartsSyncContext = React.createContext<{ hoverX: number | null; @@ -17,6 +19,31 @@ const ChartsSyncContext = React.createContext<{ const ChartsSyncContextProvider: React.FC = ({ children }) => { const [time, setTime] = useState(null); + const { urlParams, uiFilters } = useUrlParams(); + + const { start, end, serviceName } = urlParams; + const { environment } = uiFilters; + + const { data = { annotations: [] } } = useFetcher( + callApmApi => { + if (start && end && serviceName) { + return callApmApi({ + pathname: '/api/apm/services/{serviceName}/annotations', + params: { + path: { + serviceName + }, + query: { + start, + end, + environment + } + } + }); + } + }, + [start, end, environment, serviceName] + ); const value = useMemo(() => { const hoverXHandlers = { @@ -43,11 +70,12 @@ const ChartsSyncContextProvider: React.FC = ({ children }) => { }) }); }, - hoverX: time + hoverX: time, + annotations: data.annotations }; return { ...hoverXHandlers }; - }, [time, setTime]); + }, [time, data.annotations]); return ; }; diff --git a/x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx b/x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx index 4bb246a2a745b..714e4c8985678 100644 --- a/x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx +++ b/x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx @@ -3,33 +3,27 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import React from 'react'; -import { FETCH_STATUS, useFetcher } from '../../hooks/useFetcher'; -import { loadLicense, LicenseApiResponse } from '../../services/rest/xpack'; -import { InvalidLicenseNotification } from './InvalidLicenseNotification'; +import { useObservable } from '../../../../../../../src/plugins/kibana_react/public'; +import { ILicense } from '../../../../../../plugins/licensing/public'; import { useApmPluginContext } from '../../hooks/useApmPluginContext'; +import { InvalidLicenseNotification } from './InvalidLicenseNotification'; -const initialLicense: LicenseApiResponse = { - features: {}, - license: { - is_active: false - } -}; -export const LicenseContext = React.createContext(initialLicense); +export const LicenseContext = React.createContext( + undefined +); -export const LicenseProvider: React.FC = ({ children }) => { - const { http } = useApmPluginContext().core; - const { data = initialLicense, status } = useFetcher( - () => loadLicense(http), - [http] - ); - const hasValidLicense = data.license.is_active; +export function LicenseProvider({ children }: { children: React.ReactChild }) { + const { license$ } = useApmPluginContext().plugins.licensing; + const license = useObservable(license$); + const hasInvalidLicense = !license?.isActive; // if license is invalid show an error message - if (status === FETCH_STATUS.SUCCESS && !hasValidLicense) { + if (hasInvalidLicense) { return ; } // render rest of application and pass down license via context - return ; -}; + return ; +} diff --git a/x-pack/legacy/plugins/apm/public/hooks/useKibanaUrl.ts b/x-pack/legacy/plugins/apm/public/hooks/useKibanaUrl.ts new file mode 100644 index 0000000000000..0b1c416040a2f --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/hooks/useKibanaUrl.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import url from 'url'; +import { useApmPluginContext } from './useApmPluginContext'; + +export function useKibanaUrl( + /** The path to the plugin */ path: string, + /** The hash path */ hash: string +) { + const { core } = useApmPluginContext(); + return url.format({ + pathname: core.http.basePath.prepend(path), + hash + }); +} diff --git a/x-pack/legacy/plugins/infra/common/log_summary/log_summary.ts b/x-pack/legacy/plugins/apm/public/hooks/useLicense.ts similarity index 60% rename from x-pack/legacy/plugins/infra/common/log_summary/log_summary.ts rename to x-pack/legacy/plugins/apm/public/hooks/useLicense.ts index 79d8fcc9fa60f..ca828e49706a8 100644 --- a/x-pack/legacy/plugins/infra/common/log_summary/log_summary.ts +++ b/x-pack/legacy/plugins/apm/public/hooks/useLicense.ts @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface LogSummaryBucket { - count: number; - end: number; - start: number; -} +import { useContext } from 'react'; +import { LicenseContext } from '../context/LicenseContext'; -export type SummaryBucketSize = 'y' | 'M' | 'w' | 'd' | 'h' | 'm' | 's'; +export function useLicense() { + return useContext(LicenseContext); +} diff --git a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx index 64784617442ef..216af91fbb591 100644 --- a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx +++ b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx @@ -6,35 +6,37 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { Router, Route, Switch } from 'react-router-dom'; +import { Route, Router, Switch } from 'react-router-dom'; +import { ApmRoute } from '@elastic/apm-rum-react'; import styled from 'styled-components'; import { metadata } from 'ui/metadata'; -import { HomePublicPluginSetup } from '../../../../../../src/plugins/home/public'; import { + CoreSetup, CoreStart, + PackageInfo, Plugin, - CoreSetup, - PluginInitializerContext, - PackageInfo + PluginInitializerContext } from '../../../../../../src/core/public'; import { DataPublicPluginSetup } from '../../../../../../src/plugins/data/public'; -import { history } from '../utils/history'; -import { LocationProvider } from '../context/LocationContext'; -import { UrlParamsProvider } from '../context/UrlParamsContext'; -import { px, unit, units } from '../style/variables'; -import { LoadingIndicatorProvider } from '../context/LoadingIndicatorContext'; -import { LicenseProvider } from '../context/LicenseContext'; -import { UpdateBreadcrumbs } from '../components/app/Main/UpdateBreadcrumbs'; +import { HomePublicPluginSetup } from '../../../../../../src/plugins/home/public'; +import { LicensingPluginSetup } from '../../../../../plugins/licensing/public'; import { routes } from '../components/app/Main/route_config'; import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange'; +import { UpdateBreadcrumbs } from '../components/app/Main/UpdateBreadcrumbs'; +import { ApmPluginContext } from '../context/ApmPluginContext'; +import { LicenseProvider } from '../context/LicenseContext'; +import { LoadingIndicatorProvider } from '../context/LoadingIndicatorContext'; +import { LocationProvider } from '../context/LocationContext'; import { MatchedRouteProvider } from '../context/MatchedRouteContext'; +import { UrlParamsProvider } from '../context/UrlParamsContext'; import { createStaticIndexPattern } from '../services/rest/index_pattern'; -import { setHelpExtension } from './setHelpExtension'; -import { setReadonlyBadge } from './updateBadge'; +import { px, unit, units } from '../style/variables'; +import { history } from '../utils/history'; import { featureCatalogueEntry } from './featureCatalogueEntry'; import { getConfigFromInjectedMetadata } from './getConfigFromInjectedMetadata'; +import { setHelpExtension } from './setHelpExtension'; import { toggleAppLinkInNav } from './toggleAppLinkInNav'; -import { ApmPluginContext } from '../context/ApmPluginContext'; +import { setReadonlyBadge } from './updateBadge'; export const REACT_APP_ROOT_ID = 'react-apm-root'; @@ -51,7 +53,7 @@ const App = () => { {routes.map((route, i) => ( - + ))} @@ -64,6 +66,7 @@ export type ApmPluginStart = void; export interface ApmPluginSetupDeps { data: DataPublicPluginSetup; home: HomePublicPluginSetup; + licensing: LicensingPluginSetup; } export interface ConfigSchema { diff --git a/x-pack/legacy/plugins/apm/public/services/rest/xpack.ts b/x-pack/legacy/plugins/apm/public/services/rest/xpack.ts deleted file mode 100644 index 95e283cd67709..0000000000000 --- a/x-pack/legacy/plugins/apm/public/services/rest/xpack.ts +++ /dev/null @@ -1,45 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { HttpServiceBase } from 'kibana/public'; -import { callApi } from './callApi'; - -export interface LicenseApiResponse { - license: { - is_active: boolean; - }; - features: { - beats_management?: Record; - graph?: Record; - grokdebugger?: Record; - index_management?: Record; - logstash?: Record; - ml?: { - is_available: boolean; - license_type: number; - has_expired: boolean; - enable_links: boolean; - show_links: boolean; - }; - reporting?: Record; - rollup?: Record; - searchprofiler?: Record; - security?: Record; - spaces?: Record; - tilemap?: Record; - watcher?: { - is_available: boolean; - enable_links: boolean; - show_links: boolean; - }; - }; -} - -export async function loadLicense(http: HttpServiceBase) { - return callApi(http, { - pathname: `/api/xpack/v1/info` - }); -} diff --git a/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx b/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx index 0c8a7cbc17884..862c982d6b5ac 100644 --- a/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx +++ b/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx @@ -19,7 +19,11 @@ import { MemoryRouter } from 'react-router-dom'; import { APMConfig } from '../../../../../plugins/apm/server'; import { LocationProvider } from '../context/LocationContext'; import { PromiseReturnType } from '../../typings/common'; -import { ESFilter } from '../../typings/elasticsearch'; +import { + ESFilter, + ESSearchResponse, + ESSearchRequest +} from '../../typings/elasticsearch'; import { ApmPluginContext, ApmPluginContextValue @@ -117,29 +121,41 @@ interface MockSetup { }; } +interface Options { + mockResponse?: ( + request: ESSearchRequest + ) => ESSearchResponse; +} + export async function inspectSearchParams( - fn: (mockSetup: MockSetup) => Promise + fn: (mockSetup: MockSetup) => Promise, + options: Options = {} ) { - const clientSpy = jest.fn().mockReturnValueOnce({ - hits: { - total: 0 - } + const spy = jest.fn().mockImplementation(async request => { + return options.mockResponse + ? options.mockResponse(request) + : { + hits: { + hits: { + total: { + value: 0 + } + } + } + }; }); - const internalClientSpy = jest.fn().mockReturnValueOnce({ - hits: { - total: 0 - } - }); + let response; + let error; const mockSetup = { start: 1528113600000, end: 1528977600000, client: { - search: clientSpy + search: spy } as any, internalClient: { - search: internalClientSpy + search: spy } as any, config: new Proxy( {}, @@ -164,21 +180,18 @@ export async function inspectSearchParams( dynamicIndexPattern: null as any }; try { - await fn(mockSetup); - } catch { + response = await fn(mockSetup); + } catch (err) { + error = err; // we're only extracting the search params } - let params; - if (clientSpy.mock.calls.length) { - params = clientSpy.mock.calls[0][0]; - } else { - params = internalClientSpy.mock.calls[0][0]; - } - return { - params, - teardown: () => clientSpy.mockClear() + params: spy.mock.calls[0][0], + response, + error, + spy, + teardown: () => spy.mockClear() }; } diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/multiple-versions.json b/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/multiple-versions.json new file mode 100644 index 0000000000000..7e2d2405d681c --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/multiple-versions.json @@ -0,0 +1,34 @@ +{ + "took": 444, + "timed_out": false, + "_shards": { + "total": 1, + "successful": 1, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 10000, + "relation": "gte" + }, + "max_score": null, + "hits": [] + }, + "aggregations": { + "versions": { + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0, + "buckets": [ + { + "key": "8.0.0", + "doc_count": 615285 + }, + { + "key": "7.5.0", + "doc_count": 615285 + } + ] + } + } +} diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/no-versions.json b/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/no-versions.json new file mode 100644 index 0000000000000..fa5c63f1b9a54 --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/no-versions.json @@ -0,0 +1,25 @@ +{ + "took": 398, + "timed_out": false, + "_shards": { + "total": 1, + "successful": 1, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 10000, + "relation": "gte" + }, + "max_score": null, + "hits": [] + }, + "aggregations": { + "versions": { + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0, + "buckets": [] + } + } +} diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/one-version.json b/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/one-version.json new file mode 100644 index 0000000000000..56303909bcd6f --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/one-version.json @@ -0,0 +1,30 @@ +{ + "took": 444, + "timed_out": false, + "_shards": { + "total": 1, + "successful": 1, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 10000, + "relation": "gte" + }, + "max_score": null, + "hits": [] + }, + "aggregations": { + "versions": { + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0, + "buckets": [ + { + "key": "8.0.0", + "doc_count": 615285 + } + ] + } + } +} diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/versions-first-seen.json b/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/versions-first-seen.json new file mode 100644 index 0000000000000..c53b28c8bf594 --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/versions-first-seen.json @@ -0,0 +1,24 @@ +{ + "took": 4750, + "timed_out": false, + "_shards": { + "total": 1, + "successful": 1, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 10000, + "relation": "gte" + }, + "max_score": null, + "hits": [] + }, + "aggregations": { + "first_seen": { + "value": 1.5281138E12, + "value_as_string": "2018-06-04T12:00:00.000Z" + } + } +} diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/index.test.ts b/x-pack/legacy/plugins/apm/server/lib/services/annotations/index.test.ts new file mode 100644 index 0000000000000..75ac0642a1b8c --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/services/annotations/index.test.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { getServiceAnnotations } from '.'; +import { + SearchParamsMock, + inspectSearchParams +} from '../../../../public/utils/testHelpers'; +import noVersions from './__fixtures__/no-versions.json'; +import oneVersion from './__fixtures__/one-version.json'; +import multipleVersions from './__fixtures__/multiple-versions.json'; +import versionsFirstSeen from './__fixtures__/versions-first-seen.json'; + +describe('getServiceAnnotations', () => { + let mock: SearchParamsMock; + + afterEach(() => { + mock.teardown(); + }); + + describe('with 0 versions', () => { + it('returns no annotations', async () => { + mock = await inspectSearchParams( + setup => + getServiceAnnotations({ + setup, + serviceName: 'foo', + environment: 'bar' + }), + { + mockResponse: () => noVersions + } + ); + + expect(mock.response).toEqual({ annotations: [] }); + }); + }); + + describe('with 1 version', () => { + it('returns no annotations', async () => { + mock = await inspectSearchParams( + setup => + getServiceAnnotations({ + setup, + serviceName: 'foo', + environment: 'bar' + }), + { + mockResponse: () => oneVersion + } + ); + + expect(mock.response).toEqual({ annotations: [] }); + }); + }); + + describe('with more than 1 version', () => { + it('returns two annotations', async () => { + const responses = [ + multipleVersions, + versionsFirstSeen, + versionsFirstSeen + ]; + mock = await inspectSearchParams( + setup => + getServiceAnnotations({ + setup, + serviceName: 'foo', + environment: 'bar' + }), + { + mockResponse: () => responses.shift() + } + ); + + expect(mock.spy.mock.calls.length).toBe(3); + + expect(mock.response).toEqual({ + annotations: [ + { + id: '8.0.0', + text: '8.0.0', + time: 1.5281138e12, + type: 'version' + }, + { + id: '7.5.0', + text: '7.5.0', + time: 1.5281138e12, + type: 'version' + } + ] + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/index.ts b/x-pack/legacy/plugins/apm/server/lib/services/annotations/index.ts new file mode 100644 index 0000000000000..c03746ca220ee --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/services/annotations/index.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { isNumber } from 'lodash'; +import { Annotation, AnnotationType } from '../../../../common/annotations'; +import { ESFilter } from '../../../../typings/elasticsearch'; +import { + SERVICE_NAME, + SERVICE_ENVIRONMENT, + PROCESSOR_EVENT +} from '../../../../common/elasticsearch_fieldnames'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; +import { rangeFilter } from '../../helpers/range_filter'; +import { SERVICE_VERSION } from '../../../../common/elasticsearch_fieldnames'; + +export async function getServiceAnnotations({ + setup, + serviceName, + environment +}: { + serviceName: string; + environment?: string; + setup: Setup & SetupTimeRange; +}) { + const { start, end, client, indices } = setup; + + const filter: ESFilter[] = [ + { term: { [PROCESSOR_EVENT]: 'transaction' } }, + { range: rangeFilter(start, end) }, + { term: { [SERVICE_NAME]: serviceName } } + ]; + + if (environment) { + filter.push({ term: { [SERVICE_ENVIRONMENT]: environment } }); + } + + const versions = + ( + await client.search({ + index: indices['apm_oss.transactionIndices'], + body: { + size: 0, + track_total_hits: false, + query: { + bool: { + filter + } + }, + aggs: { + versions: { + terms: { + field: SERVICE_VERSION + } + } + } + } + }) + ).aggregations?.versions.buckets.map(bucket => bucket.key) ?? []; + + if (versions.length > 1) { + const annotations = await Promise.all( + versions.map(async version => { + const response = await client.search({ + index: indices['apm_oss.transactionIndices'], + body: { + size: 0, + query: { + bool: { + filter: filter + .filter(esFilter => !Object.keys(esFilter).includes('range')) + .concat({ + term: { + [SERVICE_VERSION]: version + } + }) + } + }, + aggs: { + first_seen: { + min: { + field: '@timestamp' + } + } + }, + track_total_hits: false + } + }); + + const firstSeen = response.aggregations?.first_seen.value; + + if (!isNumber(firstSeen)) { + throw new Error( + 'First seen for version was unexpectedly undefined or null.' + ); + } + + if (firstSeen < start || firstSeen > end) { + return null; + } + + return { + type: AnnotationType.VERSION, + id: version, + time: firstSeen, + text: version + }; + }) + ); + return { annotations: annotations.filter(Boolean) as Annotation[] }; + } + return { annotations: [] }; +} diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts b/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts index e451f89af5620..e7b2330b472d8 100644 --- a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts +++ b/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts @@ -54,10 +54,6 @@ export function getApmIndicesConfig(config: APMConfig): ApmIndicesConfig { }; } -// export async function getApmIndices(context: APMRequestHandlerContext) { -// return _getApmIndices(context.core, context.config); -// } - export async function getApmIndices({ config, savedObjectsClient diff --git a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts b/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts index a0149bec728c5..6363b996ce5bb 100644 --- a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts +++ b/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts @@ -9,7 +9,8 @@ import { POD_NAME, SERVICE_AGENT_NAME, HOST_NAME, - TRANSACTION_RESULT + TRANSACTION_RESULT, + SERVICE_VERSION } from '../../../../common/elasticsearch_fieldnames'; const filtersByName = { @@ -42,6 +43,12 @@ const filtersByName = { defaultMessage: 'Transaction result' }), fieldName: TRANSACTION_RESULT + }, + serviceVersion: { + title: i18n.translate('xpack.apm.localFilters.titles.serviceVersion', { + defaultMessage: 'Service version' + }), + fieldName: SERVICE_VERSION } }; diff --git a/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts b/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts index 95488591d4b89..e98842151da84 100644 --- a/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts @@ -17,7 +17,8 @@ import { serviceAgentNameRoute, serviceTransactionTypesRoute, servicesRoute, - serviceNodeMetadataRoute + serviceNodeMetadataRoute, + serviceAnnotationsRoute } from './services'; import { agentConfigurationRoute, @@ -75,6 +76,7 @@ const createApmApi = () => { .add(serviceTransactionTypesRoute) .add(servicesRoute) .add(serviceNodeMetadataRoute) + .add(serviceAnnotationsRoute) // Agent configuration .add(agentConfigurationAgentNameRoute) diff --git a/x-pack/legacy/plugins/apm/server/routes/services.ts b/x-pack/legacy/plugins/apm/server/routes/services.ts index 91495bb96b032..78cb092b85db6 100644 --- a/x-pack/legacy/plugins/apm/server/routes/services.ts +++ b/x-pack/legacy/plugins/apm/server/routes/services.ts @@ -19,6 +19,7 @@ import { getServiceNodeMetadata } from '../lib/services/get_service_node_metadat import { createRoute } from './create_route'; import { uiFiltersRt, rangeRt } from './default_api_types'; import { getServiceMap } from '../lib/services/map'; +import { getServiceAnnotations } from '../lib/services/annotations'; export const servicesRoute = createRoute(() => ({ path: '/api/apm/services', @@ -98,3 +99,29 @@ export const serviceMapRoute = createRoute(() => ({ return new Boom('Not found', { statusCode: 404 }); } })); + +export const serviceAnnotationsRoute = createRoute(() => ({ + path: '/api/apm/services/{serviceName}/annotations', + params: { + path: t.type({ + serviceName: t.string + }), + query: t.intersection([ + rangeRt, + t.partial({ + environment: t.string + }) + ]) + }, + handler: async ({ context, request }) => { + const setup = await setupRequest(context, request); + const { serviceName } = context.params.path; + const { environment } = context.params.query; + + return getServiceAnnotations({ + setup, + serviceName, + environment + }); + } +})); diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/database/index.ts b/x-pack/legacy/plugins/apm/typings/apm-rum-react.d.ts similarity index 85% rename from x-pack/legacy/plugins/uptime/server/lib/adapters/database/index.ts rename to x-pack/legacy/plugins/apm/typings/apm-rum-react.d.ts index 4e09b5d0e9e2d..6f500caabd824 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/database/index.ts +++ b/x-pack/legacy/plugins/apm/typings/apm-rum-react.d.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './adapter_types'; +declare module '@elastic/apm-rum-react'; diff --git a/x-pack/legacy/plugins/infra/common/log_summary/index.ts b/x-pack/legacy/plugins/apm/typings/react-vis.d.ts similarity index 88% rename from x-pack/legacy/plugins/infra/common/log_summary/index.ts rename to x-pack/legacy/plugins/apm/typings/react-vis.d.ts index 3fb97a5cc20d0..aef8efc30d555 100644 --- a/x-pack/legacy/plugins/infra/common/log_summary/index.ts +++ b/x-pack/legacy/plugins/apm/typings/react-vis.d.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './log_summary'; +declare module 'react-vis'; diff --git a/x-pack/legacy/plugins/apm/typings/timeseries.ts b/x-pack/legacy/plugins/apm/typings/timeseries.ts index d64486d8e71e9..600be15ea229f 100644 --- a/x-pack/legacy/plugins/apm/typings/timeseries.ts +++ b/x-pack/legacy/plugins/apm/typings/timeseries.ts @@ -15,12 +15,14 @@ export interface RectCoordinate { x0: number; } -export interface TimeSeries { +export interface TimeSeries< + TCoordinate extends { x: number } = Coordinate | RectCoordinate +> { title: string; titleShort?: string; hideLegend?: boolean; hideTooltipValue?: boolean; - data: Array; + data: TCoordinate[]; legendValue?: string; type: string; color: string; diff --git a/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js b/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js index 61dbd07cfc568..662078585422f 100644 --- a/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js +++ b/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js @@ -83,9 +83,8 @@ module.exports = async ({ config }) => { loader: 'css-loader', options: { importLoaders: 2, - modules: { - localIdentName: '[name]__[local]___[hash:base64:5]', - }, + modules: true, + localIdentName: '[name]__[local]___[hash:base64:5]', }, }, { diff --git a/x-pack/legacy/plugins/canvas/shareable_runtime/webpack.config.js b/x-pack/legacy/plugins/canvas/shareable_runtime/webpack.config.js index dad5415f6768f..c711f9510a10b 100644 --- a/x-pack/legacy/plugins/canvas/shareable_runtime/webpack.config.js +++ b/x-pack/legacy/plugins/canvas/shareable_runtime/webpack.config.js @@ -99,11 +99,10 @@ module.exports = { { loader: 'css-loader', options: { - localsConvention: 'camelCaseOnly', + modules: true, + localIdentName: '[name]__[local]___[hash:base64:5]', + camelCase: true, sourceMap: !isProd, - modules: { - localIdentName: '[name]__[local]___[hash:base64:5]', - }, }, }, { diff --git a/x-pack/legacy/plugins/infra/common/graphql/types.ts b/x-pack/legacy/plugins/infra/common/graphql/types.ts index 0520409800bce..bd1d3945f35f7 100644 --- a/x-pack/legacy/plugins/infra/common/graphql/types.ts +++ b/x-pack/legacy/plugins/infra/common/graphql/types.ts @@ -34,10 +34,6 @@ export interface InfraSource { logEntriesBetween: InfraLogEntryInterval; /** Sequences of log entries matching sets of highlighting queries within an interval */ logEntryHighlights: InfraLogEntryInterval[]; - /** A consecutive span of summary buckets within an interval */ - logSummaryBetween: InfraLogSummaryInterval; - /** Spans of summary highlight buckets within an interval */ - logSummaryHighlightsBetween: InfraLogSummaryHighlightInterval[]; logItem: InfraLogItem; /** A snapshot of nodes */ @@ -208,50 +204,6 @@ export interface InfraLogEntryFieldColumn { /** A list of highlighted substrings of the value */ highlights: string[]; } -/** A consecutive sequence of log summary buckets */ -export interface InfraLogSummaryInterval { - /** The millisecond timestamp corresponding to the start of the interval covered by the summary */ - start?: number | null; - /** The millisecond timestamp corresponding to the end of the interval covered by the summary */ - end?: number | null; - /** The query the log entries were filtered by */ - filterQuery?: string | null; - /** A list of the log entries */ - buckets: InfraLogSummaryBucket[]; -} -/** A log summary bucket */ -export interface InfraLogSummaryBucket { - /** The start timestamp of the bucket */ - start: number; - /** The end timestamp of the bucket */ - end: number; - /** The number of entries inside the bucket */ - entriesCount: number; -} -/** A consecutive sequence of log summary highlight buckets */ -export interface InfraLogSummaryHighlightInterval { - /** The millisecond timestamp corresponding to the start of the interval covered by the summary */ - start?: number | null; - /** The millisecond timestamp corresponding to the end of the interval covered by the summary */ - end?: number | null; - /** The query the log entries were filtered by */ - filterQuery?: string | null; - /** The query the log entries were highlighted with */ - highlightQuery?: string | null; - /** A list of the log entries */ - buckets: InfraLogSummaryHighlightBucket[]; -} -/** A log summary highlight bucket */ -export interface InfraLogSummaryHighlightBucket { - /** The start timestamp of the bucket */ - start: number; - /** The end timestamp of the bucket */ - end: number; - /** The number of highlighted entries inside the bucket */ - entriesCount: number; - /** The time key of a representative of the highlighted log entries in this bucket */ - representativeKey: InfraTimeKey; -} export interface InfraLogItem { /** The ID of the document */ @@ -472,28 +424,6 @@ export interface LogEntryHighlightsInfraSourceArgs { /** The highlighting to apply to the log entries */ highlights: InfraLogEntryHighlightInput[]; } -export interface LogSummaryBetweenInfraSourceArgs { - /** The millisecond timestamp that corresponds to the start of the interval */ - start: number; - /** The millisecond timestamp that corresponds to the end of the interval */ - end: number; - /** The size of each bucket in milliseconds */ - bucketSize: number; - /** The query to filter the log entries by */ - filterQuery?: string | null; -} -export interface LogSummaryHighlightsBetweenInfraSourceArgs { - /** The millisecond timestamp that corresponds to the start of the interval */ - start: number; - /** The millisecond timestamp that corresponds to the end of the interval */ - end: number; - /** The size of each bucket in milliseconds */ - bucketSize: number; - /** The query to filter the log entries by */ - filterQuery?: string | null; - /** The highlighting to apply to the log entries */ - highlightQueries: string[]; -} export interface LogItemInfraSourceArgs { id: string; } @@ -577,7 +507,7 @@ export enum InfraSnapshotMetricType { rdsQueriesExecuted = 'rdsQueriesExecuted', rdsActiveTransactions = 'rdsActiveTransactions', rdsLatency = 'rdsLatency', - sqsMessagesVisible = 'sqsOldestMessage', + sqsMessagesVisible = 'sqsMessagesVisible', sqsMessagesDelayed = 'sqsMessagesDelayed', sqsMessagesSent = 'sqsMessagesSent', sqsMessagesEmpty = 'sqsMessagesEmpty', @@ -753,99 +683,6 @@ export namespace LogEntryHighlightsQuery { export type Entries = InfraLogEntryHighlightFields.Fragment; } -export namespace LogSummaryHighlightsQuery { - export type Variables = { - sourceId?: string | null; - start: number; - end: number; - bucketSize: number; - highlightQueries: string[]; - filterQuery?: string | null; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'InfraSource'; - - id: string; - - logSummaryHighlightsBetween: LogSummaryHighlightsBetween[]; - }; - - export type LogSummaryHighlightsBetween = { - __typename?: 'InfraLogSummaryHighlightInterval'; - - start?: number | null; - - end?: number | null; - - buckets: Buckets[]; - }; - - export type Buckets = { - __typename?: 'InfraLogSummaryHighlightBucket'; - - start: number; - - end: number; - - entriesCount: number; - - representativeKey: RepresentativeKey; - }; - - export type RepresentativeKey = InfraTimeKeyFields.Fragment; -} - -export namespace LogSummary { - export type Variables = { - sourceId?: string | null; - start: number; - end: number; - bucketSize: number; - filterQuery?: string | null; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'InfraSource'; - - id: string; - - logSummaryBetween: LogSummaryBetween; - }; - - export type LogSummaryBetween = { - __typename?: 'InfraLogSummaryInterval'; - - start?: number | null; - - end?: number | null; - - buckets: Buckets[]; - }; - - export type Buckets = { - __typename?: 'InfraLogSummaryBucket'; - - start: number; - - end: number; - - entriesCount: number; - }; -} - export namespace MetricsQuery { export type Variables = { sourceId: string; diff --git a/x-pack/legacy/plugins/infra/common/http_api/index.ts b/x-pack/legacy/plugins/infra/common/http_api/index.ts index 5278ae6c249c8..326daa93de33a 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/index.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/index.ts @@ -6,3 +6,5 @@ export * from './log_analysis'; export * from './metadata_api'; +export * from './log_entries'; +export * from './metrics_explorer'; diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/index.ts b/x-pack/legacy/plugins/infra/common/http_api/log_entries/index.ts new file mode 100644 index 0000000000000..ee2d150fdaac0 --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/http_api/log_entries/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; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './summary'; +export * from './summary_highlights'; diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary.ts b/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary.ts new file mode 100644 index 0000000000000..4a2c0db0e995e --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; + +export const LOG_ENTRIES_SUMMARY_PATH = '/api/log_entries/summary'; + +export const logEntriesSummaryRequestRT = rt.type({ + sourceId: rt.string, + startDate: rt.number, + endDate: rt.number, + bucketSize: rt.number, + query: rt.union([rt.string, rt.undefined, rt.null]), +}); + +export type LogEntriesSummaryRequest = rt.TypeOf; + +export const logEntriesSummaryBucketRT = rt.type({ + start: rt.number, + end: rt.number, + entriesCount: rt.number, +}); + +export type LogEntriesSummaryBucket = rt.TypeOf; + +export const logEntriesSummaryResponseRT = rt.type({ + data: rt.type({ + start: rt.number, + end: rt.number, + buckets: rt.array(logEntriesSummaryBucketRT), + }), +}); + +export type LogEntriesSummaryResponse = rt.TypeOf; diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary_highlights.ts b/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary_highlights.ts new file mode 100644 index 0000000000000..56191368dbcdf --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary_highlights.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; +import { logEntriesSummaryRequestRT, logEntriesSummaryBucketRT } from './summary'; + +export const LOG_ENTRIES_SUMMARY_HIGHLIGHTS_PATH = '/api/log_entries/summary_highlights'; + +export const logEntriesSummaryHighlightsRequestRT = rt.intersection([ + logEntriesSummaryRequestRT, + rt.type({ + highlightTerms: rt.array(rt.string), + }), +]); + +export type LogEntriesSummaryHighlightsRequest = rt.TypeOf< + typeof logEntriesSummaryHighlightsRequestRT +>; + +export const logEntriesSummaryHighlightsBucketRT = rt.intersection([ + logEntriesSummaryBucketRT, + rt.type({ + representativeKey: rt.type({ + time: rt.number, + tiebreaker: rt.number, + }), + }), +]); + +export type LogEntriesSummaryHighlightsBucket = rt.TypeOf< + typeof logEntriesSummaryHighlightsBucketRT +>; + +export const logEntriesSummaryHighlightsResponseRT = rt.type({ + data: rt.array( + rt.type({ + start: rt.number, + end: rt.number, + buckets: rt.array(logEntriesSummaryHighlightsBucketRT), + }) + ), +}); +export type LogEntriesSummaryHighlightsResponse = rt.TypeOf< + typeof logEntriesSummaryHighlightsResponseRT +>; diff --git a/x-pack/legacy/plugins/infra/common/http_api/metrics_explorer/index.ts b/x-pack/legacy/plugins/infra/common/http_api/metrics_explorer/index.ts new file mode 100644 index 0000000000000..c10f86c40ad46 --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/http_api/metrics_explorer/index.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; + +export const METRIC_EXPLORER_AGGREGATIONS = [ + 'avg', + 'max', + 'min', + 'cardinality', + 'rate', + 'count', +] as const; + +type MetricExplorerAggregations = typeof METRIC_EXPLORER_AGGREGATIONS[number]; + +const metricsExplorerAggregationKeys = METRIC_EXPLORER_AGGREGATIONS.reduce< + Record +>((acc, agg) => ({ ...acc, [agg]: null }), {} as Record); + +export const metricsExplorerAggregationRT = rt.keyof(metricsExplorerAggregationKeys); + +export const metricsExplorerMetricRequiredFieldsRT = rt.type({ + aggregation: metricsExplorerAggregationRT, +}); + +export const metricsExplorerMetricOptionalFieldsRT = rt.partial({ + field: rt.union([rt.string, rt.undefined]), +}); + +export const metricsExplorerMetricRT = rt.intersection([ + metricsExplorerMetricRequiredFieldsRT, + metricsExplorerMetricOptionalFieldsRT, +]); + +export const timeRangeRT = rt.type({ + field: rt.string, + from: rt.number, + to: rt.number, + interval: rt.string, +}); + +export const metricsExplorerRequestBodyRequiredFieldsRT = rt.type({ + timerange: timeRangeRT, + indexPattern: rt.string, + metrics: rt.array(metricsExplorerMetricRT), +}); + +export const metricsExplorerRequestBodyOptionalFieldsRT = rt.partial({ + groupBy: rt.union([rt.string, rt.null, rt.undefined]), + afterKey: rt.union([rt.string, rt.null, rt.undefined]), + limit: rt.union([rt.number, rt.null, rt.undefined]), + filterQuery: rt.union([rt.string, rt.null, rt.undefined]), +}); + +export const metricsExplorerRequestBodyRT = rt.intersection([ + metricsExplorerRequestBodyRequiredFieldsRT, + metricsExplorerRequestBodyOptionalFieldsRT, +]); + +export const metricsExplorerPageInfoRT = rt.type({ + total: rt.number, + afterKey: rt.union([rt.string, rt.null]), +}); + +export const metricsExplorerColumnTypeRT = rt.keyof({ + date: null, + number: null, + string: null, +}); + +export const metricsExplorerColumnRT = rt.type({ + name: rt.string, + type: metricsExplorerColumnTypeRT, +}); + +export const metricsExplorerRowRT = rt.intersection([ + rt.type({ + timestamp: rt.number, + }), + rt.record(rt.string, rt.union([rt.string, rt.number, rt.null, rt.undefined])), +]); + +export const metricsExplorerSeriesRT = rt.type({ + id: rt.string, + columns: rt.array(metricsExplorerColumnRT), + rows: rt.array(metricsExplorerRowRT), +}); + +export const metricsExplorerResponseRT = rt.type({ + series: rt.array(metricsExplorerSeriesRT), + pageInfo: metricsExplorerPageInfoRT, +}); diff --git a/x-pack/legacy/plugins/infra/common/http_api/snapshot_api.ts b/x-pack/legacy/plugins/infra/common/http_api/snapshot_api.ts index 3e6aec4bad972..0b5b023f63023 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/snapshot_api.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/snapshot_api.ts @@ -34,6 +34,7 @@ export const SnapshotNodeRT = rt.type({ export const SnapshotNodeResponseRT = rt.type({ nodes: rt.array(SnapshotNodeRT), + interval: rt.string, }); export const InfraTimerangeInputRT = rt.type({ diff --git a/x-pack/legacy/plugins/infra/common/log_analysis/job_parameters.ts b/x-pack/legacy/plugins/infra/common/log_analysis/job_parameters.ts index 626e90b65a7d8..8c08e24d8665d 100644 --- a/x-pack/legacy/plugins/infra/common/log_analysis/job_parameters.ts +++ b/x-pack/legacy/plugins/infra/common/log_analysis/job_parameters.ts @@ -8,6 +8,8 @@ import * as rt from 'io-ts'; export const bucketSpan = 900000; +export const categoriesMessageField = 'message'; + export const partitionField = 'event.dataset'; export const getJobIdPrefix = (spaceId: string, sourceId: string) => diff --git a/x-pack/legacy/plugins/infra/index.ts b/x-pack/legacy/plugins/infra/index.ts index 5466f2572a48e..38661f430c402 100644 --- a/x-pack/legacy/plugins/infra/index.ts +++ b/x-pack/legacy/plugins/infra/index.ts @@ -14,6 +14,8 @@ import { getConfigSchema } from './server/kibana.index'; import { savedObjectMappings } from './server/saved_objects'; import { plugin, InfraServerPluginDeps } from './server/new_platform_index'; import { InfraSetup } from '../../../plugins/infra/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../../plugins/features/server'; +import { SpacesPluginSetup } from '../../../plugins/spaces/server'; import { APMPluginContract } from '../../../plugins/apm/server'; const APP_ID = 'infra'; @@ -91,8 +93,8 @@ export function infra(kibana: any) { indexPatternsServiceFactory: legacyServer.indexPatternsServiceFactory, }, metrics: legacyServer.plugins.metrics, - spaces: plugins.spaces, - features: plugins.features, + spaces: plugins.spaces as SpacesPluginSetup, + features: plugins.features as FeaturesPluginSetup, // NP_NOTE: [TSVB_GROUP] Huge hack to make TSVB (getVisData()) work with raw requests that // originate from the New Platform router (and are very different to the old request object). // Once TSVB has migrated over to NP, and can work with the new raw requests, or ideally just @@ -113,7 +115,7 @@ export function infra(kibana: any) { const libs = infraPluginInstance.getLibs(); - // NP_TODO how do we replace this? Answer: return from setup function. + // NP_NOTE: Left here for now for legacy plugins to consume legacyServer.expose( 'defineInternalSourceConfiguration', libs.sources.defineInternalSourceConfiguration.bind(libs.sources) diff --git a/x-pack/legacy/plugins/infra/public/components/inventory/layout.tsx b/x-pack/legacy/plugins/infra/public/components/inventory/layout.tsx index d6827aa9c5d2d..60f2385477262 100644 --- a/x-pack/legacy/plugins/infra/public/components/inventory/layout.tsx +++ b/x-pack/legacy/plugins/infra/public/components/inventory/layout.tsx @@ -37,7 +37,7 @@ export interface LayoutProps { export const Layout = (props: LayoutProps) => { const { accounts, regions } = useInventoryMeta(props.sourceId, props.nodeType); - const { loading, nodes, reload } = useSnapshot( + const { loading, nodes, reload, interval } = useSnapshot( props.filterQuery, props.metric, props.groupBy, @@ -62,6 +62,7 @@ export const Layout = (props: LayoutProps) => { view={props.view} autoBounds={props.autoBounds} boundsOverride={props.boundsOverride} + interval={interval} /> diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/index.ts b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/index.ts new file mode 100644 index 0000000000000..c7a49a90a7886 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/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; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './setup_page'; + +export * from './initial_configuration_step'; +export * from './process_step'; + +export * from './ml_unavailable_prompt'; +export * from './setup_status_unknown_prompt'; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/analysis_setup_indices_form.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_indices_form.tsx similarity index 95% rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/analysis_setup_indices_form.tsx rename to x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_indices_form.tsx index 5a4c21670191e..3334e565f70f7 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/analysis_setup_indices_form.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_indices_form.tsx @@ -8,11 +8,9 @@ import { EuiCode, EuiDescribedFormGroup, EuiFormRow, EuiCheckbox, EuiToolTip } f import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useCallback, useMemo } from 'react'; -import { - ValidatedIndex, - ValidationIndicesUIError, -} from '../../../../../containers/logs/log_analysis/log_analysis_setup_state'; -import { LoadingOverlayWrapper } from '../../../../../components/loading_overlay_wrapper'; + +import { LoadingOverlayWrapper } from '../../../loading_overlay_wrapper'; +import { ValidatedIndex, ValidationIndicesUIError } from './validation'; export const AnalysisSetupIndicesForm: React.FunctionComponent<{ indices: ValidatedIndex[]; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/analysis_setup_timerange_form.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_timerange_form.tsx similarity index 98% rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/analysis_setup_timerange_form.tsx rename to x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_timerange_form.tsx index 02a25bf134fef..f45d274169497 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/analysis_setup_timerange_form.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_timerange_form.tsx @@ -17,7 +17,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import moment, { Moment } from 'moment'; import React, { useMemo } from 'react'; -import { euiStyled } from '../../../../../../../../common/eui_styled_components'; +import { euiStyled } from '../../../../../../../common/eui_styled_components'; const startTimeLabel = i18n.translate('xpack.infra.analysisSetup.startTimeLabel', { defaultMessage: 'Start time', diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/index.ts b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index.ts similarity index 90% rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/index.ts rename to x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index.ts index c44079f0e19df..e76bc2e34e240 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/index.ts +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index.ts @@ -5,3 +5,4 @@ */ export * from './initial_configuration_step'; +export * from './validation'; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/initial_configuration_step.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx similarity index 85% rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/initial_configuration_step.tsx rename to x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx index 3b5497fb91864..2494b802cdb5b 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/initial_configuration_step/initial_configuration_step.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx @@ -5,16 +5,14 @@ */ import { EuiSpacer, EuiForm, EuiCallOut } from '@elastic/eui'; +import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import { AnalysisSetupIndicesForm } from './analysis_setup_indices_form'; import { AnalysisSetupTimerangeForm } from './analysis_setup_timerange_form'; -import { - ValidatedIndex, - ValidationIndicesUIError, -} from '../../../../../containers/logs/log_analysis/log_analysis_setup_state'; +import { ValidatedIndex, ValidationIndicesUIError } from './validation'; interface InitialConfigurationStepProps { setStartTime: (startTime: number | undefined) => void; @@ -27,6 +25,13 @@ interface InitialConfigurationStepProps { validationErrors?: ValidationIndicesUIError[]; } +export const createInitialConfigurationStep = ( + props: InitialConfigurationStepProps +): EuiContainedStepProps => ({ + title: initialConfigurationStepTitle, + children: , +}); + export const InitialConfigurationStep: React.FunctionComponent = ({ setStartTime, setEndTime, @@ -67,6 +72,13 @@ const errorCalloutTitle = i18n.translate( } ); +const initialConfigurationStepTitle = i18n.translate( + 'xpack.infra.analysisSetup.configurationStepTitle', + { + defaultMessage: 'Configuration', + } +); + const ValidationErrors: React.FC<{ errors: ValidationIndicesUIError[] }> = ({ errors }) => { if (errors.length === 0) { return null; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/validation.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/validation.tsx new file mode 100644 index 0000000000000..8b733f66ef4a8 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/validation.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ValidationIndicesError } from '../../../../../common/http_api'; + +export type ValidationIndicesUIError = + | ValidationIndicesError + | { error: 'NETWORK_ERROR' } + | { error: 'TOO_FEW_SELECTED_INDICES' }; + +interface ValidIndex { + validity: 'valid'; + name: string; + isSelected: boolean; +} + +interface InvalidIndex { + validity: 'invalid'; + name: string; + errors: ValidationIndicesError[]; +} + +export type ValidatedIndex = ValidIndex | InvalidIndex; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_unavailable_content.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx similarity index 73% rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_unavailable_content.tsx rename to x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx index c77e5d408fe5f..63e1bb23a2d82 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_unavailable_content.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx @@ -4,26 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import { EuiButton, EuiEmptyPrompt, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiEmptyPrompt, EuiLink, EuiButton } from '@elastic/eui'; +import React from 'react'; + import euiStyled from '../../../../../../common/eui_styled_components'; -export const LogEntryRateUnavailableContent: React.FunctionComponent<{}> = () => ( +export const MlUnavailablePrompt: React.FunctionComponent<{}> = () => ( } body={

= () => } actions={ - {i18n.translate('xpack.infra.logs.analysisPage.unavailable.mlAppButton', { + {i18n.translate('xpack.infra.logs.analysis.mlAppButton', { defaultMessage: 'Open Machine Learning', })} diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/process_step/create_ml_jobs_button.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/create_ml_jobs_button.tsx similarity index 100% rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/process_step/create_ml_jobs_button.tsx rename to x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/create_ml_jobs_button.tsx diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/process_step/index.ts b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/index.ts similarity index 100% rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/process_step/index.ts rename to x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/index.ts diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/process_step/process_step.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/process_step.tsx similarity index 79% rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/process_step/process_step.tsx rename to x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/process_step.tsx index 978e45e26b733..a15c7335b4acb 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/process_step/process_step.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/process_step.tsx @@ -14,28 +14,42 @@ import { EuiCallOut, EuiCode, } from '@elastic/eui'; +import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { SetupStatus } from '../../../../../../common/log_analysis'; +import { SetupStatus } from '../../../../../common/log_analysis'; import { CreateMLJobsButton } from './create_ml_jobs_button'; import { RecreateMLJobsButton } from './recreate_ml_jobs_button'; interface ProcessStepProps { - cleanupAndSetup: () => void; + cleanUpAndSetUp: () => void; errorMessages: string[]; isConfigurationValid: boolean; - setup: () => void; + setUp: () => void; setupStatus: SetupStatus; viewResults: () => void; } +export const createProcessStep = (props: ProcessStepProps): EuiContainedStepProps => ({ + title: processStepTitle, + children: , + status: + props.setupStatus === 'pending' + ? 'incomplete' + : props.setupStatus === 'failed' + ? 'danger' + : props.setupStatus === 'succeeded' + ? 'complete' + : undefined, +}); + export const ProcessStep: React.FunctionComponent = ({ - cleanupAndSetup, + cleanUpAndSetUp, errorMessages, isConfigurationValid, - setup, + setUp, setupStatus, viewResults, }) => { @@ -66,7 +80,7 @@ export const ProcessStep: React.FunctionComponent = ({ ))} - + = ({ ) : setupStatus === 'requiredForUpdate' || setupStatus === 'requiredForReconfiguration' ? ( - + ) : ( - + )} ); @@ -102,3 +116,7 @@ const errorCalloutTitle = i18n.translate( defaultMessage: 'An error occurred', } ); + +const processStepTitle = i18n.translate('xpack.infra.analysisSetup.actionStepTitle', { + defaultMessage: 'Create ML job', +}); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/process_step/recreate_ml_jobs_button.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/recreate_ml_jobs_button.tsx similarity index 100% rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/process_step/recreate_ml_jobs_button.tsx rename to x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/recreate_ml_jobs_button.tsx diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx new file mode 100644 index 0000000000000..a1c35aff0cf83 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + CommonProps, + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageContentHeader, + EuiPageContentHeaderSection, + EuiTitle, +} from '@elastic/eui'; +import React from 'react'; + +import euiStyled from '../../../../../../common/eui_styled_components'; + +export const LogAnalysisSetupPage: React.FunctionComponent = ({ + children, + ...rest +}) => { + return ( + + + + {children} + + + + ); +}; + +export const LogAnalysisSetupPageHeader: React.FunctionComponent = ({ children }) => ( + + + +

{children}

+ + + +); + +export const LogAnalysisSetupPageContent = EuiPageContentBody; + +// !important due to https://github.com/elastic/eui/issues/2232 +const LogEntryRateSetupPageContent = euiStyled(EuiPageContent)` + max-width: 768px !important; +`; + +const LogEntryRateSetupPage = euiStyled(EuiPage)` + height: 100%; +`; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_status_unknown.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx similarity index 79% rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_status_unknown.tsx rename to x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx index 4c685bd42b937..f65ff6a1eec78 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_status_unknown.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx @@ -14,21 +14,21 @@ interface Props { retry: () => void; } -export const LogEntryRateSetupStatusUnknownContent: React.FunctionComponent = ({ +export const LogAnalysisSetupStatusUnknownPrompt: React.FunctionComponent = ({ retry, }: Props) => ( } actions={ retry()} color="primary" fill> - {i18n.translate('xpack.infra.logs.analysisPage.setupStatusUnknown.tryAgainButton', { + {i18n.translate('xpack.infra.logs.analysis.setupStatusTryAgainButton', { defaultMessage: 'Try again', })} diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/aggregation.tsx b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/aggregation.tsx index d498ba1229b44..3550cfc9e03ee 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/aggregation.tsx +++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/aggregation.tsx @@ -10,6 +10,10 @@ import { i18n } from '@kbn/i18n'; import React, { useCallback } from 'react'; import { MetricsExplorerAggregation } from '../../../server/routes/metrics_explorer/types'; import { MetricsExplorerOptions } from '../../containers/metrics_explorer/use_metrics_explorer_options'; +import { + metricsExplorerAggregationRT, + METRIC_EXPLORER_AGGREGATIONS, +} from '../../../common/http_api/metrics_explorer'; interface Props { options: MetricsExplorerOptions; @@ -17,43 +21,32 @@ interface Props { onChange: (aggregation: MetricsExplorerAggregation) => void; } -const isMetricsExplorerAggregation = (subject: any): subject is MetricsExplorerAggregation => { - return Object.keys(MetricsExplorerAggregation).includes(subject); -}; - export const MetricsExplorerAggregationPicker = ({ options, onChange }: Props) => { const AGGREGATION_LABELS = { - [MetricsExplorerAggregation.avg]: i18n.translate( - 'xpack.infra.metricsExplorer.aggregationLables.avg', - { defaultMessage: 'Average' } - ), - [MetricsExplorerAggregation.max]: i18n.translate( - 'xpack.infra.metricsExplorer.aggregationLables.max', - { defaultMessage: 'Max' } - ), - [MetricsExplorerAggregation.min]: i18n.translate( - 'xpack.infra.metricsExplorer.aggregationLables.min', - { defaultMessage: 'Min' } - ), - [MetricsExplorerAggregation.cardinality]: i18n.translate( - 'xpack.infra.metricsExplorer.aggregationLables.cardinality', - { defaultMessage: 'Cardinality' } - ), - [MetricsExplorerAggregation.rate]: i18n.translate( - 'xpack.infra.metricsExplorer.aggregationLables.rate', - { defaultMessage: 'Rate' } - ), - [MetricsExplorerAggregation.count]: i18n.translate( - 'xpack.infra.metricsExplorer.aggregationLables.count', - { defaultMessage: 'Document count' } - ), + ['avg']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.avg', { + defaultMessage: 'Average', + }), + ['max']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.max', { + defaultMessage: 'Max', + }), + ['min']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.min', { + defaultMessage: 'Min', + }), + ['cardinality']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.cardinality', { + defaultMessage: 'Cardinality', + }), + ['rate']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.rate', { + defaultMessage: 'Rate', + }), + ['count']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.count', { + defaultMessage: 'Document count', + }), }; const handleChange = useCallback( e => { const aggregation = - (isMetricsExplorerAggregation(e.target.value) && e.target.value) || - MetricsExplorerAggregation.avg; + (metricsExplorerAggregationRT.is(e.target.value) && e.target.value) || 'avg'; onChange(aggregation); }, [onChange] @@ -66,7 +59,7 @@ export const MetricsExplorerAggregationPicker = ({ options, onChange }: Props) = })} fullWidth value={options.aggregation} - options={Object.keys(MetricsExplorerAggregation).map(k => ({ + options={METRIC_EXPLORER_AGGREGATIONS.map(k => ({ text: AGGREGATION_LABELS[k as MetricsExplorerAggregation], value: k, }))} diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts index c335b8b3c31ac..e85cd67060d2c 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts +++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts @@ -5,21 +5,17 @@ */ import { calculateDomain } from './calculate_domain'; -import { - MetricsExplorerSeries, - MetricsExplorerAggregation, - MetricsExplorerColumnType, -} from '../../../../server/routes/metrics_explorer/types'; +import { MetricsExplorerSeries } from '../../../../server/routes/metrics_explorer/types'; import { MetricsExplorerOptionsMetric } from '../../../containers/metrics_explorer/use_metrics_explorer_options'; import { MetricsExplorerColor } from '../../../../common/color_palette'; describe('calculateDomain()', () => { const series: MetricsExplorerSeries = { id: 'test-01', columns: [ - { type: MetricsExplorerColumnType.date, name: 'timestamp' }, - { type: MetricsExplorerColumnType.number, name: 'metric_0' }, - { type: MetricsExplorerColumnType.number, name: 'metric_1' }, - { type: MetricsExplorerColumnType.string, name: 'groupBy' }, + { type: 'date', name: 'timestamp' }, + { type: 'number', name: 'metric_0' }, + { type: 'number', name: 'metric_1' }, + { type: 'string', name: 'groupBy' }, ], rows: [ { timestamp: 1562860500000, metric_0: null, metric_1: null }, @@ -31,12 +27,12 @@ describe('calculateDomain()', () => { }; const metrics: MetricsExplorerOptionsMetric[] = [ { - aggregation: MetricsExplorerAggregation.avg, + aggregation: 'avg', field: 'system.memory.free', color: MetricsExplorerColor.color0, }, { - aggregation: MetricsExplorerAggregation.avg, + aggregation: 'avg', field: 'system.memory.used.bytes', color: MetricsExplorerColor.color1, }, diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts index 3451d901d4516..1e9902337e032 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts +++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts @@ -4,20 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - MetricsExplorerAggregation, - MetricsExplorerMetric, -} from '../../../../server/routes/metrics_explorer/types'; +import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types'; import { createFormatter } from '../../../utils/formatters'; import { InfraFormatterType } from '../../../lib/lib'; import { metricToFormat } from './metric_to_format'; export const createFormatterForMetric = (metric?: MetricsExplorerMetric) => { if (metric && metric.field) { const format = metricToFormat(metric); - if ( - format === InfraFormatterType.bits && - metric.aggregation === MetricsExplorerAggregation.rate - ) { + if (format === InfraFormatterType.bits && metric.aggregation === 'rate') { return createFormatter(InfraFormatterType.bits, '{{value}}/s'); } return createFormatter(format); diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts index 12058f37aeb1a..592dd70345bfa 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts +++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts @@ -5,34 +5,35 @@ */ import { createFormatterForMetric } from './create_formatter_for_metric'; -import { MetricsExplorerAggregation } from '../../../../server/routes/metrics_explorer/types'; +import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types'; + describe('createFormatterForMetric()', () => { it('should just work for count', () => { - const metric = { aggregation: MetricsExplorerAggregation.count }; + const metric: MetricsExplorerMetric = { aggregation: 'count' }; const format = createFormatterForMetric(metric); expect(format(1291929)).toBe('1,291,929'); }); it('should just work for numerics', () => { - const metric = { aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' }; + const metric: MetricsExplorerMetric = { aggregation: 'avg', field: 'system.load.1' }; const format = createFormatterForMetric(metric); expect(format(1000.2)).toBe('1,000.2'); }); it('should just work for percents', () => { - const metric = { aggregation: MetricsExplorerAggregation.avg, field: 'system.cpu.total.pct' }; + const metric: MetricsExplorerMetric = { aggregation: 'avg', field: 'system.cpu.total.pct' }; const format = createFormatterForMetric(metric); expect(format(0.349)).toBe('34.9%'); }); it('should just work for rates', () => { - const metric = { - aggregation: MetricsExplorerAggregation.rate, + const metric: MetricsExplorerMetric = { + aggregation: 'rate', field: 'system.network.out.bytes', }; const format = createFormatterForMetric(metric); expect(format(103929292)).toBe('831.4Mbit/s'); }); it('should just work for bytes', () => { - const metric = { - aggregation: MetricsExplorerAggregation.avg, + const metric: MetricsExplorerMetric = { + aggregation: 'avg', field: 'system.network.out.bytes', }; const format = createFormatterForMetric(metric); diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts index b148d68c71dba..0c52d8d56a6e9 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts +++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts @@ -5,15 +5,15 @@ */ import { createMetricLabel } from './create_metric_label'; -import { MetricsExplorerAggregation } from '../../../../server/routes/metrics_explorer/types'; +import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types'; describe('createMetricLabel()', () => { it('should work with metrics with fields', () => { - const metric = { aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' }; + const metric: MetricsExplorerMetric = { aggregation: 'avg', field: 'system.load.1' }; expect(createMetricLabel(metric)).toBe('avg(system.load.1)'); }); it('should work with document count', () => { - const metric = { aggregation: MetricsExplorerAggregation.count }; + const metric: MetricsExplorerMetric = { aggregation: 'count' }; expect(createMetricLabel(metric)).toBe('count()'); }); }); diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts index d190d09da992e..860c4f1ba406c 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts +++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts @@ -8,11 +8,11 @@ import { createTSVBLink, createFilterFromOptions } from './create_tsvb_link'; import { source, options, timeRange, chartOptions } from '../../../utils/fixtures/metrics_explorer'; import uuid from 'uuid'; import { OutputBuffer } from 'uuid/interfaces'; -import { MetricsExplorerAggregation } from '../../../../server/routes/metrics_explorer/types'; import { MetricsExplorerYAxisMode, MetricsExplorerChartType, } from '../../../containers/metrics_explorer/use_metrics_explorer_options'; +import { MetricsExplorerOptions } from '../../../containers/metrics_explorer/use_metrics_explorer_options'; jest.mock('uuid'); const mockedUuid = uuid as jest.Mocked; @@ -28,11 +28,9 @@ describe('createTSVBLink()', () => { }); it('should work with rates', () => { - const customOptions = { + const customOptions: MetricsExplorerOptions = { ...options, - metrics: [ - { aggregation: MetricsExplorerAggregation.rate, field: 'system.network.out.bytes' }, - ], + metrics: [{ aggregation: 'rate', field: 'system.network.out.bytes' }], }; const link = createTSVBLink(source, customOptions, series, timeRange, chartOptions); expect(link).toBe( diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts index 788de6a129aa9..2fe81123594e9 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts +++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts @@ -8,10 +8,7 @@ import { encode } from 'rison-node'; import uuid from 'uuid'; import { set } from 'lodash'; import { colorTransformer, MetricsExplorerColor } from '../../../../common/color_palette'; -import { - MetricsExplorerSeries, - MetricsExplorerAggregation, -} from '../../../../server/routes/metrics_explorer/types'; +import { MetricsExplorerSeries } from '../../../../server/routes/metrics_explorer/types'; import { MetricsExplorerOptions, MetricsExplorerOptionsMetric, @@ -26,7 +23,7 @@ import { SourceQuery } from '../../../graphql/types'; import { createMetricLabel } from './create_metric_label'; export const metricsExplorerMetricToTSVBMetric = (metric: MetricsExplorerOptionsMetric) => { - if (metric.aggregation === MetricsExplorerAggregation.rate) { + if (metric.aggregation === 'rate') { const metricId = uuid.v1(); const positiveOnlyId = uuid.v1(); const derivativeId = uuid.v1(); @@ -73,8 +70,7 @@ const mapMetricToSeries = (chartOptions: MetricsExplorerChartOptions) => ( ), fill: chartOptions.type === MetricsExplorerChartType.area ? 0.5 : 0, formatter: format === InfraFormatterType.bits ? InfraFormatterType.bytes : format, - value_template: - MetricsExplorerAggregation.rate === metric.aggregation ? '{{value}}/s' : '{{value}}', + value_template: 'rate' === metric.aggregation ? '{{value}}/s' : '{{value}}', id: uuid.v1(), line_width: 2, metrics: metricsExplorerMetricToTSVBMetric(metric), diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts index 9812023174a8c..a38a9ac54d828 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts +++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts @@ -5,37 +5,37 @@ */ import { metricToFormat } from './metric_to_format'; -import { MetricsExplorerAggregation } from '../../../../server/routes/metrics_explorer/types'; +import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types'; import { InfraFormatterType } from '../../../lib/lib'; describe('metricToFormat()', () => { it('should just work for numeric metrics', () => { - const metric = { aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' }; + const metric: MetricsExplorerMetric = { aggregation: 'avg', field: 'system.load.1' }; expect(metricToFormat(metric)).toBe(InfraFormatterType.number); }); it('should just work for byte metrics', () => { - const metric = { - aggregation: MetricsExplorerAggregation.avg, + const metric: MetricsExplorerMetric = { + aggregation: 'avg', field: 'system.network.out.bytes', }; expect(metricToFormat(metric)).toBe(InfraFormatterType.bytes); }); it('should just work for rate bytes metrics', () => { - const metric = { - aggregation: MetricsExplorerAggregation.rate, + const metric: MetricsExplorerMetric = { + aggregation: 'rate', field: 'system.network.out.bytes', }; expect(metricToFormat(metric)).toBe(InfraFormatterType.bits); }); it('should just work for rate metrics', () => { - const metric = { - aggregation: MetricsExplorerAggregation.rate, + const metric: MetricsExplorerMetric = { + aggregation: 'rate', field: 'system.cpu.user.ticks', }; expect(metricToFormat(metric)).toBe(InfraFormatterType.number); }); it('should just work for percent metrics', () => { - const metric = { - aggregation: MetricsExplorerAggregation.avg, + const metric: MetricsExplorerMetric = { + aggregation: 'avg', field: 'system.cpu.user.pct', }; expect(metricToFormat(metric)).toBe(InfraFormatterType.percent); diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts index de6214fda2df3..69f2bd50ef42f 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts +++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts @@ -5,10 +5,7 @@ */ import { last } from 'lodash'; -import { - MetricsExplorerAggregation, - MetricsExplorerMetric, -} from '../../../../server/routes/metrics_explorer/types'; +import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types'; import { InfraFormatterType } from '../../../lib/lib'; export const metricToFormat = (metric?: MetricsExplorerMetric) => { if (metric && metric.field) { @@ -16,7 +13,7 @@ export const metricToFormat = (metric?: MetricsExplorerMetric) => { if (suffix === 'pct') { return InfraFormatterType.percent; } - if (suffix === 'bytes' && metric.aggregation === MetricsExplorerAggregation.rate) { + if (suffix === 'bytes' && metric.aggregation === 'rate') { return InfraFormatterType.bits; } if (suffix === 'bytes') { diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/metrics.tsx b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/metrics.tsx index 42df7c6915a0d..31d301e2164f8 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/metrics.tsx +++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/metrics.tsx @@ -10,10 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useCallback, useState } from 'react'; import { FieldType } from 'ui/index_patterns'; import { colorTransformer, MetricsExplorerColor } from '../../../common/color_palette'; -import { - MetricsExplorerMetric, - MetricsExplorerAggregation, -} from '../../../server/routes/metrics_explorer/types'; +import { MetricsExplorerMetric } from '../../../server/routes/metrics_explorer/types'; import { MetricsExplorerOptions } from '../../containers/metrics_explorer/use_metrics_explorer_options'; import { isDisplayable } from '../../utils/is_displayable'; @@ -61,7 +58,7 @@ export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus = .filter(field => isDisplayable(field)) .map(field => ({ label: field.name, value: field.name })); const selectedOptions = options.metrics - .filter(m => m.aggregation !== MetricsExplorerAggregation.count) + .filter(m => m.aggregation !== 'count') .map(metric => ({ label: metric.field || '', value: metric.field || '', @@ -74,7 +71,7 @@ export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus = return ( { - const isDefaultOptions = - options.aggregation === MetricsExplorerAggregation.avg && options.metrics.length === 0; + const isDefaultOptions = options.aggregation === 'avg' && options.metrics.length === 0; return ( - + - {options.aggregation !== MetricsExplorerAggregation.count && ( + {options.aggregation !== 'count' && ( )} - {options.aggregation !== MetricsExplorerAggregation.count && ( + {options.aggregation !== 'count' && ( { view, currentTime, options, + interval, } = this.props; if (loading) { return ( @@ -153,6 +156,7 @@ export const NodesOverview = class extends React.Component { } const dataBounds = calculateBoundsFromNodes(nodes); const bounds = autoBounds ? dataBounds : boundsOverride; + const intervalAsString = convertIntervalToString(interval); return ( @@ -165,7 +169,8 @@ export const NodesOverview = class extends React.Component {

diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts index 91e517b0db008..2e1ba52353bef 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts @@ -49,6 +49,7 @@ const datafeedStateRT = rt.keyof({ const jobStateRT = rt.keyof({ closed: null, closing: null, + deleting: null, failed: null, opened: null, opening: null, diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/index.ts index eb044c86e50fe..185f6936662bd 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/index.ts @@ -9,3 +9,4 @@ export * from './log_analysis_cleanup'; export * from './log_analysis_module'; export * from './log_analysis_module_status'; export * from './log_analysis_module_types'; +export * from './log_analysis_setup_state'; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx index 74dbb3c7a8062..9f966ed3342e6 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx @@ -6,8 +6,11 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; -import { ValidationIndicesError } from '../../../../common/http_api'; import { isExampleDataIndex } from '../../../../common/log_analysis'; +import { + ValidatedIndex, + ValidationIndicesUIError, +} from '../../../components/logging/log_analysis_setup/initial_configuration_step'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { ModuleDescriptor, ModuleSourceConfiguration } from './log_analysis_module_types'; @@ -17,38 +20,19 @@ type SetupHandler = ( endTime: number | undefined ) => void; -export type ValidationIndicesUIError = - | ValidationIndicesError - | { error: 'NETWORK_ERROR' } - | { error: 'TOO_FEW_SELECTED_INDICES' }; - -interface ValidIndex { - validity: 'valid'; - name: string; - isSelected: boolean; -} - -interface InvalidIndex { - validity: 'invalid'; - name: string; - errors: ValidationIndicesError[]; -} - -export type ValidatedIndex = ValidIndex | InvalidIndex; - interface AnalysisSetupStateArguments { - cleanupAndSetupModule: SetupHandler; + cleanUpAndSetUpModule: SetupHandler; moduleDescriptor: ModuleDescriptor; - setupModule: SetupHandler; + setUpModule: SetupHandler; sourceConfiguration: ModuleSourceConfiguration; } const fourWeeksInMs = 86400000 * 7 * 4; export const useAnalysisSetupState = ({ - cleanupAndSetupModule, + cleanUpAndSetUpModule, moduleDescriptor: { validateSetupIndices }, - setupModule, + setUpModule, sourceConfiguration, }: AnalysisSetupStateArguments) => { const [startTime, setStartTime] = useState(Date.now() - fourWeeksInMs); @@ -107,13 +91,13 @@ export const useAnalysisSetupState = ({ [validatedIndices] ); - const setup = useCallback(() => { - return setupModule(selectedIndexNames, startTime, endTime); - }, [setupModule, selectedIndexNames, startTime, endTime]); + const setUp = useCallback(() => { + return setUpModule(selectedIndexNames, startTime, endTime); + }, [setUpModule, selectedIndexNames, startTime, endTime]); - const cleanupAndSetup = useCallback(() => { - return cleanupAndSetupModule(selectedIndexNames, startTime, endTime); - }, [cleanupAndSetupModule, selectedIndexNames, startTime, endTime]); + const cleanUpAndSetUp = useCallback(() => { + return cleanUpAndSetUpModule(selectedIndexNames, startTime, endTime); + }, [cleanUpAndSetUpModule, selectedIndexNames, startTime, endTime]); const isValidating = useMemo( () => @@ -143,13 +127,13 @@ export const useAnalysisSetupState = ({ }, [isValidating, validateIndicesRequest.state, selectedIndexNames, validatedIndices]); return { - cleanupAndSetup, + cleanUpAndSetUp, endTime, isValidating, selectedIndexNames, setEndTime, setStartTime, - setup, + setUp, startTime, validatedIndices, setValidatedIndices, diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts new file mode 100644 index 0000000000000..781e8401cf881 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { identity } from 'fp-ts/lib/function'; +import { kfetch } from 'ui/kfetch'; + +import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; + +import { + LOG_ENTRIES_SUMMARY_HIGHLIGHTS_PATH, + LogEntriesSummaryHighlightsRequest, + logEntriesSummaryHighlightsRequestRT, + logEntriesSummaryHighlightsResponseRT, +} from '../../../../../common/http_api'; + +export const fetchLogSummaryHighlights = async ( + requestArgs: LogEntriesSummaryHighlightsRequest +) => { + const response = await kfetch({ + method: 'POST', + pathname: LOG_ENTRIES_SUMMARY_HIGHLIGHTS_PATH, + body: JSON.stringify(logEntriesSummaryHighlightsRequestRT.encode(requestArgs)), + }); + + return pipe( + logEntriesSummaryHighlightsResponseRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.gql_query.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.gql_query.ts deleted file mode 100644 index 4d2c4075b50e7..0000000000000 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.gql_query.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -import { sharedFragments } from '../../../../common/graphql/shared'; - -export const logSummaryHighlightsQuery = gql` - query LogSummaryHighlightsQuery( - $sourceId: ID = "default" - $start: Float! - $end: Float! - $bucketSize: Float! - $highlightQueries: [String!]! - $filterQuery: String - ) { - source(id: $sourceId) { - id - logSummaryHighlightsBetween( - start: $start - end: $end - bucketSize: $bucketSize - highlightQueries: $highlightQueries - filterQuery: $filterQuery - ) { - start - end - buckets { - start - end - entriesCount - representativeKey { - ...InfraTimeKeyFields - } - } - } - } - } - - ${sharedFragments.InfraTimeKey} -`; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts index 874c70e016496..81639aba411ef 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts @@ -7,12 +7,9 @@ import { useEffect, useMemo, useState } from 'react'; import { debounce } from 'lodash'; -import { LogSummaryHighlightsQuery } from '../../../graphql/types'; -import { DependencyError, useApolloClient } from '../../../utils/apollo_context'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; -import { logSummaryHighlightsQuery } from './log_summary_highlights.gql_query'; - -export type LogSummaryHighlights = LogSummaryHighlightsQuery.Query['source']['logSummaryHighlightsBetween']; +import { fetchLogSummaryHighlights } from './api/fetch_log_summary_highlights'; +import { LogEntriesSummaryHighlightsResponse } from '../../../../common/http_api'; export const useLogSummaryHighlights = ( sourceId: string, @@ -23,41 +20,32 @@ export const useLogSummaryHighlights = ( filterQuery: string | null, highlightTerms: string[] ) => { - const apolloClient = useApolloClient(); - const [logSummaryHighlights, setLogSummaryHighlights] = useState([]); + const [logSummaryHighlights, setLogSummaryHighlights] = useState< + LogEntriesSummaryHighlightsResponse['data'] + >([]); const [loadLogSummaryHighlightsRequest, loadLogSummaryHighlights] = useTrackedPromise( { cancelPreviousOn: 'resolution', createPromise: async () => { - if (!apolloClient) { - throw new DependencyError('Failed to load source: No apollo client available.'); - } if (!start || !end || !highlightTerms.length) { throw new Error('Skipping request: Insufficient parameters'); } - return await apolloClient.query< - LogSummaryHighlightsQuery.Query, - LogSummaryHighlightsQuery.Variables - >({ - fetchPolicy: 'no-cache', - query: logSummaryHighlightsQuery, - variables: { - sourceId, - start, - end, - bucketSize, - highlightQueries: [highlightTerms[0]], - filterQuery, - }, + return await fetchLogSummaryHighlights({ + sourceId, + startDate: start, + endDate: end, + bucketSize, + query: filterQuery, + highlightTerms, }); }, onResolve: response => { - setLogSummaryHighlights(response.data.source.logSummaryHighlightsBetween); + setLogSummaryHighlights(response.data); }, }, - [apolloClient, sourceId, start, end, bucketSize, filterQuery, highlightTerms] + [sourceId, start, end, bucketSize, filterQuery, highlightTerms] ); const debouncedLoadSummaryHighlights = useMemo(() => debounce(loadLogSummaryHighlights, 275), [ diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts new file mode 100644 index 0000000000000..5dabb8ebf0179 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { identity } from 'fp-ts/lib/function'; +import { kfetch } from 'ui/kfetch'; + +import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; + +import { + LOG_ENTRIES_SUMMARY_PATH, + LogEntriesSummaryRequest, + logEntriesSummaryRequestRT, + logEntriesSummaryResponseRT, +} from '../../../../../common/http_api'; + +export const fetchLogSummary = async (requestArgs: LogEntriesSummaryRequest) => { + const response = await kfetch({ + method: 'POST', + pathname: LOG_ENTRIES_SUMMARY_PATH, + body: JSON.stringify(logEntriesSummaryRequestRT.encode(requestArgs)), + }); + + return pipe( + logEntriesSummaryResponseRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.gql_query.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.gql_query.ts deleted file mode 100644 index a8bd808e51947..0000000000000 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.gql_query.ts +++ /dev/null @@ -1,35 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const logSummaryQuery = gql` - query LogSummary( - $sourceId: ID = "default" - $start: Float! - $end: Float! - $bucketSize: Float! - $filterQuery: String - ) { - source(id: $sourceId) { - id - logSummaryBetween( - start: $start - end: $end - bucketSize: $bucketSize - filterQuery: $filterQuery - ) { - start - end - buckets { - start - end - entriesCount - } - } - } - } -`; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx index b65bf011422d2..2bbcc22b150e4 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx @@ -4,271 +4,170 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { mountHook } from 'test_utils/enzyme_helpers'; +import { renderHook } from '@testing-library/react-hooks'; -import { ApolloClientContext } from '../../../utils/apollo_context'; import { useLogSummary } from './log_summary'; -describe('useLogSummary hook', () => { - it('provides an empty list of buckets by default', () => { - const mockApolloClient = { - query: jest.fn(), - }; +import { fetchLogSummary } from './api/fetch_log_summary'; - const { getLastHookValue } = mountHook( - () => useLogSummary('SOURCE_ID', null, 1000, null), - createMockApolloProvider(mockApolloClient) - ); +// Typescript doesn't know that `fetchLogSummary` is a jest mock. +// We use a second variable with a type cast to help the compiler further down the line. +jest.mock('./api/fetch_log_summary', () => ({ fetchLogSummary: jest.fn() })); +const fetchLogSummaryMock = fetchLogSummary as jest.MockedFunction; - expect(getLastHookValue().buckets).toEqual([]); +describe('useLogSummary hook', () => { + beforeEach(() => { + fetchLogSummaryMock.mockClear(); }); - /** - * This is skipped until `act` can deal with async operations, see comment - * below. - * - * The test cases below this are a temporary alternative until the - * shortcomings of the `act` function have been overcome. - */ - it.skip('queries for new summary buckets when the source id changes', async () => { - const firstMockResponse = createMockResponse([{ start: 99000, end: 101000, entriesCount: 1 }]); - const secondMockResponse = createMockResponse([{ start: 99000, end: 101000, entriesCount: 2 }]); - const mockApolloClient = { - query: jest - .fn() - .mockResolvedValueOnce(firstMockResponse) - .mockResolvedValueOnce(secondMockResponse), - }; - - const { act, getLastHookValue } = mountHook( - ({ sourceId }) => useLogSummary(sourceId, 100000, 1000, null), - createMockApolloProvider(mockApolloClient), - { sourceId: 'INITIAL_SOURCE_ID' } - ); - - expect(mockApolloClient.query).toHaveBeenCalledTimes(1); - expect(mockApolloClient.query).toHaveBeenLastCalledWith( - expect.objectContaining({ - variables: expect.objectContaining({ - sourceId: 'INITIAL_SOURCE_ID', - }), - }) - ); - expect(getLastHookValue().buckets).toEqual( - firstMockResponse.data.source.logSummaryBetween.buckets - ); - - // DOESN'T WORK YET until https://github.com/facebook/react/pull/14853 has been merged - await act(async (_, setArgs) => { - setArgs({ sourceId: 'CHANGED_SOURCE_ID' }); - - // wait for the promise queue to be processed - await mockApolloClient.query(); - }); - - expect(mockApolloClient.query).toHaveBeenCalledTimes(2); - expect(mockApolloClient.query).toHaveBeenLastCalledWith( - expect.objectContaining({ - variables: expect.objectContaining({ - sourceId: 'CHANGED_SOURCE_ID', - }), - }) - ); - expect(getLastHookValue().buckets).toEqual( - secondMockResponse.data.source.logSummaryBetween.buckets - ); + it('provides an empty list of buckets by default', () => { + const { result } = renderHook(() => useLogSummary('SOURCE_ID', null, 1000, null)); + expect(result.current.buckets).toEqual([]); }); - /** - * The following test cases use a bad workaround to avoid the problems - * exhibited by the skipped test case above. Instead of a real Promise we - * fake a synchronously resolving promise-like return value to avoid any - * async behavior. - * - * They should be rewritten to the cleaner async/await style shown in the - * test case above once `act` is capable of dealing with it. - */ - - it('queries for new summary buckets when the source id changes - workaround', () => { + it('queries for new summary buckets when the source id changes', async () => { const firstMockResponse = createMockResponse([{ start: 99000, end: 101000, entriesCount: 1 }]); const secondMockResponse = createMockResponse([{ start: 99000, end: 101000, entriesCount: 2 }]); - const mockApolloClient = { - query: jest - .fn() - .mockReturnValueOnce(createSyncMockPromise(firstMockResponse)) - .mockReturnValueOnce(createSyncMockPromise(secondMockResponse)), - }; - const { act, getLastHookValue } = mountHook( + fetchLogSummaryMock + .mockResolvedValueOnce(firstMockResponse) + .mockResolvedValueOnce(secondMockResponse); + + const { result, waitForNextUpdate, rerender } = renderHook( ({ sourceId }) => useLogSummary(sourceId, 100000, 1000, null), - createMockApolloProvider(mockApolloClient), - { sourceId: 'INITIAL_SOURCE_ID' } + { + initialProps: { sourceId: 'INITIAL_SOURCE_ID' }, + } ); - expect(mockApolloClient.query).toHaveBeenCalledTimes(1); - expect(mockApolloClient.query).toHaveBeenLastCalledWith( + await waitForNextUpdate(); + + expect(fetchLogSummaryMock).toHaveBeenCalledTimes(1); + expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - variables: expect.objectContaining({ - sourceId: 'INITIAL_SOURCE_ID', - }), + sourceId: 'INITIAL_SOURCE_ID', }) ); - expect(getLastHookValue().buckets).toEqual( - firstMockResponse.data.source.logSummaryBetween.buckets - ); + expect(result.current.buckets).toEqual(firstMockResponse.data.buckets); - act((_, setArgs) => { - setArgs({ sourceId: 'CHANGED_SOURCE_ID' }); - }); + rerender({ sourceId: 'CHANGED_SOURCE_ID' }); + await waitForNextUpdate(); - expect(mockApolloClient.query).toHaveBeenCalledTimes(2); - expect(mockApolloClient.query).toHaveBeenLastCalledWith( + expect(fetchLogSummaryMock).toHaveBeenCalledTimes(2); + expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - variables: expect.objectContaining({ - sourceId: 'CHANGED_SOURCE_ID', - }), + sourceId: 'CHANGED_SOURCE_ID', }) ); - expect(getLastHookValue().buckets).toEqual( - secondMockResponse.data.source.logSummaryBetween.buckets - ); + expect(result.current.buckets).toEqual(secondMockResponse.data.buckets); }); - it('queries for new summary buckets when the filter query changes', () => { + it('queries for new summary buckets when the filter query changes', async () => { const firstMockResponse = createMockResponse([{ start: 99000, end: 101000, entriesCount: 1 }]); const secondMockResponse = createMockResponse([{ start: 99000, end: 101000, entriesCount: 2 }]); - const mockApolloClient = { - query: jest - .fn() - .mockReturnValueOnce(createSyncMockPromise(firstMockResponse)) - .mockReturnValueOnce(createSyncMockPromise(secondMockResponse)), - }; - const { act, getLastHookValue } = mountHook( + fetchLogSummaryMock + .mockResolvedValueOnce(firstMockResponse) + .mockResolvedValueOnce(secondMockResponse); + + const { result, waitForNextUpdate, rerender } = renderHook( ({ filterQuery }) => useLogSummary('SOURCE_ID', 100000, 1000, filterQuery), - createMockApolloProvider(mockApolloClient), - { filterQuery: 'INITIAL_FILTER_QUERY' } + { + initialProps: { filterQuery: 'INITIAL_FILTER_QUERY' }, + } ); - expect(mockApolloClient.query).toHaveBeenCalledTimes(1); - expect(mockApolloClient.query).toHaveBeenLastCalledWith( + await waitForNextUpdate(); + + expect(fetchLogSummaryMock).toHaveBeenCalledTimes(1); + expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - variables: expect.objectContaining({ - filterQuery: 'INITIAL_FILTER_QUERY', - }), + query: 'INITIAL_FILTER_QUERY', }) ); - expect(getLastHookValue().buckets).toEqual( - firstMockResponse.data.source.logSummaryBetween.buckets - ); + expect(result.current.buckets).toEqual(firstMockResponse.data.buckets); - act((_, setArgs) => { - setArgs({ filterQuery: 'CHANGED_FILTER_QUERY' }); - }); + rerender({ filterQuery: 'CHANGED_FILTER_QUERY' }); + await waitForNextUpdate(); - expect(mockApolloClient.query).toHaveBeenCalledTimes(2); - expect(mockApolloClient.query).toHaveBeenLastCalledWith( + expect(fetchLogSummaryMock).toHaveBeenCalledTimes(2); + expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - variables: expect.objectContaining({ - filterQuery: 'CHANGED_FILTER_QUERY', - }), + query: 'CHANGED_FILTER_QUERY', }) ); - expect(getLastHookValue().buckets).toEqual( - secondMockResponse.data.source.logSummaryBetween.buckets - ); + expect(result.current.buckets).toEqual(secondMockResponse.data.buckets); }); - it('queries for new summary buckets when the midpoint time changes', () => { - const mockApolloClient = { - query: jest - .fn() - .mockReturnValueOnce(createSyncMockPromise(createMockResponse([]))) - .mockReturnValueOnce(createSyncMockPromise(createMockResponse([]))), - }; + it('queries for new summary buckets when the midpoint time changes', async () => { + fetchLogSummaryMock + .mockResolvedValueOnce(createMockResponse([])) + .mockResolvedValueOnce(createMockResponse([])); - const { act } = mountHook( + const { waitForNextUpdate, rerender } = renderHook( ({ midpointTime }) => useLogSummary('SOURCE_ID', midpointTime, 1000, null), - createMockApolloProvider(mockApolloClient), - { midpointTime: 100000 } + { + initialProps: { midpointTime: 100000 }, + } ); - expect(mockApolloClient.query).toHaveBeenCalledTimes(1); - expect(mockApolloClient.query).toHaveBeenLastCalledWith( + await waitForNextUpdate(); + expect(fetchLogSummaryMock).toHaveBeenCalledTimes(1); + expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - variables: expect.objectContaining({ - start: 98500, - end: 101500, - }), + startDate: 98500, + endDate: 101500, }) ); - act((_, setArgs) => { - setArgs({ midpointTime: 200000 }); - }); + rerender({ midpointTime: 200000 }); + await waitForNextUpdate(); - expect(mockApolloClient.query).toHaveBeenCalledTimes(2); - expect(mockApolloClient.query).toHaveBeenLastCalledWith( + expect(fetchLogSummaryMock).toHaveBeenCalledTimes(2); + expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - variables: expect.objectContaining({ - start: 198500, - end: 201500, - }), + startDate: 198500, + endDate: 201500, }) ); }); - it('queries for new summary buckets when the interval size changes', () => { - const mockApolloClient = { - query: jest - .fn() - .mockReturnValueOnce(createSyncMockPromise(createMockResponse([]))) - .mockReturnValueOnce(createSyncMockPromise(createMockResponse([]))), - }; + it('queries for new summary buckets when the interval size changes', async () => { + fetchLogSummaryMock + .mockResolvedValueOnce(createMockResponse([])) + .mockResolvedValueOnce(createMockResponse([])); - const { act } = mountHook( + const { waitForNextUpdate, rerender } = renderHook( ({ intervalSize }) => useLogSummary('SOURCE_ID', 100000, intervalSize, null), - createMockApolloProvider(mockApolloClient), - { intervalSize: 1000 } + { + initialProps: { intervalSize: 1000 }, + } ); - expect(mockApolloClient.query).toHaveBeenCalledTimes(1); - expect(mockApolloClient.query).toHaveBeenLastCalledWith( + await waitForNextUpdate(); + expect(fetchLogSummaryMock).toHaveBeenCalledTimes(1); + expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - variables: expect.objectContaining({ - bucketSize: 10, - start: 98500, - end: 101500, - }), + bucketSize: 10, + startDate: 98500, + endDate: 101500, }) ); - act((_, setArgs) => { - setArgs({ intervalSize: 2000 }); - }); + rerender({ intervalSize: 2000 }); + await waitForNextUpdate(); - expect(mockApolloClient.query).toHaveBeenCalledTimes(2); - expect(mockApolloClient.query).toHaveBeenLastCalledWith( + expect(fetchLogSummaryMock).toHaveBeenCalledTimes(2); + expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - variables: expect.objectContaining({ - bucketSize: 20, - start: 97000, - end: 103000, - }), + bucketSize: 20, + startDate: 97000, + endDate: 103000, }) ); }); }); -const createMockApolloProvider = (mockClient: any): React.FunctionComponent => ({ children }) => ( - {children} -); - const createMockResponse = ( buckets: Array<{ start: number; end: number; entriesCount: number }> -) => ({ data: { source: { logSummaryBetween: { buckets } } } }); - -const createSyncMockPromise = (value: Value) => ({ - then: (callback: (value: Value) => any) => callback(value), -}); +) => ({ data: { buckets, start: Number.NEGATIVE_INFINITY, end: Number.POSITIVE_INFINITY } }); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.tsx index a188e698991a0..c39b7075af325 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.tsx @@ -6,14 +6,12 @@ import { useState } from 'react'; -import { LogSummary as LogSummaryQuery } from '../../../graphql/types'; -import { useApolloClient } from '../../../utils/apollo_context'; import { useCancellableEffect } from '../../../utils/cancellable_effect'; -import { logSummaryQuery } from './log_summary.gql_query'; import { useLogSummaryBufferInterval } from './use_log_summary_buffer_interval'; +import { fetchLogSummary } from './api/fetch_log_summary'; +import { LogEntriesSummaryResponse } from '../../../../common/http_api'; -export type LogSummaryBetween = LogSummaryQuery.Query['source']['logSummaryBetween']; -export type LogSummaryBuckets = LogSummaryBetween['buckets']; +export type LogSummaryBuckets = LogEntriesSummaryResponse['data']['buckets']; export const useLogSummary = ( sourceId: string, @@ -21,9 +19,7 @@ export const useLogSummary = ( intervalSize: number, filterQuery: string | null ) => { - const [logSummaryBetween, setLogSummaryBetween] = useState({ buckets: [] }); - const apolloClient = useApolloClient(); - + const [logSummaryBuckets, setLogSummaryBuckets] = useState([]); const { start: bufferStart, end: bufferEnd, bucketSize } = useLogSummaryBufferInterval( midpointTime, intervalSize @@ -31,33 +27,27 @@ export const useLogSummary = ( useCancellableEffect( getIsCancelled => { - if (!apolloClient || bufferStart === null || bufferEnd === null) { + if (bufferStart === null || bufferEnd === null) { return; } - apolloClient - .query({ - fetchPolicy: 'no-cache', - query: logSummaryQuery, - variables: { - filterQuery, - sourceId, - start: bufferStart, - end: bufferEnd, - bucketSize, - }, - }) - .then(response => { - if (!getIsCancelled()) { - setLogSummaryBetween(response.data.source.logSummaryBetween); - } - }); + fetchLogSummary({ + sourceId, + startDate: bufferStart, + endDate: bufferEnd, + bucketSize, + query: filterQuery, + }).then(response => { + if (!getIsCancelled()) { + setLogSummaryBuckets(response.data.buckets); + } + }); }, - [apolloClient, sourceId, filterQuery, bufferStart, bufferEnd, bucketSize] + [sourceId, filterQuery, bufferStart, bufferEnd, bucketSize] ); return { - buckets: logSummaryBetween.buckets, + buckets: logSummaryBuckets, start: bufferStart, end: bufferEnd, }; diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx b/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx index c43e2f5a544cf..4baa93f93753c 100644 --- a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx @@ -6,7 +6,6 @@ import { fetch } from '../../utils/fetch'; import { useMetricsExplorerData } from './use_metrics_explorer_data'; -import { MetricsExplorerAggregation } from '../../../server/routes/metrics_explorer/types'; import { renderHook } from '@testing-library/react-hooks'; @@ -132,8 +131,8 @@ describe('useMetricsExplorerData Hook', () => { rerender({ options: { ...options, - aggregation: MetricsExplorerAggregation.count, - metrics: [{ aggregation: MetricsExplorerAggregation.count }], + aggregation: 'count', + metrics: [{ aggregation: 'count' }], }, source, derivedIndexPattern, diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts b/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts index c2a599ea1ae78..1bca733a6e02a 100644 --- a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts +++ b/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts @@ -9,10 +9,7 @@ import { isEqual } from 'lodash'; import { useEffect, useState } from 'react'; import { IIndexPattern } from 'src/plugins/data/public'; import { SourceQuery } from '../../../common/graphql/types'; -import { - MetricsExplorerAggregation, - MetricsExplorerResponse, -} from '../../../server/routes/metrics_explorer/types'; +import { MetricsExplorerResponse } from '../../../server/routes/metrics_explorer/types'; import { fetch } from '../../utils/fetch'; import { convertKueryToElasticSearchQuery } from '../../utils/kuery'; import { MetricsExplorerOptions, MetricsExplorerTimeOptions } from './use_metrics_explorer_options'; @@ -48,8 +45,8 @@ export function useMetricsExplorerData( '../api/infra/metrics_explorer', { metrics: - options.aggregation === MetricsExplorerAggregation.count - ? [{ aggregation: MetricsExplorerAggregation.count }] + options.aggregation === 'count' + ? [{ aggregation: 'count' }] : options.metrics.map(metric => ({ aggregation: metric.aggregation, field: metric.field, diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.test.tsx b/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.test.tsx index e58184c78b4b8..1381ed9da656a 100644 --- a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.test.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.test.tsx @@ -14,7 +14,6 @@ import { DEFAULT_OPTIONS, DEFAULT_TIMERANGE, } from './use_metrics_explorer_options'; -import { MetricsExplorerAggregation } from '../../../server/routes/metrics_explorer/types'; const renderUseMetricsExplorerOptionsHook = () => renderHook(() => useMetricsExplorerOptions(), { @@ -68,7 +67,7 @@ describe('useMetricExplorerOptions', () => { const { result, rerender } = renderUseMetricsExplorerOptionsHook(); const newOptions: MetricsExplorerOptions = { ...DEFAULT_OPTIONS, - metrics: [{ aggregation: MetricsExplorerAggregation.count }], + metrics: [{ aggregation: 'count' }], }; act(() => { result.current.setOptions(newOptions); @@ -95,7 +94,7 @@ describe('useMetricExplorerOptions', () => { it('should load from store when available', () => { const newOptions: MetricsExplorerOptions = { ...DEFAULT_OPTIONS, - metrics: [{ aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' }], + metrics: [{ aggregation: 'avg', field: 'system.load.1' }], }; STORE.MetricsExplorerOptions = JSON.stringify(newOptions); const { result } = renderUseMetricsExplorerOptionsHook(); diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts b/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts index de7a8d5805ecc..076a9159940e2 100644 --- a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts +++ b/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts @@ -62,24 +62,24 @@ export const DEFAULT_CHART_OPTIONS: MetricsExplorerChartOptions = { export const DEFAULT_METRICS: MetricsExplorerOptionsMetric[] = [ { - aggregation: MetricsExplorerAggregation.avg, + aggregation: 'avg', field: 'system.cpu.user.pct', color: MetricsExplorerColor.color0, }, { - aggregation: MetricsExplorerAggregation.avg, + aggregation: 'avg', field: 'kubernetes.pod.cpu.usage.node.pct', color: MetricsExplorerColor.color1, }, { - aggregation: MetricsExplorerAggregation.avg, + aggregation: 'avg', field: 'docker.cpu.total.pct', color: MetricsExplorerColor.color2, }, ]; export const DEFAULT_OPTIONS: MetricsExplorerOptions = { - aggregation: MetricsExplorerAggregation.avg, + aggregation: 'avg', metrics: DEFAULT_METRICS, }; diff --git a/x-pack/legacy/plugins/infra/public/containers/waffle/use_snaphot.ts b/x-pack/legacy/plugins/infra/public/containers/waffle/use_snaphot.ts index 1a46c6269af1e..1ff7512080274 100644 --- a/x-pack/legacy/plugins/infra/public/containers/waffle/use_snaphot.ts +++ b/x-pack/legacy/plugins/infra/public/containers/waffle/use_snaphot.ts @@ -65,6 +65,7 @@ export function useSnapshot( error: (error && error.message) || null, loading, nodes: response ? response.nodes : [], + interval: response ? response.interval : '60s', reload: makeRequest, }; } diff --git a/x-pack/legacy/plugins/infra/public/graphql/introspection.json b/x-pack/legacy/plugins/infra/public/graphql/introspection.json index ae8d3dcdd5bec..efb527569b30e 100644 --- a/x-pack/legacy/plugins/infra/public/graphql/introspection.json +++ b/x-pack/legacy/plugins/infra/public/graphql/introspection.json @@ -286,134 +286,6 @@ "isDeprecated": false, "deprecationReason": null }, - { - "name": "logSummaryBetween", - "description": "A consecutive span of summary buckets within an interval", - "args": [ - { - "name": "start", - "description": "The millisecond timestamp that corresponds to the start of the interval", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "end", - "description": "The millisecond timestamp that corresponds to the end of the interval", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "bucketSize", - "description": "The size of each bucket in milliseconds", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "The query to filter the log entries by", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "InfraLogSummaryInterval", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "logSummaryHighlightsBetween", - "description": "Spans of summary highlight buckets within an interval", - "args": [ - { - "name": "start", - "description": "The millisecond timestamp that corresponds to the start of the interval", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "end", - "description": "The millisecond timestamp that corresponds to the end of the interval", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "bucketSize", - "description": "The size of each bucket in milliseconds", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "The query to filter the log entries by", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "highlightQueries", - "description": "The highlighting to apply to the log entries", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "InfraLogSummaryHighlightInterval", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "logItem", "description": "", @@ -1665,234 +1537,6 @@ "enumValues": null, "possibleTypes": null }, - { - "kind": "OBJECT", - "name": "InfraLogSummaryInterval", - "description": "A consecutive sequence of log summary buckets", - "fields": [ - { - "name": "start", - "description": "The millisecond timestamp corresponding to the start of the interval covered by the summary", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "end", - "description": "The millisecond timestamp corresponding to the end of the interval covered by the summary", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "filterQuery", - "description": "The query the log entries were filtered by", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "buckets", - "description": "A list of the log entries", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "InfraLogSummaryBucket", "ofType": null } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "InfraLogSummaryBucket", - "description": "A log summary bucket", - "fields": [ - { - "name": "start", - "description": "The start timestamp of the bucket", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "end", - "description": "The end timestamp of the bucket", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "entriesCount", - "description": "The number of entries inside the bucket", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Int", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "InfraLogSummaryHighlightInterval", - "description": "A consecutive sequence of log summary highlight buckets", - "fields": [ - { - "name": "start", - "description": "The millisecond timestamp corresponding to the start of the interval covered by the summary", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "end", - "description": "The millisecond timestamp corresponding to the end of the interval covered by the summary", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "filterQuery", - "description": "The query the log entries were filtered by", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "highlightQuery", - "description": "The query the log entries were highlighted with", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "buckets", - "description": "A list of the log entries", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "InfraLogSummaryHighlightBucket", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "InfraLogSummaryHighlightBucket", - "description": "A log summary highlight bucket", - "fields": [ - { - "name": "start", - "description": "The start timestamp of the bucket", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "end", - "description": "The end timestamp of the bucket", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "entriesCount", - "description": "The number of highlighted entries inside the bucket", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Int", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "representativeKey", - "description": "The time key of a representative of the highlighted log entries in this bucket", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "InfraTimeKey", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, { "kind": "OBJECT", "name": "InfraLogItem", diff --git a/x-pack/legacy/plugins/infra/public/graphql/types.ts b/x-pack/legacy/plugins/infra/public/graphql/types.ts index 3715f02bb252e..29c849a202930 100644 --- a/x-pack/legacy/plugins/infra/public/graphql/types.ts +++ b/x-pack/legacy/plugins/infra/public/graphql/types.ts @@ -36,10 +36,6 @@ export interface InfraSource { logEntriesBetween: InfraLogEntryInterval; /** Sequences of log entries matching sets of highlighting queries within an interval */ logEntryHighlights: InfraLogEntryInterval[]; - /** A consecutive span of summary buckets within an interval */ - logSummaryBetween: InfraLogSummaryInterval; - /** Spans of summary highlight buckets within an interval */ - logSummaryHighlightsBetween: InfraLogSummaryHighlightInterval[]; logItem: InfraLogItem; /** A snapshot of nodes */ @@ -210,50 +206,6 @@ export interface InfraLogEntryFieldColumn { /** A list of highlighted substrings of the value */ highlights: string[]; } -/** A consecutive sequence of log summary buckets */ -export interface InfraLogSummaryInterval { - /** The millisecond timestamp corresponding to the start of the interval covered by the summary */ - start?: number | null; - /** The millisecond timestamp corresponding to the end of the interval covered by the summary */ - end?: number | null; - /** The query the log entries were filtered by */ - filterQuery?: string | null; - /** A list of the log entries */ - buckets: InfraLogSummaryBucket[]; -} -/** A log summary bucket */ -export interface InfraLogSummaryBucket { - /** The start timestamp of the bucket */ - start: number; - /** The end timestamp of the bucket */ - end: number; - /** The number of entries inside the bucket */ - entriesCount: number; -} -/** A consecutive sequence of log summary highlight buckets */ -export interface InfraLogSummaryHighlightInterval { - /** The millisecond timestamp corresponding to the start of the interval covered by the summary */ - start?: number | null; - /** The millisecond timestamp corresponding to the end of the interval covered by the summary */ - end?: number | null; - /** The query the log entries were filtered by */ - filterQuery?: string | null; - /** The query the log entries were highlighted with */ - highlightQuery?: string | null; - /** A list of the log entries */ - buckets: InfraLogSummaryHighlightBucket[]; -} -/** A log summary highlight bucket */ -export interface InfraLogSummaryHighlightBucket { - /** The start timestamp of the bucket */ - start: number; - /** The end timestamp of the bucket */ - end: number; - /** The number of highlighted entries inside the bucket */ - entriesCount: number; - /** The time key of a representative of the highlighted log entries in this bucket */ - representativeKey: InfraTimeKey; -} export interface InfraLogItem { /** The ID of the document */ @@ -474,28 +426,6 @@ export interface LogEntryHighlightsInfraSourceArgs { /** The highlighting to apply to the log entries */ highlights: InfraLogEntryHighlightInput[]; } -export interface LogSummaryBetweenInfraSourceArgs { - /** The millisecond timestamp that corresponds to the start of the interval */ - start: number; - /** The millisecond timestamp that corresponds to the end of the interval */ - end: number; - /** The size of each bucket in milliseconds */ - bucketSize: number; - /** The query to filter the log entries by */ - filterQuery?: string | null; -} -export interface LogSummaryHighlightsBetweenInfraSourceArgs { - /** The millisecond timestamp that corresponds to the start of the interval */ - start: number; - /** The millisecond timestamp that corresponds to the end of the interval */ - end: number; - /** The size of each bucket in milliseconds */ - bucketSize: number; - /** The query to filter the log entries by */ - filterQuery?: string | null; - /** The highlighting to apply to the log entries */ - highlightQueries: string[]; -} export interface LogItemInfraSourceArgs { id: string; } @@ -755,99 +685,6 @@ export namespace LogEntryHighlightsQuery { export type Entries = InfraLogEntryHighlightFields.Fragment; } -export namespace LogSummaryHighlightsQuery { - export type Variables = { - sourceId?: string | null; - start: number; - end: number; - bucketSize: number; - highlightQueries: string[]; - filterQuery?: string | null; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'InfraSource'; - - id: string; - - logSummaryHighlightsBetween: LogSummaryHighlightsBetween[]; - }; - - export type LogSummaryHighlightsBetween = { - __typename?: 'InfraLogSummaryHighlightInterval'; - - start?: number | null; - - end?: number | null; - - buckets: Buckets[]; - }; - - export type Buckets = { - __typename?: 'InfraLogSummaryHighlightBucket'; - - start: number; - - end: number; - - entriesCount: number; - - representativeKey: RepresentativeKey; - }; - - export type RepresentativeKey = InfraTimeKeyFields.Fragment; -} - -export namespace LogSummary { - export type Variables = { - sourceId?: string | null; - start: number; - end: number; - bucketSize: number; - filterQuery?: string | null; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'InfraSource'; - - id: string; - - logSummaryBetween: LogSummaryBetween; - }; - - export type LogSummaryBetween = { - __typename?: 'InfraLogSummaryInterval'; - - start?: number | null; - - end?: number | null; - - buckets: Buckets[]; - }; - - export type Buckets = { - __typename?: 'InfraLogSummaryBucket'; - - start: number; - - end: number; - - entriesCount: number; - }; -} - export namespace MetricsQuery { export type Variables = { sourceId: string; diff --git a/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx b/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx index 0512fb0a46b90..14533d46aaef8 100644 --- a/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx @@ -15,7 +15,6 @@ import { resp, createSeries, } from '../../../utils/fixtures/metrics_explorer'; -import { MetricsExplorerAggregation } from '../../../../server/routes/metrics_explorer/types'; const renderUseMetricsExplorerStateHook = () => renderHook(props => useMetricsExplorerState(props.source, props.derivedIndexPattern), { @@ -89,11 +88,9 @@ describe('useMetricsExplorerState', () => { it('should change the metric', async () => { const { result } = renderUseMetricsExplorerStateHook(); const { handleMetricsChange } = result.current; - handleMetricsChange([ - { aggregation: MetricsExplorerAggregation.max, field: 'system.load.1' }, - ]); + handleMetricsChange([{ aggregation: 'max', field: 'system.load.1' }]); expect(result.current.options.metrics).toEqual([ - { aggregation: MetricsExplorerAggregation.max, field: 'system.load.1' }, + { aggregation: 'max', field: 'system.load.1' }, ]); }); }); @@ -134,38 +131,32 @@ describe('useMetricsExplorerState', () => { it('should set the metrics to only count when selecting count', async () => { const { result, waitForNextUpdate } = renderUseMetricsExplorerStateHook(); const { handleMetricsChange } = result.current; - handleMetricsChange([ - { aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' }, - ]); + handleMetricsChange([{ aggregation: 'avg', field: 'system.load.1' }]); expect(result.current.options.metrics).toEqual([ - { aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' }, + { aggregation: 'avg', field: 'system.load.1' }, ]); await waitForNextUpdate(); const { handleAggregationChange } = result.current; - handleAggregationChange(MetricsExplorerAggregation.count); + handleAggregationChange('count'); await waitForNextUpdate(); - expect(result.current.options.aggregation).toBe(MetricsExplorerAggregation.count); - expect(result.current.options.metrics).toEqual([ - { aggregation: MetricsExplorerAggregation.count }, - ]); + expect(result.current.options.aggregation).toBe('count'); + expect(result.current.options.metrics).toEqual([{ aggregation: 'count' }]); }); it('should change aggregation for metrics', async () => { const { result, waitForNextUpdate } = renderUseMetricsExplorerStateHook(); const { handleMetricsChange } = result.current; - handleMetricsChange([ - { aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' }, - ]); + handleMetricsChange([{ aggregation: 'avg', field: 'system.load.1' }]); expect(result.current.options.metrics).toEqual([ - { aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' }, + { aggregation: 'avg', field: 'system.load.1' }, ]); await waitForNextUpdate(); const { handleAggregationChange } = result.current; - handleAggregationChange(MetricsExplorerAggregation.max); + handleAggregationChange('max'); await waitForNextUpdate(); - expect(result.current.options.aggregation).toBe(MetricsExplorerAggregation.max); + expect(result.current.options.aggregation).toBe('max'); expect(result.current.options.metrics).toEqual([ - { aggregation: MetricsExplorerAggregation.max, field: 'system.load.1' }, + { aggregation: 'max', field: 'system.load.1' }, ]); }); }); diff --git a/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts b/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts index 57ea886169701..22d18234197b6 100644 --- a/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts +++ b/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts @@ -99,10 +99,10 @@ export const useMetricsExplorerState = ( (aggregation: MetricsExplorerAggregation) => { setAfterKey(null); const metrics = - aggregation === MetricsExplorerAggregation.count + aggregation === 'count' ? [{ aggregation }] : options.metrics - .filter(metric => metric.aggregation !== MetricsExplorerAggregation.count) + .filter(metric => metric.aggregation !== 'count') .map(metric => ({ ...metric, aggregation, diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/index.tsx index 4eddecf732f75..b0619fdcc2acf 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/index.tsx @@ -21,13 +21,14 @@ import { Source, useSource } from '../../containers/source'; import { StreamPage } from './stream'; import { SettingsPage } from '../shared/settings'; import { AppNavigation } from '../../components/navigation/app_navigation'; -import { LogEntryRatePage } from './log_entry_rate'; import { useLogAnalysisCapabilities, LogAnalysisCapabilities, } from '../../containers/logs/log_analysis'; import { useSourceId } from '../../containers/source_id'; import { RedirectWithQueryParams } from '../../utils/redirect_with_query_params'; +import { LogEntryCategoriesPage } from './log_entry_categories'; +import { LogEntryRatePage } from './log_entry_rate'; interface LogsPageProps extends RouteComponentProps { uiCapabilities: UICapabilities; @@ -53,6 +54,16 @@ export const LogsPage = injectUICapabilities(({ match, uiCapabilities }: LogsPag path: `${match.path}/log-rate`, }; + const logCategoriesTab = { + title: ( + <> + {logCategoriesTabTitle} + + + ), + path: `${match.path}/log-categories`, + }; + const settingsTab = { title: settingsTabTitle, path: `${match.path}/settings`, @@ -90,7 +101,7 @@ export const LogsPage = injectUICapabilities(({ match, uiCapabilities }: LogsPag @@ -99,6 +110,7 @@ export const LogsPage = injectUICapabilities(({ match, uiCapabilities }: LogsPag + + jobTypes.reduce( + (accumulatedJobIds, jobType) => ({ + ...accumulatedJobIds, + [jobType]: getJobId(spaceId, sourceId, jobType), + }), + {} as Record + ); + +const getJobSummary = async (spaceId: string, sourceId: string) => { + const response = await callJobsSummaryAPI(spaceId, sourceId, jobTypes); + const jobIds = Object.values(getJobIds(spaceId, sourceId)); + + return response.filter(jobSummary => jobIds.includes(jobSummary.id)); +}; + +const getModuleDefinition = async () => { + return await callGetMlModuleAPI(moduleId); +}; + +const setUpModule = async ( + start: number | undefined, + end: number | undefined, + { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration +) => { + const indexNamePattern = indices.join(','); + const jobOverrides = [ + { + job_id: 'log-entry-categories-count' as const, + analysis_config: { + bucket_span: `${bucketSpan}ms`, + }, + data_description: { + time_field: timestampField, + }, + custom_settings: { + logs_source_config: { + indexPattern: indexNamePattern, + timestampField, + bucketSpan, + }, + }, + }, + ]; + + return callSetupMlModuleAPI( + moduleId, + start, + end, + spaceId, + sourceId, + indexNamePattern, + jobOverrides + ); +}; + +const cleanUpModule = async (spaceId: string, sourceId: string) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, jobTypes); +}; + +const validateSetupIndices = async ({ indices, timestampField }: ModuleSourceConfiguration) => { + return await callValidateIndicesAPI(indices, [ + { + name: timestampField, + validTypes: ['date'], + }, + { + name: partitionField, + validTypes: ['keyword'], + }, + { + name: categoriesMessageField, + validTypes: ['text'], + }, + ]); +}; + +export const logEntryCategoriesModule: ModuleDescriptor = { + moduleId, + jobTypes, + bucketSpan, + getJobIds, + getJobSummary, + getModuleDefinition, + setUpModule, + cleanUpModule, + validateSetupIndices, +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page.tsx new file mode 100644 index 0000000000000..64e83a6eaa497 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { ColumnarPage } from '../../../components/page'; +import { LogEntryCategoriesPageContent } from './page_content'; +import { LogEntryCategoriesPageProviders } from './page_providers'; + +export const LogEntryCategoriesPage = () => { + return ( + + + + + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx new file mode 100644 index 0000000000000..9a50acf622ee1 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import React, { useContext, useEffect } from 'react'; + +import { isSetupStatusWithResults } from '../../../../common/log_analysis'; +import { LoadingPage } from '../../../components/loading_page'; +import { + LogAnalysisSetupStatusUnknownPrompt, + MlUnavailablePrompt, +} from '../../../components/logging/log_analysis_setup'; +import { LogAnalysisCapabilities } from '../../../containers/logs/log_analysis'; +import { LogEntryCategoriesSetupContent } from './page_setup_content'; +import { useLogEntryCategoriesModuleContext } from './use_log_entry_categories_module'; + +export const LogEntryCategoriesPageContent = () => { + const { hasLogAnalysisCapabilites } = useContext(LogAnalysisCapabilities.Context); + + const { + fetchJobStatus, + fetchModuleDefinition, + setupStatus, + } = useLogEntryCategoriesModuleContext(); + + useEffect(() => { + fetchModuleDefinition(); + fetchJobStatus(); + }, [fetchJobStatus, fetchModuleDefinition]); + + if (!hasLogAnalysisCapabilites) { + return ; + } else if (setupStatus === 'initializing') { + return ( + + ); + } else if (setupStatus === 'unknown') { + return ; + } else if (isSetupStatusWithResults(setupStatus)) { + return null; + // return ; + } else { + return ; + } +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx new file mode 100644 index 0000000000000..619cea6eda8b7 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { useSourceContext } from '../../../containers/source'; +import { useKibanaSpaceId } from '../../../utils/use_kibana_space_id'; +import { LogEntryCategoriesModuleProvider } from './use_log_entry_categories_module'; + +export const LogEntryCategoriesPageProviders: React.FunctionComponent = ({ children }) => { + const { sourceId, source } = useSourceContext(); + const spaceId = useKibanaSpaceId(); + + return ( + + {children} + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx new file mode 100644 index 0000000000000..f0e90cb7ccc05 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiSpacer, EuiSteps, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { useMemo } from 'react'; + +import { + createInitialConfigurationStep, + createProcessStep, + LogAnalysisSetupPage, + LogAnalysisSetupPageContent, + LogAnalysisSetupPageHeader, +} from '../../../components/logging/log_analysis_setup'; +import { useTrackPageview } from '../../../hooks/use_track_metric'; +import { useLogEntryCategoriesSetup } from './use_log_entry_categories_setup'; + +export const LogEntryCategoriesSetupContent: React.FunctionComponent = () => { + useTrackPageview({ app: 'infra_logs', path: 'log_entry_categories_setup' }); + useTrackPageview({ app: 'infra_logs', path: 'log_entry_categories_setup', delay: 15000 }); + + const { + cleanUpAndSetUp, + endTime, + isValidating, + lastSetupErrorMessages, + setEndTime, + setStartTime, + setValidatedIndices, + setUp, + setupStatus, + startTime, + validatedIndices, + validationErrors, + viewResults, + } = useLogEntryCategoriesSetup(); + + const steps = useMemo( + () => [ + createInitialConfigurationStep({ + setStartTime, + setEndTime, + startTime, + endTime, + isValidating, + validatedIndices, + setValidatedIndices, + validationErrors, + }), + createProcessStep({ + cleanUpAndSetUp, + errorMessages: lastSetupErrorMessages, + isConfigurationValid: validationErrors.length <= 0, + setUp, + setupStatus, + viewResults, + }), + ], + [ + cleanUpAndSetUp, + endTime, + isValidating, + lastSetupErrorMessages, + setEndTime, + setStartTime, + setUp, + setValidatedIndices, + setupStatus, + startTime, + validatedIndices, + validationErrors, + viewResults, + ] + ); + + return ( + + + + + + + + + + + + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx new file mode 100644 index 0000000000000..918c252f6350c --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import createContainer from 'constate'; +import { useMemo } from 'react'; + +import { + useLogAnalysisModule, + ModuleSourceConfiguration, +} from '../../../containers/logs/log_analysis'; +import { logEntryCategoriesModule } from './module_descriptor'; + +export const useLogEntryCategoriesModule = ({ + indexPattern, + sourceId, + spaceId, + timestampField, +}: { + indexPattern: string; + sourceId: string; + spaceId: string; + timestampField: string; +}) => { + const sourceConfiguration: ModuleSourceConfiguration = useMemo( + () => ({ + indices: indexPattern.split(','), + sourceId, + spaceId, + timestampField, + }), + [indexPattern, sourceId, spaceId, timestampField] + ); + + return useLogAnalysisModule({ + moduleDescriptor: logEntryCategoriesModule, + sourceConfiguration, + }); +}; + +export const [ + LogEntryCategoriesModuleProvider, + useLogEntryCategoriesModuleContext, +] = createContainer(useLogEntryCategoriesModule); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_setup.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_setup.tsx new file mode 100644 index 0000000000000..c011230942d7c --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_setup.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useAnalysisSetupState } from '../../../containers/logs/log_analysis'; +import { useLogEntryCategoriesModuleContext } from './use_log_entry_categories_module'; + +export const useLogEntryCategoriesSetup = () => { + const { + cleanUpAndSetUpModule, + lastSetupErrorMessages, + moduleDescriptor, + setUpModule, + setupStatus, + sourceConfiguration, + viewResults, + } = useLogEntryCategoriesModuleContext(); + + const { + cleanUpAndSetUp, + endTime, + isValidating, + setEndTime, + setStartTime, + setValidatedIndices, + setUp, + startTime, + validatedIndices, + validationErrors, + } = useAnalysisSetupState({ + cleanUpAndSetUpModule, + moduleDescriptor, + setUpModule, + sourceConfiguration, + }); + + return { + cleanUpAndSetUp, + endTime, + isValidating, + lastSetupErrorMessages, + setEndTime, + setStartTime, + setValidatedIndices, + setUp, + setupStatus, + startTime, + validatedIndices, + validationErrors, + viewResults, + }; +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx index e71985f73fbb8..a80464ed42cb2 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx @@ -9,29 +9,19 @@ import React, { useContext, useEffect } from 'react'; import { isSetupStatusWithResults } from '../../../../common/log_analysis'; import { LoadingPage } from '../../../components/loading_page'; +import { + LogAnalysisSetupStatusUnknownPrompt, + MlUnavailablePrompt, +} from '../../../components/logging/log_analysis_setup'; import { LogAnalysisCapabilities } from '../../../containers/logs/log_analysis'; -import { Source } from '../../../containers/source'; import { LogEntryRateResultsContent } from './page_results_content'; import { LogEntryRateSetupContent } from './page_setup_content'; -import { LogEntryRateUnavailableContent } from './page_unavailable_content'; -import { LogEntryRateSetupStatusUnknownContent } from './page_setup_status_unknown'; import { useLogEntryRateModuleContext } from './use_log_entry_rate_module'; export const LogEntryRatePageContent = () => { - const { sourceId } = useContext(Source.Context); const { hasLogAnalysisCapabilites } = useContext(LogAnalysisCapabilities.Context); - const { - cleanUpAndSetUpModule: cleanupAndSetup, - fetchJobStatus, - fetchModuleDefinition, - lastSetupErrorMessages, - moduleDescriptor, - setUpModule, - setupStatus, - sourceConfiguration, - viewResults, - } = useLogEntryRateModuleContext(); + const { fetchJobStatus, fetchModuleDefinition, setupStatus } = useLogEntryRateModuleContext(); useEffect(() => { fetchModuleDefinition(); @@ -39,7 +29,7 @@ export const LogEntryRatePageContent = () => { }, [fetchJobStatus, fetchModuleDefinition]); if (!hasLogAnalysisCapabilites) { - return ; + return ; } else if (setupStatus === 'initializing') { return ( { /> ); } else if (setupStatus === 'unknown') { - return ; + return ; } else if (isSetupStatusWithResults(setupStatus)) { - return ( - - ); + return ; } else { - return ( - - ); + return ; } }; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx index be637bc29a0db..b6ab8acdea5b2 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx @@ -38,18 +38,22 @@ import { const JOB_STATUS_POLLING_INTERVAL = 30000; -export const LogEntryRateResultsContent = ({ - sourceId, - isFirstUse, -}: { - sourceId: string; - isFirstUse: boolean; -}) => { +export const LogEntryRateResultsContent: React.FunctionComponent = () => { useTrackPageview({ app: 'infra_logs', path: 'log_entry_rate_results' }); useTrackPageview({ app: 'infra_logs', path: 'log_entry_rate_results', delay: 15000 }); const [dateFormat] = useKibanaUiSetting('dateFormat', 'MMMM D, YYYY h:mm A'); + const { + fetchJobStatus, + jobStatus, + setupStatus, + viewSetupForReconfiguration, + viewSetupForUpdate, + jobIds, + sourceConfiguration: { sourceId }, + } = useLogEntryRateModuleContext(); + const { timeRange: selectedTimeRange, setTimeRange: setSelectedTimeRange, @@ -126,14 +130,7 @@ export const LogEntryRateResultsContent = ({ [setAutoRefresh] ); - const { - fetchJobStatus, - jobStatus, - setupStatus, - viewSetupForReconfiguration, - viewSetupForUpdate, - jobIds, - } = useLogEntryRateModuleContext(); + const isFirstUse = useMemo(() => setupStatus === 'hiddenAfterSuccess', [setupStatus]); useEffect(() => { getLogEntryRate(); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx index 6c04404b91231..7e90cf29072e1 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx @@ -4,103 +4,96 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - EuiPage, - EuiPageBody, - EuiPageContent, - EuiPageContentBody, - EuiPageContentHeader, - EuiPageContentHeaderSection, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; +import { EuiSpacer, EuiSteps, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { useMemo } from 'react'; -import euiStyled from '../../../../../../common/eui_styled_components'; -import { SetupStatus } from '../../../../common/log_analysis'; -import { ModuleDescriptor, ModuleSourceConfiguration } from '../../../containers/logs/log_analysis'; +import { + createInitialConfigurationStep, + createProcessStep, + LogAnalysisSetupPage, + LogAnalysisSetupPageContent, + LogAnalysisSetupPageHeader, +} from '../../../components/logging/log_analysis_setup'; import { useTrackPageview } from '../../../hooks/use_track_metric'; -import { LogEntryRateSetupSteps } from './setup'; - -type SetupHandler = ( - indices: string[], - startTime: number | undefined, - endTime: number | undefined -) => void; +import { useLogEntryRateSetup } from './use_log_entry_rate_setup'; -interface LogEntryRateSetupContentProps { - cleanupAndSetup: SetupHandler; - errorMessages: string[]; - moduleDescriptor: ModuleDescriptor; - setup: SetupHandler; - setupStatus: SetupStatus; - sourceConfiguration: ModuleSourceConfiguration; - viewResults: () => void; -} - -export const LogEntryRateSetupContent = ({ - cleanupAndSetup, - errorMessages, - setup, - setupStatus, - viewResults, - moduleDescriptor, - sourceConfiguration, -}: LogEntryRateSetupContentProps) => { +export const LogEntryRateSetupContent: React.FunctionComponent = () => { useTrackPageview({ app: 'infra_logs', path: 'log_entry_rate_setup' }); useTrackPageview({ app: 'infra_logs', path: 'log_entry_rate_setup', delay: 15000 }); + const { + cleanUpAndSetUp, + endTime, + isValidating, + lastSetupErrorMessages, + setEndTime, + setStartTime, + setValidatedIndices, + setUp, + setupStatus, + startTime, + validatedIndices, + validationErrors, + viewResults, + } = useLogEntryRateSetup(); + + const steps = useMemo( + () => [ + createInitialConfigurationStep({ + setStartTime, + setEndTime, + startTime, + endTime, + isValidating, + validatedIndices, + setValidatedIndices, + validationErrors, + }), + createProcessStep({ + cleanUpAndSetUp, + errorMessages: lastSetupErrorMessages, + isConfigurationValid: validationErrors.length <= 0, + setUp, + setupStatus, + viewResults, + }), + ], + [ + cleanUpAndSetUp, + endTime, + isValidating, + lastSetupErrorMessages, + setEndTime, + setStartTime, + setUp, + setValidatedIndices, + setupStatus, + startTime, + validatedIndices, + validationErrors, + viewResults, + ] + ); + return ( - - - - - - -

- -

-
-
-
- - - - - - - -
-
-
+ + + + + + + + + + + + ); }; - -// !important due to https://github.com/elastic/eui/issues/2232 -const LogEntryRateSetupPageContent = euiStyled(EuiPageContent)` - max-width: 768px !important; -`; - -const LogEntryRateSetupPage = euiStyled(EuiPage)` - height: 100%; -`; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx index 38aa4b068c9e9..e5e719c2d69f6 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx @@ -12,6 +12,7 @@ import { EuiStat, EuiTitle, EuiLoadingSpinner, + EuiButton, } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { i18n } from '@kbn/i18n'; @@ -97,6 +98,11 @@ export const AnomaliesResults: React.FunctionComponent<{

{title}

+ + + Recreate jobs + + diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/setup_steps.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/setup_steps.tsx deleted file mode 100644 index 967c69dfae950..0000000000000 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/setup_steps.tsx +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiSteps, EuiStepStatus } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; - -import { SetupStatus } from '../../../../../common/log_analysis'; -import { useAnalysisSetupState } from '../../../../containers/logs/log_analysis/log_analysis_setup_state'; -import { InitialConfigurationStep } from './initial_configuration_step'; -import { ProcessStep } from './process_step'; -import { - ModuleDescriptor, - ModuleSourceConfiguration, -} from '../../../../containers/logs/log_analysis'; - -type SetupHandler = ( - indices: string[], - startTime: number | undefined, - endTime: number | undefined -) => void; - -interface LogEntryRateSetupStepsProps { - cleanupAndSetup: SetupHandler; - errorMessages: string[]; - setup: SetupHandler; - setupStatus: SetupStatus; - viewResults: () => void; - moduleDescriptor: ModuleDescriptor; - sourceConfiguration: ModuleSourceConfiguration; -} - -export const LogEntryRateSetupSteps = ({ - cleanupAndSetup: cleanupAndSetupModule, - errorMessages, - setup: setupModule, - setupStatus, - viewResults, - moduleDescriptor, - sourceConfiguration, -}: LogEntryRateSetupStepsProps) => { - const { - setup, - cleanupAndSetup, - setStartTime, - setEndTime, - startTime, - endTime, - isValidating, - validationErrors, - validatedIndices, - setValidatedIndices, - } = useAnalysisSetupState({ - cleanupAndSetupModule, - moduleDescriptor, - setupModule, - sourceConfiguration, - }); - - const steps = [ - { - title: i18n.translate('xpack.infra.analysisSetup.configurationStepTitle', { - defaultMessage: 'Configuration', - }), - children: ( - - ), - }, - { - title: i18n.translate('xpack.infra.analysisSetup.actionStepTitle', { - defaultMessage: 'Create ML job', - }), - children: ( - - ), - status: - setupStatus === 'pending' - ? ('incomplete' as EuiStepStatus) - : setupStatus === 'failed' - ? ('danger' as EuiStepStatus) - : setupStatus === 'succeeded' - ? ('complete' as EuiStepStatus) - : undefined, - }, - ]; - - return ; -}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_setup.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_setup.tsx new file mode 100644 index 0000000000000..3595b6bf830fc --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_setup.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useAnalysisSetupState } from '../../../containers/logs/log_analysis'; +import { useLogEntryRateModuleContext } from './use_log_entry_rate_module'; + +export const useLogEntryRateSetup = () => { + const { + cleanUpAndSetUpModule, + lastSetupErrorMessages, + moduleDescriptor, + setUpModule, + setupStatus, + sourceConfiguration, + viewResults, + } = useLogEntryRateModuleContext(); + + const { + cleanUpAndSetUp, + endTime, + isValidating, + setEndTime, + setStartTime, + setValidatedIndices, + setUp, + startTime, + validatedIndices, + validationErrors, + } = useAnalysisSetupState({ + cleanUpAndSetUpModule, + moduleDescriptor, + setUpModule, + sourceConfiguration, + }); + + return { + cleanUpAndSetUp, + endTime, + isValidating, + lastSetupErrorMessages, + setEndTime, + setStartTime, + setValidatedIndices, + setUp, + setupStatus, + startTime, + validatedIndices, + validationErrors, + viewResults, + }; +}; diff --git a/x-pack/legacy/plugins/infra/public/utils/convert_interval_to_string.ts b/x-pack/legacy/plugins/infra/public/utils/convert_interval_to_string.ts new file mode 100644 index 0000000000000..a125360235755 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/utils/convert_interval_to_string.ts @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import dateMath from '@elastic/datemath'; +import moment from 'moment'; +import { i18n } from '@kbn/i18n'; +import * as rt from 'io-ts'; + +export const INTERVAL_STRING_RE = new RegExp(`^([\\d\\.]+)\\s*(${dateMath.units.join('|')})$`); + +export const parseInterval = (intervalString: string) => { + if (intervalString) { + const matches = intervalString.match(INTERVAL_STRING_RE); + if (matches) { + const value = Number(matches[1]); + const unit = matches[2]; + return { value, unit }; + } + } + throw new Error( + i18n.translate('xpack.infra.parseInterval.errorMessage', { + defaultMessage: '{value} is not an interval string', + values: { + value: intervalString, + }, + }) + ); +}; + +const ValidUnitRT = rt.keyof({ + seconds: null, + minutes: null, + hours: null, + days: null, + weeks: null, + months: null, + years: null, +}); +type ValidUnit = rt.TypeOf; +const UNITS = ['seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'] as ValidUnit[]; + +const DISPLAY_STRINGS_FOR_UNITS_PLURAL = { + seconds: i18n.translate('xpack.infra.durationUnits.seconds.plural', { + defaultMessage: 'seconds', + }), + minutes: i18n.translate('xpack.infra.durationUnits.minutes.plural', { + defaultMessage: 'minutes', + }), + hours: i18n.translate('xpack.infra.durationUnits.hours.plural', { + defaultMessage: 'hours', + }), + days: i18n.translate('xpack.infra.durationUnits.days.plural', { + defaultMessage: 'days', + }), + weeks: i18n.translate('xpack.infra.durationUnits.weeks.plural', { + defaultMessage: 'weeks', + }), + months: i18n.translate('xpack.infra.durationUnits.months.plural', { + defaultMessage: 'months', + }), + years: i18n.translate('xpack.infra.durationUnits.years.plural', { + defaultMessage: 'years', + }), +}; + +const DISPLAY_STRINGS_FOR_UNITS_SINGULAR = { + seconds: i18n.translate('xpack.infra.durationUnits.seconds.singular', { + defaultMessage: 'second', + }), + minutes: i18n.translate('xpack.infra.durationUnits.minutes.singular', { + defaultMessage: 'minute', + }), + hours: i18n.translate('xpack.infra.durationUnits.hours.singular', { + defaultMessage: 'hour', + }), + days: i18n.translate('xpack.infra.durationUnits.days.singular', { + defaultMessage: 'day', + }), + weeks: i18n.translate('xpack.infra.durationUnits.weeks.singular', { + defaultMessage: 'week', + }), + months: i18n.translate('xpack.infra.durationUnits.months.singular', { + defaultMessage: 'month', + }), + years: i18n.translate('xpack.infra.durationUnits.years.singular', { + defaultMessage: 'year', + }), +}; + +const getDisplayableUnit = (value: number, unit: ValidUnit) => { + return Math.floor(value) === 1 + ? DISPLAY_STRINGS_FOR_UNITS_SINGULAR[unit] + : DISPLAY_STRINGS_FOR_UNITS_PLURAL[unit]; +}; + +export const convertIntervalToString = (input: string) => { + const interval = parseInterval(input); + if (interval?.unit === 's') { + const duration = moment.duration(interval.value, interval.unit); + const targetUnit = UNITS.reduce((answer, unit) => { + if (duration.as(unit) >= 1) { + return unit; + } + return answer; + }, 'seconds'); + const durationAsUnit = duration.as(targetUnit); + return `${Math.floor(durationAsUnit)} ${getDisplayableUnit(durationAsUnit, targetUnit)}`; + } +}; diff --git a/x-pack/legacy/plugins/infra/public/utils/fixtures/metrics_explorer.ts b/x-pack/legacy/plugins/infra/public/utils/fixtures/metrics_explorer.ts index d553f03c4ed34..3d5198f2d379b 100644 --- a/x-pack/legacy/plugins/infra/public/utils/fixtures/metrics_explorer.ts +++ b/x-pack/legacy/plugins/infra/public/utils/fixtures/metrics_explorer.ts @@ -5,10 +5,8 @@ */ import { - MetricsExplorerAggregation, MetricsExplorerResponse, MetricsExplorerSeries, - MetricsExplorerColumnType, } from '../../../server/routes/metrics_explorer/types'; import { MetricsExplorerOptions, @@ -21,8 +19,8 @@ import { export const options: MetricsExplorerOptions = { limit: 3, groupBy: 'host.name', - aggregation: MetricsExplorerAggregation.avg, - metrics: [{ aggregation: MetricsExplorerAggregation.avg, field: 'system.cpu.user.pct' }], + aggregation: 'avg', + metrics: [{ aggregation: 'avg', field: 'system.cpu.user.pct' }], }; export const source = { @@ -58,9 +56,9 @@ export const timeRange: MetricsExplorerTimeOptions = { export const createSeries = (id: string): MetricsExplorerSeries => ({ id, columns: [ - { name: 'timestamp', type: MetricsExplorerColumnType.date }, - { name: 'metric_0', type: MetricsExplorerColumnType.number }, - { name: 'groupBy', type: MetricsExplorerColumnType.string }, + { name: 'timestamp', type: 'date' }, + { name: 'metric_0', type: 'number' }, + { name: 'groupBy', type: 'string' }, ], rows: [ { timestamp: 1, metric_0: 0.5, groupBy: id }, diff --git a/x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts b/x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts index b97b482d8424d..a18ffea3cfd28 100644 --- a/x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts +++ b/x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts @@ -21,7 +21,6 @@ import { } from '../../graphql/types'; import { InfraLogEntriesDomain } from '../../lib/domains/log_entries_domain'; import { SourceConfigurationRuntimeType } from '../../lib/sources'; -import { UsageCollector } from '../../usage/usage_collector'; import { parseFilterQuery } from '../../utils/serialized_query'; import { ChildResolverOf, InfraResolverOf } from '../../utils/typed_resolvers'; import { QuerySourceResolver } from '../sources/resolvers'; @@ -41,16 +40,6 @@ export type InfraSourceLogEntryHighlightsResolver = ChildResolverOf< QuerySourceResolver >; -export type InfraSourceLogSummaryBetweenResolver = ChildResolverOf< - InfraResolverOf, - QuerySourceResolver ->; - -export type InfraSourceLogSummaryHighlightsBetweenResolver = ChildResolverOf< - InfraResolverOf, - QuerySourceResolver ->; - export type InfraSourceLogItem = ChildResolverOf< InfraResolverOf, QuerySourceResolver @@ -63,8 +52,6 @@ export const createLogEntriesResolvers = (libs: { logEntriesAround: InfraSourceLogEntriesAroundResolver; logEntriesBetween: InfraSourceLogEntriesBetweenResolver; logEntryHighlights: InfraSourceLogEntryHighlightsResolver; - logSummaryBetween: InfraSourceLogSummaryBetweenResolver; - logSummaryHighlightsBetween: InfraSourceLogSummaryHighlightsBetweenResolver; logItem: InfraSourceLogItem; }; InfraLogEntryColumn: { @@ -150,40 +137,6 @@ export const createLogEntriesResolvers = (libs: { entries, })); }, - async logSummaryBetween(source, args, { req }) { - UsageCollector.countLogs(); - const buckets = await libs.logEntries.getLogSummaryBucketsBetween( - req, - source.id, - args.start, - args.end, - args.bucketSize, - parseFilterQuery(args.filterQuery) - ); - - return { - start: buckets.length > 0 ? buckets[0].start : null, - end: buckets.length > 0 ? buckets[buckets.length - 1].end : null, - buckets, - }; - }, - async logSummaryHighlightsBetween(source, args, { req }) { - const summaryHighlightSets = await libs.logEntries.getLogSummaryHighlightBucketsBetween( - req, - source.id, - args.start, - args.end, - args.bucketSize, - args.highlightQueries.filter(highlightQuery => !!highlightQuery), - parseFilterQuery(args.filterQuery) - ); - - return summaryHighlightSets.map(buckets => ({ - start: buckets.length > 0 ? buckets[0].start : null, - end: buckets.length > 0 ? buckets[buckets.length - 1].end : null, - buckets, - })); - }, async logItem(source, args, { req }) { const sourceConfiguration = pipe( SourceConfigurationRuntimeType.decode(source.configuration), diff --git a/x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts b/x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts index 0ef896e8edfa4..4681ce5a49aa9 100644 --- a/x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts +++ b/x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts @@ -82,28 +82,6 @@ export const logEntriesSchema = gql` countAfter: Int! } - "A log summary bucket" - type InfraLogSummaryBucket { - "The start timestamp of the bucket" - start: Float! - "The end timestamp of the bucket" - end: Float! - "The number of entries inside the bucket" - entriesCount: Int! - } - - "A log summary highlight bucket" - type InfraLogSummaryHighlightBucket { - "The start timestamp of the bucket" - start: Float! - "The end timestamp of the bucket" - end: Float! - "The number of highlighted entries inside the bucket" - entriesCount: Int! - "The time key of a representative of the highlighted log entries in this bucket" - representativeKey: InfraTimeKey! - } - "A consecutive sequence of log entries" type InfraLogEntryInterval { "The key corresponding to the start of the interval covered by the entries" @@ -122,32 +100,6 @@ export const logEntriesSchema = gql` entries: [InfraLogEntry!]! } - "A consecutive sequence of log summary buckets" - type InfraLogSummaryInterval { - "The millisecond timestamp corresponding to the start of the interval covered by the summary" - start: Float - "The millisecond timestamp corresponding to the end of the interval covered by the summary" - end: Float - "The query the log entries were filtered by" - filterQuery: String - "A list of the log entries" - buckets: [InfraLogSummaryBucket!]! - } - - "A consecutive sequence of log summary highlight buckets" - type InfraLogSummaryHighlightInterval { - "The millisecond timestamp corresponding to the start of the interval covered by the summary" - start: Float - "The millisecond timestamp corresponding to the end of the interval covered by the summary" - end: Float - "The query the log entries were filtered by" - filterQuery: String - "The query the log entries were highlighted with" - highlightQuery: String - "A list of the log entries" - buckets: [InfraLogSummaryHighlightBucket!]! - } - type InfraLogItemField { "The flattened field name" field: String! @@ -198,30 +150,6 @@ export const logEntriesSchema = gql` "The highlighting to apply to the log entries" highlights: [InfraLogEntryHighlightInput!]! ): [InfraLogEntryInterval!]! - "A consecutive span of summary buckets within an interval" - logSummaryBetween( - "The millisecond timestamp that corresponds to the start of the interval" - start: Float! - "The millisecond timestamp that corresponds to the end of the interval" - end: Float! - "The size of each bucket in milliseconds" - bucketSize: Float! - "The query to filter the log entries by" - filterQuery: String - ): InfraLogSummaryInterval! - "Spans of summary highlight buckets within an interval" - logSummaryHighlightsBetween( - "The millisecond timestamp that corresponds to the start of the interval" - start: Float! - "The millisecond timestamp that corresponds to the end of the interval" - end: Float! - "The size of each bucket in milliseconds" - bucketSize: Float! - "The query to filter the log entries by" - filterQuery: String - "The highlighting to apply to the log entries" - highlightQueries: [String!]! - ): [InfraLogSummaryHighlightInterval!]! logItem(id: ID!): InfraLogItem! } `; diff --git a/x-pack/legacy/plugins/infra/server/graphql/types.ts b/x-pack/legacy/plugins/infra/server/graphql/types.ts index 88ad5b2f58f23..bb27cc4e21b4e 100644 --- a/x-pack/legacy/plugins/infra/server/graphql/types.ts +++ b/x-pack/legacy/plugins/infra/server/graphql/types.ts @@ -62,10 +62,6 @@ export interface InfraSource { logEntriesBetween: InfraLogEntryInterval; /** Sequences of log entries matching sets of highlighting queries within an interval */ logEntryHighlights: InfraLogEntryInterval[]; - /** A consecutive span of summary buckets within an interval */ - logSummaryBetween: InfraLogSummaryInterval; - /** Spans of summary highlight buckets within an interval */ - logSummaryHighlightsBetween: InfraLogSummaryHighlightInterval[]; logItem: InfraLogItem; /** A snapshot of nodes */ @@ -236,50 +232,6 @@ export interface InfraLogEntryFieldColumn { /** A list of highlighted substrings of the value */ highlights: string[]; } -/** A consecutive sequence of log summary buckets */ -export interface InfraLogSummaryInterval { - /** The millisecond timestamp corresponding to the start of the interval covered by the summary */ - start?: number | null; - /** The millisecond timestamp corresponding to the end of the interval covered by the summary */ - end?: number | null; - /** The query the log entries were filtered by */ - filterQuery?: string | null; - /** A list of the log entries */ - buckets: InfraLogSummaryBucket[]; -} -/** A log summary bucket */ -export interface InfraLogSummaryBucket { - /** The start timestamp of the bucket */ - start: number; - /** The end timestamp of the bucket */ - end: number; - /** The number of entries inside the bucket */ - entriesCount: number; -} -/** A consecutive sequence of log summary highlight buckets */ -export interface InfraLogSummaryHighlightInterval { - /** The millisecond timestamp corresponding to the start of the interval covered by the summary */ - start?: number | null; - /** The millisecond timestamp corresponding to the end of the interval covered by the summary */ - end?: number | null; - /** The query the log entries were filtered by */ - filterQuery?: string | null; - /** The query the log entries were highlighted with */ - highlightQuery?: string | null; - /** A list of the log entries */ - buckets: InfraLogSummaryHighlightBucket[]; -} -/** A log summary highlight bucket */ -export interface InfraLogSummaryHighlightBucket { - /** The start timestamp of the bucket */ - start: number; - /** The end timestamp of the bucket */ - end: number; - /** The number of highlighted entries inside the bucket */ - entriesCount: number; - /** The time key of a representative of the highlighted log entries in this bucket */ - representativeKey: InfraTimeKey; -} export interface InfraLogItem { /** The ID of the document */ @@ -500,28 +452,6 @@ export interface LogEntryHighlightsInfraSourceArgs { /** The highlighting to apply to the log entries */ highlights: InfraLogEntryHighlightInput[]; } -export interface LogSummaryBetweenInfraSourceArgs { - /** The millisecond timestamp that corresponds to the start of the interval */ - start: number; - /** The millisecond timestamp that corresponds to the end of the interval */ - end: number; - /** The size of each bucket in milliseconds */ - bucketSize: number; - /** The query to filter the log entries by */ - filterQuery?: string | null; -} -export interface LogSummaryHighlightsBetweenInfraSourceArgs { - /** The millisecond timestamp that corresponds to the start of the interval */ - start: number; - /** The millisecond timestamp that corresponds to the end of the interval */ - end: number; - /** The size of each bucket in milliseconds */ - bucketSize: number; - /** The query to filter the log entries by */ - filterQuery?: string | null; - /** The highlighting to apply to the log entries */ - highlightQueries: string[]; -} export interface LogItemInfraSourceArgs { id: string; } @@ -605,7 +535,7 @@ export enum InfraSnapshotMetricType { rdsQueriesExecuted = 'rdsQueriesExecuted', rdsActiveTransactions = 'rdsActiveTransactions', rdsLatency = 'rdsLatency', - sqsMessagesVisible = 'sqsOldestMessage', + sqsMessagesVisible = 'sqsMessagesVisible', sqsMessagesDelayed = 'sqsMessagesDelayed', sqsMessagesSent = 'sqsMessagesSent', sqsMessagesEmpty = 'sqsMessagesEmpty', @@ -744,14 +674,6 @@ export namespace InfraSourceResolvers { logEntriesBetween?: LogEntriesBetweenResolver; /** Sequences of log entries matching sets of highlighting queries within an interval */ logEntryHighlights?: LogEntryHighlightsResolver; - /** A consecutive span of summary buckets within an interval */ - logSummaryBetween?: LogSummaryBetweenResolver; - /** Spans of summary highlight buckets within an interval */ - logSummaryHighlightsBetween?: LogSummaryHighlightsBetweenResolver< - InfraLogSummaryHighlightInterval[], - TypeParent, - Context - >; logItem?: LogItemResolver; /** A snapshot of nodes */ @@ -836,40 +758,6 @@ export namespace InfraSourceResolvers { highlights: InfraLogEntryHighlightInput[]; } - export type LogSummaryBetweenResolver< - R = InfraLogSummaryInterval, - Parent = InfraSource, - Context = InfraContext - > = Resolver; - export interface LogSummaryBetweenArgs { - /** The millisecond timestamp that corresponds to the start of the interval */ - start: number; - /** The millisecond timestamp that corresponds to the end of the interval */ - end: number; - /** The size of each bucket in milliseconds */ - bucketSize: number; - /** The query to filter the log entries by */ - filterQuery?: string | null; - } - - export type LogSummaryHighlightsBetweenResolver< - R = InfraLogSummaryHighlightInterval[], - Parent = InfraSource, - Context = InfraContext - > = Resolver; - export interface LogSummaryHighlightsBetweenArgs { - /** The millisecond timestamp that corresponds to the start of the interval */ - start: number; - /** The millisecond timestamp that corresponds to the end of the interval */ - end: number; - /** The size of each bucket in milliseconds */ - bucketSize: number; - /** The query to filter the log entries by */ - filterQuery?: string | null; - /** The highlighting to apply to the log entries */ - highlightQueries: string[]; - } - export type LogItemResolver< R = InfraLogItem, Parent = InfraSource, @@ -1422,145 +1310,6 @@ export namespace InfraLogEntryFieldColumnResolvers { Context = InfraContext > = Resolver; } -/** A consecutive sequence of log summary buckets */ -export namespace InfraLogSummaryIntervalResolvers { - export interface Resolvers { - /** The millisecond timestamp corresponding to the start of the interval covered by the summary */ - start?: StartResolver; - /** The millisecond timestamp corresponding to the end of the interval covered by the summary */ - end?: EndResolver; - /** The query the log entries were filtered by */ - filterQuery?: FilterQueryResolver; - /** A list of the log entries */ - buckets?: BucketsResolver; - } - - export type StartResolver< - R = number | null, - Parent = InfraLogSummaryInterval, - Context = InfraContext - > = Resolver; - export type EndResolver< - R = number | null, - Parent = InfraLogSummaryInterval, - Context = InfraContext - > = Resolver; - export type FilterQueryResolver< - R = string | null, - Parent = InfraLogSummaryInterval, - Context = InfraContext - > = Resolver; - export type BucketsResolver< - R = InfraLogSummaryBucket[], - Parent = InfraLogSummaryInterval, - Context = InfraContext - > = Resolver; -} -/** A log summary bucket */ -export namespace InfraLogSummaryBucketResolvers { - export interface Resolvers { - /** The start timestamp of the bucket */ - start?: StartResolver; - /** The end timestamp of the bucket */ - end?: EndResolver; - /** The number of entries inside the bucket */ - entriesCount?: EntriesCountResolver; - } - - export type StartResolver< - R = number, - Parent = InfraLogSummaryBucket, - Context = InfraContext - > = Resolver; - export type EndResolver< - R = number, - Parent = InfraLogSummaryBucket, - Context = InfraContext - > = Resolver; - export type EntriesCountResolver< - R = number, - Parent = InfraLogSummaryBucket, - Context = InfraContext - > = Resolver; -} -/** A consecutive sequence of log summary highlight buckets */ -export namespace InfraLogSummaryHighlightIntervalResolvers { - export interface Resolvers< - Context = InfraContext, - TypeParent = InfraLogSummaryHighlightInterval - > { - /** The millisecond timestamp corresponding to the start of the interval covered by the summary */ - start?: StartResolver; - /** The millisecond timestamp corresponding to the end of the interval covered by the summary */ - end?: EndResolver; - /** The query the log entries were filtered by */ - filterQuery?: FilterQueryResolver; - /** The query the log entries were highlighted with */ - highlightQuery?: HighlightQueryResolver; - /** A list of the log entries */ - buckets?: BucketsResolver; - } - - export type StartResolver< - R = number | null, - Parent = InfraLogSummaryHighlightInterval, - Context = InfraContext - > = Resolver; - export type EndResolver< - R = number | null, - Parent = InfraLogSummaryHighlightInterval, - Context = InfraContext - > = Resolver; - export type FilterQueryResolver< - R = string | null, - Parent = InfraLogSummaryHighlightInterval, - Context = InfraContext - > = Resolver; - export type HighlightQueryResolver< - R = string | null, - Parent = InfraLogSummaryHighlightInterval, - Context = InfraContext - > = Resolver; - export type BucketsResolver< - R = InfraLogSummaryHighlightBucket[], - Parent = InfraLogSummaryHighlightInterval, - Context = InfraContext - > = Resolver; -} -/** A log summary highlight bucket */ -export namespace InfraLogSummaryHighlightBucketResolvers { - export interface Resolvers { - /** The start timestamp of the bucket */ - start?: StartResolver; - /** The end timestamp of the bucket */ - end?: EndResolver; - /** The number of highlighted entries inside the bucket */ - entriesCount?: EntriesCountResolver; - /** The time key of a representative of the highlighted log entries in this bucket */ - representativeKey?: RepresentativeKeyResolver; - } - - export type StartResolver< - R = number, - Parent = InfraLogSummaryHighlightBucket, - Context = InfraContext - > = Resolver; - export type EndResolver< - R = number, - Parent = InfraLogSummaryHighlightBucket, - Context = InfraContext - > = Resolver; - export type EntriesCountResolver< - R = number, - Parent = InfraLogSummaryHighlightBucket, - Context = InfraContext - > = Resolver; - export type RepresentativeKeyResolver< - R = InfraTimeKey, - Parent = InfraLogSummaryHighlightBucket, - Context = InfraContext - > = Resolver; -} export namespace InfraLogItemResolvers { export interface Resolvers { diff --git a/x-pack/legacy/plugins/infra/server/infra_server.ts b/x-pack/legacy/plugins/infra/server/infra_server.ts index fdbe0f86ded4f..f5a4bf8e8e054 100644 --- a/x-pack/legacy/plugins/infra/server/infra_server.ts +++ b/x-pack/legacy/plugins/infra/server/infra_server.ts @@ -19,6 +19,10 @@ import { initMetricExplorerRoute } from './routes/metrics_explorer'; import { initMetadataRoute } from './routes/metadata'; import { initSnapshotRoute } from './routes/snapshot'; import { initNodeDetailsRoute } from './routes/node_details'; +import { + initLogEntriesSummaryRoute, + initLogEntriesSummaryHighlightsRoute, +} from './routes/log_entries'; import { initInventoryMetaRoute } from './routes/inventory_metadata'; export const initInfraServer = (libs: InfraBackendLibs) => { @@ -38,6 +42,8 @@ export const initInfraServer = (libs: InfraBackendLibs) => { initSnapshotRoute(libs); initNodeDetailsRoute(libs); initValidateLogAnalysisIndicesRoute(libs); + initLogEntriesSummaryRoute(libs); + initLogEntriesSummaryHighlightsRoute(libs); initMetricExplorerRoute(libs); initMetadataRoute(libs); initInventoryMetaRoute(libs); diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts index a0fd6d3e951bf..28d5f75c3fdaf 100644 --- a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts @@ -9,19 +9,21 @@ import { Lifecycle } from 'hapi'; import { ObjectType } from '@kbn/config-schema'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { RouteMethod, RouteConfig } from '../../../../../../../../src/core/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../../plugins/features/server'; +import { SpacesPluginSetup } from '../../../../../../../plugins/spaces/server'; import { APMPluginContract } from '../../../../../../../plugins/apm/server'; // NP_TODO: Compose real types from plugins we depend on, no "any" export interface InfraServerPluginDeps { + spaces: SpacesPluginSetup; usageCollection: UsageCollectionSetup; - spaces: any; metrics: { getVisData: any; }; indexPatterns: { indexPatternsServiceFactory: any; }; - features: any; + features: FeaturesPluginSetup; apm: APMPluginContract; ___legacy: any; } diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/legacy/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts index 19121d92f02c9..b0cdb8389cb29 100644 --- a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/legacy/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts @@ -147,10 +147,6 @@ export class KibanaFramework { headers, body: errorBody, }); - - // NP_TODO: Do we still need to re-throw this error in this case? if we do, can we - // still call the response.customError method to control the HTTP response? - // throw error; } } this.router.post(routeOptions, handler); @@ -250,7 +246,7 @@ export class KibanaFramework { } } - // NP_TODO: This method needs to no longer require full KibanaRequest + // NP_TODO: [TSVB_GROUP] This method needs fixing when the metrics plugin has migrated to the New Platform public async makeTSVBRequest( request: KibanaRequest, model: TSVBMetricModel, diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/adapter_types.ts b/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/adapter_types.ts index acd7a2528bb42..844eaf7604927 100644 --- a/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/adapter_types.ts +++ b/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/adapter_types.ts @@ -28,7 +28,7 @@ export interface InfraMetricsAdapter { getMetrics( requestContext: RequestHandlerContext, options: InfraMetricsRequestOptions, - request: KibanaRequest // NP_TODO: temporarily needed until metrics getVisData no longer needs full request + request: KibanaRequest ): Promise; } diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts b/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts index c4146f5758d80..c1b567576c214 100644 --- a/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts +++ b/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts @@ -25,7 +25,7 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter { public async getMetrics( requestContext: RequestHandlerContext, options: InfraMetricsRequestOptions, - rawRequest: KibanaRequest // NP_TODO: Temporarily needed until metrics getVisData no longer needs full request + rawRequest: KibanaRequest ): Promise { const indexPattern = `${options.sourceConfiguration.metricAlias},${options.sourceConfiguration.logAlias}`; const fields = findInventoryFields(options.nodeType, options.sourceConfiguration.fields); diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts index 597073b1e901f..e3f9d8cef0430 100644 --- a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts +++ b/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts @@ -11,12 +11,10 @@ import { RequestHandlerContext } from 'src/core/server'; import { TimeKey } from '../../../../common/time'; import { JsonObject } from '../../../../common/typed_json'; import { - InfraLogEntry, - InfraLogItem, - InfraLogMessageSegment, - InfraLogSummaryBucket, - InfraLogSummaryHighlightBucket, -} from '../../../graphql/types'; + LogEntriesSummaryBucket, + LogEntriesSummaryHighlightsBucket, +} from '../../../../common/http_api'; +import { InfraLogEntry, InfraLogItem, InfraLogMessageSegment } from '../../../graphql/types'; import { InfraSourceConfiguration, InfraSources, @@ -218,7 +216,7 @@ export class InfraLogEntriesDomain { end: number, bucketSize: number, filterQuery?: LogEntryQuery - ): Promise { + ): Promise { const { configuration } = await this.libs.sources.getSourceConfiguration( requestContext, sourceId @@ -242,7 +240,7 @@ export class InfraLogEntriesDomain { bucketSize: number, highlightQueries: string[], filterQuery?: LogEntryQuery - ): Promise { + ): Promise { const { configuration } = await this.libs.sources.getSourceConfiguration( requestContext, sourceId @@ -402,7 +400,7 @@ const logSummaryBucketHasEntries = (bucket: LogSummaryBucket) => const convertLogSummaryBucketToSummaryHighlightBucket = ( bucket: LogSummaryBucket -): InfraLogSummaryHighlightBucket => ({ +): LogEntriesSummaryHighlightsBucket => ({ entriesCount: bucket.entriesCount, start: bucket.start, end: bucket.end, diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/metrics_domain.ts b/x-pack/legacy/plugins/infra/server/lib/domains/metrics_domain.ts index 5d7d54a6a2e50..e53e45afae5c4 100644 --- a/x-pack/legacy/plugins/infra/server/lib/domains/metrics_domain.ts +++ b/x-pack/legacy/plugins/infra/server/lib/domains/metrics_domain.ts @@ -18,7 +18,7 @@ export class InfraMetricsDomain { public async getMetrics( requestContext: RequestHandlerContext, options: InfraMetricsRequestOptions, - rawRequest: KibanaRequest // NP_TODO: temporarily needed until metrics getVisData no longer needs full request + rawRequest: KibanaRequest ): Promise { return await this.adapter.getMetrics(requestContext, options, rawRequest); } diff --git a/x-pack/legacy/plugins/infra/server/lib/snapshot/snapshot.ts b/x-pack/legacy/plugins/infra/server/lib/snapshot/snapshot.ts index d1db0ef07b338..d5e10ecdce486 100644 --- a/x-pack/legacy/plugins/infra/server/lib/snapshot/snapshot.ts +++ b/x-pack/legacy/plugins/infra/server/lib/snapshot/snapshot.ts @@ -37,7 +37,7 @@ export class InfraSnapshot { public async getNodes( requestContext: RequestHandlerContext, options: InfraSnapshotRequestOptions - ): Promise { + ): Promise<{ nodes: InfraSnapshotNode[]; interval: string }> { // Both requestGroupedNodes and requestNodeMetrics may send several requests to elasticsearch // in order to page through the results of their respective composite aggregations. // Both chains of requests are supposed to run in parallel, and their results be merged @@ -61,7 +61,10 @@ export class InfraSnapshot { const groupedNodeBuckets = await groupedNodesPromise; const nodeMetricBuckets = await nodeMetricsPromise; - return mergeNodeBuckets(groupedNodeBuckets, nodeMetricBuckets, options); + return { + nodes: mergeNodeBuckets(groupedNodeBuckets, nodeMetricBuckets, options), + interval: timeRangeWithIntervalApplied.interval, + }; } } diff --git a/x-pack/legacy/plugins/infra/server/new_platform_index.ts b/x-pack/legacy/plugins/infra/server/new_platform_index.ts index 6b759ecfe9fde..e59897a6b241d 100644 --- a/x-pack/legacy/plugins/infra/server/new_platform_index.ts +++ b/x-pack/legacy/plugins/infra/server/new_platform_index.ts @@ -5,11 +5,10 @@ */ import { PluginInitializerContext } from 'src/core/server'; -import { InfraServerPlugin } from './new_platform_plugin'; +import { InfraServerPlugin, InfraPluginSetup } from './new_platform_plugin'; import { config, InfraConfig } from '../../../../plugins/infra/server'; import { InfraServerPluginDeps } from './lib/adapters/framework'; - -export { config, InfraConfig, InfraServerPluginDeps }; +export { config, InfraConfig, InfraServerPluginDeps, InfraPluginSetup }; export function plugin(context: PluginInitializerContext) { return new InfraServerPlugin(context); diff --git a/x-pack/legacy/plugins/infra/server/new_platform_plugin.ts b/x-pack/legacy/plugins/infra/server/new_platform_plugin.ts index 462a07574b2dd..ad26663402adc 100644 --- a/x-pack/legacy/plugins/infra/server/new_platform_plugin.ts +++ b/x-pack/legacy/plugins/infra/server/new_platform_plugin.ts @@ -23,11 +23,19 @@ import { InfraSources } from './lib/sources'; import { InfraServerPluginDeps } from './lib/adapters/framework'; import { METRICS_FEATURE, LOGS_FEATURE } from './features'; import { UsageCollector } from './usage/usage_collector'; +import { InfraStaticSourceConfiguration } from './lib/sources/types'; export interface KbnServer extends Server { usage: any; } +export interface InfraPluginSetup { + defineInternalSourceConfiguration: ( + sourceId: string, + sourceProperties: InfraStaticSourceConfiguration + ) => void; +} + const DEFAULT_CONFIG: InfraConfig = { enabled: true, query: { @@ -103,5 +111,11 @@ export class InfraServerPlugin { // Telemetry UsageCollector.registerUsageCollector(plugins.usageCollection); + + return { + defineInternalSourceConfiguration(sourceId, sourceProperties) { + sources.defineInternalSourceConfiguration(sourceId, sourceProperties); + }, + } as InfraPluginSetup; } } diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts index 02866e797e305..9778311bd8e58 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts @@ -33,12 +33,12 @@ export const initGetLogEntryRateRoute = ({ framework, logAnalysis }: InfraBacken }, }, async (requestContext, request, response) => { - const payload = pipe( - getLogEntryRateRequestPayloadRT.decode(request.body), - fold(throwErrors(Boom.badRequest), identity) - ); - try { + const payload = pipe( + getLogEntryRateRequestPayloadRT.decode(request.body), + fold(throwErrors(Boom.badRequest), identity) + ); + const logEntryRateBuckets = await logAnalysis.getLogEntryRateBuckets( requestContext, payload.data.sourceId, diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/validation/indices.ts b/x-pack/legacy/plugins/infra/server/routes/log_analysis/validation/indices.ts index ba143a597b66d..fe579124cfe10 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_analysis/validation/indices.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_analysis/validation/indices.ts @@ -42,8 +42,10 @@ export const initValidateLogAnalysisIndicesRoute = ({ framework }: InfraBackendL await Promise.all( indices.map(async index => { const fieldCaps = await framework.callWithRequest(requestContext, 'fieldCaps', { - index, + allow_no_indices: true, fields: fields.map(field => field.name), + ignore_unavailable: true, + index, }); if (fieldCaps.indices.length === 0) { diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/index.ts b/x-pack/legacy/plugins/infra/server/routes/log_entries/index.ts new file mode 100644 index 0000000000000..ee2d150fdaac0 --- /dev/null +++ b/x-pack/legacy/plugins/infra/server/routes/log_entries/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; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './summary'; +export * from './summary_highlights'; diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/summary.ts b/x-pack/legacy/plugins/infra/server/routes/log_entries/summary.ts new file mode 100644 index 0000000000000..05643adbe781f --- /dev/null +++ b/x-pack/legacy/plugins/infra/server/routes/log_entries/summary.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; + +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { schema } from '@kbn/config-schema'; + +import { throwErrors } from '../../../common/runtime_types'; + +import { InfraBackendLibs } from '../../lib/infra_types'; +import { + LOG_ENTRIES_SUMMARY_PATH, + logEntriesSummaryRequestRT, + logEntriesSummaryResponseRT, +} from '../../../common/http_api/log_entries'; +import { parseFilterQuery } from '../../utils/serialized_query'; + +const escapeHatch = schema.object({}, { allowUnknowns: true }); + +export const initLogEntriesSummaryRoute = ({ framework, logEntries }: InfraBackendLibs) => { + framework.registerRoute( + { + method: 'post', + path: LOG_ENTRIES_SUMMARY_PATH, + validate: { body: escapeHatch }, + }, + async (requestContext, request, response) => { + try { + const payload = pipe( + logEntriesSummaryRequestRT.decode(request.body), + fold(throwErrors(Boom.badRequest), identity) + ); + const { sourceId, startDate, endDate, bucketSize, query } = payload; + + const buckets = await logEntries.getLogSummaryBucketsBetween( + requestContext, + sourceId, + startDate, + endDate, + bucketSize, + parseFilterQuery(query) + ); + + return response.ok({ + body: logEntriesSummaryResponseRT.encode({ + data: { + start: startDate, + end: endDate, + buckets, + }, + }), + }); + } catch (error) { + return response.internalError({ + body: error.message, + }); + } + } + ); +}; diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/summary_highlights.ts b/x-pack/legacy/plugins/infra/server/routes/log_entries/summary_highlights.ts new file mode 100644 index 0000000000000..ecccd931bb371 --- /dev/null +++ b/x-pack/legacy/plugins/infra/server/routes/log_entries/summary_highlights.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; + +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { schema } from '@kbn/config-schema'; + +import { throwErrors } from '../../../common/runtime_types'; + +import { InfraBackendLibs } from '../../lib/infra_types'; +import { + LOG_ENTRIES_SUMMARY_HIGHLIGHTS_PATH, + logEntriesSummaryHighlightsRequestRT, + logEntriesSummaryHighlightsResponseRT, +} from '../../../common/http_api/log_entries'; +import { parseFilterQuery } from '../../utils/serialized_query'; + +const escapeHatch = schema.object({}, { allowUnknowns: true }); + +export const initLogEntriesSummaryHighlightsRoute = ({ + framework, + logEntries, +}: InfraBackendLibs) => { + framework.registerRoute( + { + method: 'post', + path: LOG_ENTRIES_SUMMARY_HIGHLIGHTS_PATH, + validate: { body: escapeHatch }, + }, + async (requestContext, request, response) => { + try { + const payload = pipe( + logEntriesSummaryHighlightsRequestRT.decode(request.body), + fold(throwErrors(Boom.badRequest), identity) + ); + const { sourceId, startDate, endDate, bucketSize, query, highlightTerms } = payload; + + const bucketsPerHighlightTerm = await logEntries.getLogSummaryHighlightBucketsBetween( + requestContext, + sourceId, + startDate, + endDate, + bucketSize, + highlightTerms, + parseFilterQuery(query) + ); + + return response.ok({ + body: logEntriesSummaryHighlightsResponseRT.encode({ + data: bucketsPerHighlightTerm.map(buckets => ({ + start: startDate, + end: endDate, + buckets, + })), + }), + }); + } catch (error) { + return response.internalError({ + body: error.message, + }); + } + } + ); +}; diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/index.ts b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/index.ts index 0c69034c66940..64cdb9318b6e1 100644 --- a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/index.ts +++ b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/index.ts @@ -4,15 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ +import Boom from 'boom'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; import { schema } from '@kbn/config-schema'; import { InfraBackendLibs } from '../../lib/infra_types'; import { getGroupings } from './lib/get_groupings'; import { populateSeriesWithTSVBData } from './lib/populate_series_with_tsvb_data'; -import { MetricsExplorerRequestBody } from './types'; -// import { metricsExplorerSchema } from './schema'; -// import { MetricsExplorerResponse, MetricsExplorerRequestBody } from './types'; +import { metricsExplorerRequestBodyRT, metricsExplorerResponseRT } from '../../../common/http_api'; +import { throwErrors } from '../../../common/runtime_types'; -// NP_TODO: need to replace all of this with real types or io-ts or something? const escapeHatch = schema.object({}, { allowUnknowns: true }); export const initMetricExplorerRoute = (libs: InfraBackendLibs) => { @@ -29,20 +31,27 @@ export const initMetricExplorerRoute = (libs: InfraBackendLibs) => { }, async (requestContext, request, response) => { try { + const payload = pipe( + metricsExplorerRequestBodyRT.decode(request.body), + fold(throwErrors(Boom.badRequest), identity) + ); + const search = (searchOptions: object) => callWithRequest<{}, Aggregation>(requestContext, 'search', searchOptions); - const options = request.body as MetricsExplorerRequestBody; // Need to remove this casting and swap in config-schema demands :( + // First we get the groupings from a composite aggregation - const groupings = await getGroupings(search, options); + const groupings = await getGroupings(search, payload); // Then we take the results and fill in the data from TSVB with the // user's custom metrics const seriesWithMetrics = await Promise.all( groupings.series.map( - populateSeriesWithTSVBData(request, options, framework, requestContext) + populateSeriesWithTSVBData(request, payload, framework, requestContext) ) ); - return response.ok({ body: { ...groupings, series: seriesWithMetrics } }); + return response.ok({ + body: metricsExplorerResponseRT.encode({ ...groupings, series: seriesWithMetrics }), + }); } catch (error) { return response.internalError({ body: error.message, diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/create_metrics_model.ts b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/create_metrics_model.ts index 64b9fba0e7aa2..9e5fe16d482b2 100644 --- a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/create_metrics_model.ts +++ b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/create_metrics_model.ts @@ -5,7 +5,7 @@ */ import { InfraMetricModelMetricType } from '../../../lib/adapters/metrics'; -import { MetricsExplorerAggregation, MetricsExplorerRequestBody } from '../types'; +import { MetricsExplorerRequestBody } from '../types'; import { InfraMetric } from '../../../graphql/types'; import { TSVBMetricModel } from '../../../../common/inventory_models/types'; export const createMetricModel = (options: MetricsExplorerRequestBody): TSVBMetricModel => { @@ -20,7 +20,7 @@ export const createMetricModel = (options: MetricsExplorerRequestBody): TSVBMetr // when the responses are processed and combined with the grouping request. series: options.metrics.map((metric, index) => { // If the metric is a rate then we need to add TSVB metrics for calculating the derivative - if (metric.aggregation === MetricsExplorerAggregation.rate) { + if (metric.aggregation === 'rate') { const aggType = 'max'; return { id: `metric_${index}`, @@ -49,8 +49,7 @@ export const createMetricModel = (options: MetricsExplorerRequestBody): TSVBMetr }; } // Create a basic TSVB series with a single metric - const aggregation = - MetricsExplorerAggregation[metric.aggregation] || MetricsExplorerAggregation.avg; + const aggregation = metric.aggregation || 'avg'; return { id: `metric_${index}`, diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts index 1a0edb1053730..3dfbfbb5c3979 100644 --- a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts +++ b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts @@ -8,10 +8,10 @@ import { union } from 'lodash'; import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; import { KibanaFramework } from '../../../lib/adapters/framework/kibana_framework_adapter'; import { - MetricsExplorerColumnType, MetricsExplorerRow, MetricsExplorerSeries, MetricsExplorerRequestBody, + MetricsExplorerColumn, } from '../types'; import { createMetricModel } from './create_metrics_model'; import { JsonObject } from '../../../../common/typed_json'; @@ -95,11 +95,11 @@ export const populateSeriesWithTSVBData = ( // Setup the dynamic columns and row attributes depending on if the user is doing a group by // and multiple metrics - const attributeColumns = - options.groupBy != null ? [{ name: 'groupBy', type: MetricsExplorerColumnType.string }] : []; - const metricColumns = options.metrics.map((m, i) => ({ + const attributeColumns: MetricsExplorerColumn[] = + options.groupBy != null ? [{ name: 'groupBy', type: 'string' }] : []; + const metricColumns: MetricsExplorerColumn[] = options.metrics.map((m, i) => ({ name: `metric_${i}`, - type: MetricsExplorerColumnType.number, + type: 'number', })); const rowAttributes = options.groupBy != null ? { groupBy: series.id } : {}; @@ -132,7 +132,7 @@ export const populateSeriesWithTSVBData = ( ...series, rows, columns: [ - { name: 'timestamp', type: MetricsExplorerColumnType.date }, + { name: 'timestamp', type: 'date' } as MetricsExplorerColumn, ...metricColumns, ...attributeColumns, ], diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/schema.ts b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/schema.ts deleted file mode 100644 index 00231bc2a7938..0000000000000 --- a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/schema.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import * as Joi from 'joi'; -import { values } from 'lodash'; -import { MetricsExplorerColor } from '../../../common/color_palette'; -import { MetricsExplorerAggregation } from './types'; - -export const metricsExplorerSchema = Joi.object({ - limit: Joi.number() - .min(1) - .default(9), - afterKey: Joi.string().allow(null), - groupBy: Joi.string().allow(null), - indexPattern: Joi.string().required(), - metrics: Joi.array() - .items( - Joi.object().keys({ - aggregation: Joi.string() - .valid(values(MetricsExplorerAggregation)) - .required(), - field: Joi.string(), - rate: Joi.bool().default(false), - color: Joi.string().valid(values(MetricsExplorerColor)), - label: Joi.string(), - }) - ) - .required(), - filterQuery: Joi.string(), - timerange: Joi.object() - .keys({ - field: Joi.string().required(), - from: Joi.number().required(), - to: Joi.number().required(), - interval: Joi.string().required(), - }) - .required(), -}); diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/types.ts b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/types.ts index a43e3adbdd184..f4c5e26c5c6d1 100644 --- a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/types.ts +++ b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/types.ts @@ -4,65 +4,33 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface InfraTimerange { - field: string; - from: number; - to: number; - interval: string; -} +import * as rt from 'io-ts'; +import { + metricsExplorerMetricRT, + metricsExplorerPageInfoRT, + metricsExplorerColumnRT, + metricsExplorerRowRT, + metricsExplorerSeriesRT, + metricsExplorerRequestBodyRT, + metricsExplorerResponseRT, + metricsExplorerAggregationRT, + metricsExplorerColumnTypeRT, +} from '../../../common/http_api'; -export enum MetricsExplorerAggregation { - avg = 'avg', - max = 'max', - min = 'min', - cardinality = 'cardinality', - rate = 'rate', - count = 'count', -} +export type MetricsExplorerAggregation = rt.TypeOf; -export interface MetricsExplorerMetric { - aggregation: MetricsExplorerAggregation; - field?: string | undefined; -} +export type MetricsExplorerColumnType = rt.TypeOf; -export interface MetricsExplorerRequestBody { - timerange: InfraTimerange; - indexPattern: string; - metrics: MetricsExplorerMetric[]; - groupBy?: string; - afterKey?: string; - limit?: number; - filterQuery?: string; -} +export type MetricsExplorerMetric = rt.TypeOf; -export interface MetricsExplorerPageInfo { - total: number; - afterKey?: string | null; -} +export type MetricsExplorerPageInfo = rt.TypeOf; -export enum MetricsExplorerColumnType { - date = 'date', - number = 'number', - string = 'string', -} +export type MetricsExplorerColumn = rt.TypeOf; -export interface MetricsExplorerColumn { - name: string; - type: MetricsExplorerColumnType; -} +export type MetricsExplorerRow = rt.TypeOf; -export interface MetricsExplorerRow { - timestamp: number; - [key: string]: string | number | null | undefined; -} +export type MetricsExplorerSeries = rt.TypeOf; -export interface MetricsExplorerSeries { - id: string; - columns: MetricsExplorerColumn[]; - rows: MetricsExplorerRow[]; -} +export type MetricsExplorerRequestBody = rt.TypeOf; -export interface MetricsExplorerResponse { - series: MetricsExplorerSeries[]; - pageInfo: MetricsExplorerPageInfo; -} +export type MetricsExplorerResponse = rt.TypeOf; diff --git a/x-pack/legacy/plugins/infra/server/routes/snapshot/index.ts b/x-pack/legacy/plugins/infra/server/routes/snapshot/index.ts index ae707bae79b9e..dffeb04c92a8a 100644 --- a/x-pack/legacy/plugins/infra/server/routes/snapshot/index.ts +++ b/x-pack/legacy/plugins/infra/server/routes/snapshot/index.ts @@ -47,10 +47,9 @@ export const initSnapshotRoute = (libs: InfraBackendLibs) => { metric: metric as InfraSnapshotMetricInput, timerange, }; + const nodesWithInterval = await libs.snapshot.getNodes(requestContext, options); return response.ok({ - body: SnapshotNodeResponseRT.encode({ - nodes: await libs.snapshot.getNodes(requestContext, options), - }), + body: SnapshotNodeResponseRT.encode(nodesWithInterval), }); } catch (error) { return response.internalError({ diff --git a/x-pack/legacy/plugins/license_management/__jest__/start_trial.test.js b/x-pack/legacy/plugins/license_management/__jest__/start_trial.test.js index 69c606c866eef..5436a51a2632b 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/start_trial.test.js +++ b/x-pack/legacy/plugins/license_management/__jest__/start_trial.test.js @@ -6,6 +6,7 @@ import { StartTrial } from '../public/np_ready/application/sections/license_dashboard/start_trial'; import { createMockLicense, getComponent } from './util'; +jest.mock('ui/new_platform'); jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`); describe('StartTrial component when trial is allowed', () => { diff --git a/x-pack/legacy/plugins/license_management/__jest__/telemetry_opt_in.test.js b/x-pack/legacy/plugins/license_management/__jest__/telemetry_opt_in.test.js index 8df35b797bf45..1b03ce869e52b 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/telemetry_opt_in.test.js +++ b/x-pack/legacy/plugins/license_management/__jest__/telemetry_opt_in.test.js @@ -11,9 +11,7 @@ import { import { TelemetryOptIn } from '../public/np_ready/application/components/telemetry_opt_in'; import { mountWithIntl } from '../../../../test_utils/enzyme_helpers'; -jest.mock('ui/capabilities', () => ({ - get: jest.fn(), -})); +jest.mock('ui/new_platform'); setTelemetryEnabled(true); diff --git a/x-pack/legacy/plugins/license_management/__jest__/upload_license.test.tsx b/x-pack/legacy/plugins/license_management/__jest__/upload_license.test.tsx index b852daf78ca15..ca9b5b0db9ca1 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/upload_license.test.tsx +++ b/x-pack/legacy/plugins/license_management/__jest__/upload_license.test.tsx @@ -9,6 +9,8 @@ import { mountWithIntl } from '../../../../test_utils/enzyme_helpers'; import React from 'react'; import { Provider } from 'react-redux'; +jest.mock('ui/new_platform'); + // @ts-ignore import { uploadLicense } from '../public/np_ready/application/store/actions/upload_license'; diff --git a/x-pack/legacy/plugins/license_management/index.ts b/x-pack/legacy/plugins/license_management/index.ts index c621a96945c41..e9fbb56e9d6ac 100644 --- a/x-pack/legacy/plugins/license_management/index.ts +++ b/x-pack/legacy/plugins/license_management/index.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Legacy } from 'kibana'; import { resolve } from 'path'; import { PLUGIN } from './common/constants'; -import { Legacy } from '../../../../kibana'; import { plugin } from './server/np_ready'; export function licenseManagement(kibana: any) { @@ -18,6 +18,23 @@ export function licenseManagement(kibana: any) { uiExports: { styleSheetPaths: resolve(__dirname, 'public/np_ready/application/index.scss'), managementSections: ['plugins/license_management/legacy'], + injectDefaultVars(server: Legacy.Server) { + const config = server.config(); + return { + licenseManagementUiEnabled: config.get('xpack.license_management.ui.enabled'), + }; + }, + }, + config(Joi: any) { + return Joi.object({ + // display menu item + ui: Joi.object({ + enabled: Joi.boolean().default(true), + }).default(), + + // enable plugin + enabled: Joi.boolean().default(true), + }).default(); }, init: (server: Legacy.Server) => { plugin({} as any).setup(server.newPlatform.setup.core, { diff --git a/x-pack/legacy/plugins/license_management/public/management_section.ts b/x-pack/legacy/plugins/license_management/public/management_section.ts index 4f69581fff0df..c7232649857e3 100644 --- a/x-pack/legacy/plugins/license_management/public/management_section.ts +++ b/x-pack/legacy/plugins/license_management/public/management_section.ts @@ -5,11 +5,16 @@ */ import { management } from 'ui/management'; +import chrome from 'ui/chrome'; import { BASE_PATH, PLUGIN } from '../common/constants'; -management.getSection('elasticsearch').register('license_management', { - visible: true, - display: PLUGIN.TITLE, - order: 99, - url: `#${BASE_PATH}home`, -}); +const licenseManagementUiEnabled = chrome.getInjected('licenseManagementUiEnabled'); + +if (licenseManagementUiEnabled) { + management.getSection('elasticsearch').register('license_management', { + visible: true, + display: PLUGIN.TITLE, + order: 99, + url: `#${BASE_PATH}home`, + }); +} diff --git a/x-pack/legacy/plugins/license_management/public/register_route.ts b/x-pack/legacy/plugins/license_management/public/register_route.ts index ad84f28c6b6d7..994a888ac020a 100644 --- a/x-pack/legacy/plugins/license_management/public/register_route.ts +++ b/x-pack/legacy/plugins/license_management/public/register_route.ts @@ -9,6 +9,7 @@ import { App } from 'src/core/public'; /* Legacy Imports */ import { npSetup, npStart } from 'ui/new_platform'; import { MANAGEMENT_BREADCRUMB } from 'ui/management'; +import chrome from 'ui/chrome'; import routes from 'ui/routes'; // @ts-ignore import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; @@ -25,74 +26,78 @@ import { import { BASE_PATH } from '../common/constants'; -/* - This method handles the cleanup needed when route is scope is destroyed. It also prevents Angular - from destroying scope when route changes and both old route and new route are this same route. -*/ -const manageAngularLifecycle = ($scope: any, $route: any, unmount: () => void) => { - const lastRoute = $route.current; - const deregister = $scope.$on('$locationChangeSuccess', () => { - const currentRoute = $route.current; - // if templates are the same we are on the same route - if (lastRoute.$$route.template === currentRoute.$$route.template) { - // this prevents angular from destroying scope - $route.current = lastRoute; - } - }); - $scope.$on('$destroy', () => { - if (deregister) { - deregister(); - } - unmount(); - }); -}; +const licenseManagementUiEnabled = chrome.getInjected('licenseManagementUiEnabled'); + +if (licenseManagementUiEnabled) { + /* + This method handles the cleanup needed when route is scope is destroyed. It also prevents Angular + from destroying scope when route changes and both old route and new route are this same route. + */ + const manageAngularLifecycle = ($scope: any, $route: any, unmount: () => void) => { + const lastRoute = $route.current; + const deregister = $scope.$on('$locationChangeSuccess', () => { + const currentRoute = $route.current; + // if templates are the same we are on the same route + if (lastRoute.$$route.template === currentRoute.$$route.template) { + // this prevents angular from destroying scope + $route.current = lastRoute; + } + }); + $scope.$on('$destroy', () => { + if (deregister) { + deregister(); + } + unmount(); + }); + }; -const initializeTelemetry = ($injector: any) => { - const telemetryEnabled = $injector.get('telemetryEnabled'); - const Private = $injector.get('Private'); - const telemetryOptInProvider = Private(TelemetryOptInProvider); - setTelemetryOptInService(telemetryOptInProvider); - setTelemetryEnabled(telemetryEnabled); - setHttpClient($injector.get('$http')); -}; + const initializeTelemetry = ($injector: any) => { + const telemetryEnabled = $injector.get('telemetryEnabled'); + const Private = $injector.get('Private'); + const telemetryOptInProvider = Private(TelemetryOptInProvider); + setTelemetryOptInService(telemetryOptInProvider); + setTelemetryEnabled(telemetryEnabled); + setHttpClient($injector.get('$http')); + }; -const template = ` -
-
`; + const template = ` +
+
`; -routes.when(`${BASE_PATH}:view?`, { - template, - controllerAs: 'licenseManagement', - controller: class LicenseManagementController { - constructor($injector: any, $rootScope: any, $scope: any, $route: any) { - initializeTelemetry($injector); + routes.when(`${BASE_PATH}:view?`, { + template, + controllerAs: 'licenseManagement', + controller: class LicenseManagementController { + constructor($injector: any, $rootScope: any, $scope: any, $route: any) { + initializeTelemetry($injector); - $scope.$$postDigest(() => { - const element = document.getElementById('licenseReactRoot')!; + $scope.$$postDigest(() => { + const element = document.getElementById('licenseReactRoot')!; - const refreshXpack = async () => { - await xpackInfo.refresh($injector); - }; + const refreshXpack = async () => { + await xpackInfo.refresh($injector); + }; - plugin({} as any).setup( - { - ...npSetup.core, - application: { - ...npSetup.core.application, - async register(app: App) { - const unmountApp = await app.mount({ ...npStart } as any, { - element, - appBasePath: '', - }); - manageAngularLifecycle($scope, $route, unmountApp as any); + plugin({} as any).setup( + { + ...npSetup.core, + application: { + ...npSetup.core.application, + async register(app: App) { + const unmountApp = await app.mount({ ...npStart } as any, { + element, + appBasePath: '', + }); + manageAngularLifecycle($scope, $route, unmountApp as any); + }, }, }, - }, - { - __LEGACY: { xpackInfo, refreshXpack, MANAGEMENT_BREADCRUMB }, - } - ); - }); - } - } as any, -} as any); + { + __LEGACY: { xpackInfo, refreshXpack, MANAGEMENT_BREADCRUMB }, + } + ); + }); + } + } as any, + } as any); +} diff --git a/x-pack/legacy/plugins/maps/common/constants.js b/x-pack/legacy/plugins/maps/common/constants.js index afe30f3492147..a6bdee3d2ed12 100644 --- a/x-pack/legacy/plugins/maps/common/constants.js +++ b/x-pack/legacy/plugins/maps/common/constants.js @@ -59,6 +59,8 @@ export const FIELD_ORIGIN = { export const SOURCE_DATA_ID_ORIGIN = 'source'; export const META_ID_ORIGIN_SUFFIX = 'meta'; export const SOURCE_META_ID_ORIGIN = `${SOURCE_DATA_ID_ORIGIN}_${META_ID_ORIGIN_SUFFIX}`; +export const FORMATTERS_ID_ORIGIN_SUFFIX = 'formatters'; +export const SOURCE_FORMATTERS_ID_ORIGIN = `${SOURCE_DATA_ID_ORIGIN}_${FORMATTERS_ID_ORIGIN_SUFFIX}`; export const GEOJSON_FILE = 'GEOJSON_FILE'; diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js index 77755459ca5cf..a6e28c9c29a32 100644 --- a/x-pack/legacy/plugins/maps/index.js +++ b/x-pack/legacy/plugins/maps/index.js @@ -106,7 +106,6 @@ export function maps(kibana) { // legacy dependencies const __LEGACY = { - pluginRef: this, config: server.config, mapConfig() { return server.config().get('map'); @@ -114,7 +113,6 @@ export function maps(kibana) { route: server.route.bind(server), plugins: { elasticsearch: server.plugins.elasticsearch, - xpackMainPlugin: server.plugins.xpack_main, }, savedObjects: { getSavedObjectsRepository: server.savedObjects.getSavedObjectsRepository, diff --git a/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js b/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js index 0eba30e0c4e9b..bd496f44aa8ac 100644 --- a/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js +++ b/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import './saved_gis_map'; +import { createSavedGisMapClass } from './saved_gis_map'; import { uiModules } from 'ui/modules'; import { SavedObjectLoader } from 'ui/saved_objects'; import { npStart } from '../../../../../../../src/legacy/ui/public/new_platform'; @@ -12,6 +12,15 @@ import { npStart } from '../../../../../../../src/legacy/ui/public/new_platform' const module = uiModules.get('app/maps'); // This is the only thing that gets injected into controllers -module.service('gisMapSavedObjectLoader', function(SavedGisMap) { +module.service('gisMapSavedObjectLoader', function() { + const savedObjectsClient = npStart.core.savedObjects.client; + const services = { + savedObjectsClient, + indexPatterns: npStart.plugins.data.indexPatterns, + chrome: npStart.core.chrome, + overlays: npStart.core.overlays, + }; + const SavedGisMap = createSavedGisMapClass(services); + return new SavedObjectLoader(SavedGisMap, npStart.core.savedObjects.client, npStart.core.chrome); }); diff --git a/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js b/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js index b846bb5b77543..035ae4886920c 100644 --- a/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js +++ b/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js @@ -5,9 +5,7 @@ */ import _ from 'lodash'; -import { uiModules } from 'ui/modules'; -import { createLegacyClass } from 'ui/utils/legacy_class'; -import { SavedObjectProvider } from 'ui/saved_objects/saved_object'; +import { createSavedObjectClass } from 'ui/saved_objects/saved_object'; import { getTimeFilters, getMapZoom, @@ -24,95 +22,89 @@ import { copyPersistentState } from '../../reducers/util'; import { extractReferences, injectReferences } from '../../../common/migrations/references'; import { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants'; -const module = uiModules.get('app/maps'); - -module.factory('SavedGisMap', function(Private) { - const SavedObject = Private(SavedObjectProvider); - createLegacyClass(SavedGisMap).inherits(SavedObject); - function SavedGisMap(id) { - SavedGisMap.Super.call(this, { - type: SavedGisMap.type, - mapping: SavedGisMap.mapping, - searchSource: SavedGisMap.searchsource, - extractReferences, - injectReferences: (savedObject, references) => { - const { attributes } = injectReferences({ - attributes: { layerListJSON: savedObject.layerListJSON }, - references, - }); - - savedObject.layerListJSON = attributes.layerListJSON; - - const indexPatternIds = references - .filter(reference => { - return reference.type === 'index-pattern'; - }) - .map(reference => { - return reference.id; - }); - savedObject.indexPatternIds = _.uniq(indexPatternIds); - }, - - // if this is null/undefined then the SavedObject will be assigned the defaults - id: id, - - // default values that will get assigned if the doc is new - defaults: { - title: 'New Map', - description: '', +export function createSavedGisMapClass(services) { + const SavedObjectClass = createSavedObjectClass(services); + + class SavedGisMap extends SavedObjectClass { + static type = MAP_SAVED_OBJECT_TYPE; + + // Mappings are used to place object properties into saved object _source + static mapping = { + title: 'text', + description: 'text', + mapStateJSON: 'text', + layerListJSON: 'text', + uiStateJSON: 'text', + bounds: { + type: 'object', }, - }); + }; + static fieldOrder = ['title', 'description']; + static searchSource = false; + + constructor(id) { + super({ + type: SavedGisMap.type, + mapping: SavedGisMap.mapping, + searchSource: SavedGisMap.searchSource, + extractReferences, + injectReferences: (savedObject, references) => { + const { attributes } = injectReferences({ + attributes: { layerListJSON: savedObject.layerListJSON }, + references, + }); - this.showInRecentlyAccessed = true; + savedObject.layerListJSON = attributes.layerListJSON; + + const indexPatternIds = references + .filter(reference => { + return reference.type === 'index-pattern'; + }) + .map(reference => { + return reference.id; + }); + savedObject.indexPatternIds = _.uniq(indexPatternIds); + }, + + // if this is null/undefined then the SavedObject will be assigned the defaults + id: id, + + // default values that will get assigned if the doc is new + defaults: { + title: 'New Map', + description: '', + }, + }); + this.showInRecentlyAccessed = true; + } + getFullPath() { + return `/app/maps#map/${this.id}`; + } + getLayerList() { + return this.layerListJSON ? JSON.parse(this.layerListJSON) : null; + } + + syncWithStore(state) { + const layerList = getLayerListRaw(state); + const layerListConfigOnly = copyPersistentState(layerList); + this.layerListJSON = JSON.stringify(layerListConfigOnly); + + this.mapStateJSON = JSON.stringify({ + zoom: getMapZoom(state), + center: getMapCenter(state), + timeFilters: getTimeFilters(state), + refreshConfig: getRefreshConfig(state), + query: _.omit(getQuery(state), 'queryLastTriggeredAt'), + filters: getFilters(state), + }); + + this.uiStateJSON = JSON.stringify({ + isLayerTOCOpen: getIsLayerTOCOpen(state), + openTOCDetails: getOpenTOCDetails(state), + }); + + this.bounds = convertMapExtentToPolygon(getMapExtent(state)); + } } - - SavedGisMap.type = MAP_SAVED_OBJECT_TYPE; - - // Mappings are used to place object properties into saved object _source - SavedGisMap.mapping = { - title: 'text', - description: 'text', - mapStateJSON: 'text', - layerListJSON: 'text', - uiStateJSON: 'text', - bounds: { - type: 'object', - }, - }; - - SavedGisMap.fieldOrder = ['title', 'description']; - - SavedGisMap.searchsource = false; - - SavedGisMap.prototype.getFullPath = function() { - return `/app/maps#map/${this.id}`; - }; - - SavedGisMap.prototype.getLayerList = function() { - return this.layerListJSON ? JSON.parse(this.layerListJSON) : null; - }; - - SavedGisMap.prototype.syncWithStore = function(state) { - const layerList = getLayerListRaw(state); - const layerListConfigOnly = copyPersistentState(layerList); - this.layerListJSON = JSON.stringify(layerListConfigOnly); - - this.mapStateJSON = JSON.stringify({ - zoom: getMapZoom(state), - center: getMapCenter(state), - timeFilters: getTimeFilters(state), - refreshConfig: getRefreshConfig(state), - query: _.omit(getQuery(state), 'queryLastTriggeredAt'), - filters: getFilters(state), - }); - - this.uiStateJSON = JSON.stringify({ - isLayerTOCOpen: getIsLayerTOCOpen(state), - openTOCDetails: getOpenTOCDetails(state), - }); - - this.bounds = convertMapExtentToPolygon(getMapExtent(state)); - }; - return SavedGisMap; -}); +} diff --git a/x-pack/legacy/plugins/maps/public/kibana_services.js b/x-pack/legacy/plugins/maps/public/kibana_services.js index 7e63866877903..1ec7565df6f26 100644 --- a/x-pack/legacy/plugins/maps/public/kibana_services.js +++ b/x-pack/legacy/plugins/maps/public/kibana_services.js @@ -8,7 +8,6 @@ import { getRequestInspectorStats, getResponseInspectorStats, } from '../../../../../src/legacy/ui/public/courier'; -export { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; import { esFilters } from '../../../../../src/plugins/data/public'; import { npStart } from 'ui/new_platform'; @@ -16,6 +15,12 @@ export const SPATIAL_FILTER_TYPE = esFilters.FILTERS.SPATIAL_FILTER; export { SearchSource } from '../../../../../src/legacy/ui/public/courier'; export const indexPatternService = npStart.plugins.data.indexPatterns; +let licenseId; +export const setLicenseId = latestLicenseId => (licenseId = latestLicenseId); +export const getLicenseId = () => { + return licenseId; +}; + export async function fetchSearchSourceAndRecordWithInspector({ searchSource, requestId, diff --git a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js b/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js index 6671914bce74f..13a2e05ab8eeb 100644 --- a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js +++ b/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js @@ -6,7 +6,7 @@ import { ESTermSource } from '../sources/es_term_source'; import { getComputedFieldNamePrefix } from '../styles/vector/style_util'; -import { META_ID_ORIGIN_SUFFIX } from '../../../common/constants'; +import { META_ID_ORIGIN_SUFFIX, FORMATTERS_ID_ORIGIN_SUFFIX } from '../../../common/constants'; export class InnerJoin { constructor(joinDescriptor, leftSource) { @@ -45,6 +45,10 @@ export class InnerJoin { return `${this.getSourceDataRequestId()}_${META_ID_ORIGIN_SUFFIX}`; } + getSourceFormattersDataRequestId() { + return `${this.getSourceDataRequestId()}_${FORMATTERS_ID_ORIGIN_SUFFIX}`; + } + getLeftField() { return this._leftField; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js index c07b51c20ab85..fd3ae8f0ab7e3 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js @@ -82,6 +82,11 @@ export class AbstractESAggSource extends AbstractESSource { }); } + hasMatchingMetricField(fieldName) { + const matchingField = this.getMetricFieldForName(fieldName); + return !!matchingField; + } + getMetricFieldForName(fieldName) { return this.getMetricFields().find(metricField => { return metricField.getName() === fieldName; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js index d386c0ad4a5e0..161c0ea69e86c 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -19,6 +19,7 @@ import { FIELD_ORIGIN, STYLE_TYPE, SOURCE_META_ID_ORIGIN, + SOURCE_FORMATTERS_ID_ORIGIN, LAYER_STYLE_TYPE, } from '../../../../common/constants'; import { VectorIcon } from './components/legend/vector_icon'; @@ -294,14 +295,17 @@ export class VectorStyle extends AbstractStyle { return this._isOnlySingleFeatureType(VECTOR_SHAPE_TYPES.POLYGON); }; - _getFieldMeta = fieldName => { - const fieldMetaFromLocalFeatures = _.get(this._descriptor, ['__styleMeta', fieldName]); - + _getDynamicPropertyByFieldName(fieldName) { const dynamicProps = this.getDynamicPropertiesArray(); - const dynamicProp = dynamicProps.find(dynamicProp => { + return dynamicProps.find(dynamicProp => { return fieldName === dynamicProp.getField().getName(); }); + } + _getFieldMeta = fieldName => { + const fieldMetaFromLocalFeatures = _.get(this._descriptor, ['__styleMeta', fieldName]); + + const dynamicProp = this._getDynamicPropertyByFieldName(fieldName); if (!dynamicProp || !dynamicProp.isFieldMetaEnabled()) { return fieldMetaFromLocalFeatures; } @@ -311,8 +315,7 @@ export class VectorStyle extends AbstractStyle { dataRequestId = SOURCE_META_ID_ORIGIN; } else { const join = this._layer.getValidJoins().find(join => { - const matchingField = join.getRightJoinSource().getMetricFieldForName(fieldName); - return !!matchingField; + return join.getRightJoinSource().hasMatchingMetricField(fieldName); }); if (join) { dataRequestId = join.getSourceMetaDataRequestId(); @@ -323,7 +326,7 @@ export class VectorStyle extends AbstractStyle { return fieldMetaFromLocalFeatures; } - const styleMetaDataRequest = this._layer._findDataRequestForSource(dataRequestId); + const styleMetaDataRequest = this._layer._findDataRequestById(dataRequestId); if (!styleMetaDataRequest || !styleMetaDataRequest.hasData()) { return fieldMetaFromLocalFeatures; } @@ -334,6 +337,37 @@ export class VectorStyle extends AbstractStyle { return fieldMeta ? fieldMeta : fieldMetaFromLocalFeatures; }; + _getFieldFormatter(fieldName) { + const dynamicProp = this._getDynamicPropertyByFieldName(fieldName); + if (!dynamicProp) { + return null; + } + + let dataRequestId; + if (dynamicProp.getFieldOrigin() === FIELD_ORIGIN.SOURCE) { + dataRequestId = SOURCE_FORMATTERS_ID_ORIGIN; + } else { + const join = this._layer.getValidJoins().find(join => { + return join.getRightJoinSource().hasMatchingMetricField(fieldName); + }); + if (join) { + dataRequestId = join.getSourceFormattersDataRequestId(); + } + } + + if (!dataRequestId) { + return null; + } + + const formattersDataRequest = this._layer._findDataRequestById(dataRequestId); + if (!formattersDataRequest || !formattersDataRequest.hasData()) { + return null; + } + + const formatters = formattersDataRequest.getData(); + return formatters[fieldName]; + } + _getStyleMeta = () => { return _.get(this._descriptor, '__styleMeta', {}); }; @@ -382,7 +416,7 @@ export class VectorStyle extends AbstractStyle { const promises = styles.map(async style => { return { label: await style.getField().getLabel(), - fieldFormatter: await this._source.getFieldFormatter(style.getField().getName()), + fieldFormatter: this._getFieldFormatter(style.getField().getName()), meta: this._getFieldMeta(style.getField().getName()), style, }; @@ -539,14 +573,10 @@ export class VectorStyle extends AbstractStyle { fieldName: fieldDescriptor.name, }); } else if (fieldDescriptor.origin === FIELD_ORIGIN.JOIN) { - let matchingField = null; - const joins = this._layer.getValidJoins(); - joins.find(join => { - const aggSource = join.getRightJoinSource(); - matchingField = aggSource.getMetricFieldForName(fieldDescriptor.name); - return !!matchingField; + const join = this._layer.getValidJoins().find(join => { + return join.getRightJoinSource().hasMatchingMetricField(fieldDescriptor.name); }); - return matchingField; + return join ? join.getRightJoinSource().getMetricFieldForName(fieldDescriptor.name) : null; } else { throw new Error(`Unknown origin-type ${fieldDescriptor.origin}`); } diff --git a/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.js b/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.js index 41f6de65e4032..d9182be56a75f 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.js +++ b/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.js @@ -158,3 +158,15 @@ export function canSkipStyleMetaUpdate({ prevDataRequest, nextMeta }) { !updateDueToFields && !updateDueToSourceQuery && !updateDueToIsTimeAware && !updateDueToTime ); } + +export function canSkipFormattersUpdate({ prevDataRequest, nextMeta }) { + if (!prevDataRequest) { + return false; + } + const prevMeta = prevDataRequest.getMeta(); + if (!prevMeta) { + return false; + } + + return !_.isEqual(prevMeta.fieldNames, nextMeta.fieldNames); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js index 16129087de1f8..30c47658bb327 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js @@ -13,6 +13,7 @@ import { FEATURE_ID_PROPERTY_NAME, SOURCE_DATA_ID_ORIGIN, SOURCE_META_ID_ORIGIN, + SOURCE_FORMATTERS_ID_ORIGIN, FEATURE_VISIBLE_PROPERTY_NAME, EMPTY_FEATURE_COLLECTION, LAYER_TYPE, @@ -24,7 +25,11 @@ import { JoinTooltipProperty } from './tooltips/join_tooltip_property'; import { EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DataRequestAbortError } from './util/data_request'; -import { canSkipSourceUpdate, canSkipStyleMetaUpdate } from './util/can_skip_fetch'; +import { + canSkipSourceUpdate, + canSkipStyleMetaUpdate, + canSkipFormattersUpdate, +} from './util/can_skip_fetch'; import { assignFeatureIds } from './util/assign_feature_ids'; import { getFillFilterExpression, @@ -220,7 +225,7 @@ export class VectorLayer extends AbstractLayer { return indexPatternIds; } - _findDataRequestForSource(sourceDataId) { + _findDataRequestById(sourceDataId) { return this._dataRequests.find(dataRequest => dataRequest.getDataId() === sourceDataId); } @@ -241,7 +246,7 @@ export class VectorLayer extends AbstractLayer { sourceQuery: joinSource.getWhereQuery(), applyGlobalQuery: joinSource.getApplyGlobalQuery(), }; - const prevDataRequest = this._findDataRequestForSource(sourceDataId); + const prevDataRequest = this._findDataRequestById(sourceDataId); const canSkipFetch = await canSkipSourceUpdate({ source: joinSource, @@ -286,6 +291,7 @@ export class VectorLayer extends AbstractLayer { async _syncJoins(syncContext) { const joinSyncs = this.getValidJoins().map(async join => { await this._syncJoinStyleMeta(syncContext, join); + await this._syncJoinFormatters(syncContext, join); return this._syncJoin({ join, ...syncContext }); }); @@ -355,7 +361,7 @@ export class VectorLayer extends AbstractLayer { registerCancelCallback, dataFilters, }) { - const requestToken = Symbol(`layer-source-data:${this.getId()}`); + const requestToken = Symbol(`layer-${this.getId()}-${SOURCE_DATA_ID_ORIGIN}`); const searchFilters = this._getSearchFilters(dataFilters); const prevDataRequest = this.getSourceDataRequest(); @@ -459,13 +465,13 @@ export class VectorLayer extends AbstractLayer { isTimeAware: this._style.isTimeAware() && (await source.isTimeAware()), timeFilters: dataFilters.timeFilters, }; - const prevDataRequest = this._findDataRequestForSource(dataRequestId); + const prevDataRequest = this._findDataRequestById(dataRequestId); const canSkipFetch = canSkipStyleMetaUpdate({ prevDataRequest, nextMeta }); if (canSkipFetch) { return; } - const requestToken = Symbol(`layer-${this.getId()}-style-meta`); + const requestToken = Symbol(`layer-${this.getId()}-${dataRequestId}`); try { startLoading(dataRequestId, requestToken, nextMeta); const layerName = await this.getDisplayName(); @@ -484,12 +490,87 @@ export class VectorLayer extends AbstractLayer { } } + async _syncSourceFormatters(syncContext) { + if (this._style.constructor.type !== LAYER_STYLE_TYPE.VECTOR) { + return; + } + + return this._syncFormatters({ + source: this._source, + dataRequestId: SOURCE_FORMATTERS_ID_ORIGIN, + fields: this._style + .getDynamicPropertiesArray() + .filter(dynamicStyleProp => { + return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.SOURCE; + }) + .map(dynamicStyleProp => { + return dynamicStyleProp.getField(); + }), + ...syncContext, + }); + } + + async _syncJoinFormatters(syncContext, join) { + const joinSource = join.getRightJoinSource(); + return this._syncFormatters({ + source: joinSource, + dataRequestId: join.getSourceFormattersDataRequestId(), + fields: this._style + .getDynamicPropertiesArray() + .filter(dynamicStyleProp => { + const matchingField = joinSource.getMetricFieldForName( + dynamicStyleProp.getField().getName() + ); + return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && !!matchingField; + }) + .map(dynamicStyleProp => { + return dynamicStyleProp.getField(); + }), + ...syncContext, + }); + } + + async _syncFormatters({ source, dataRequestId, fields, startLoading, stopLoading, onLoadError }) { + if (fields.length === 0) { + return; + } + + const fieldNames = fields.map(field => { + return field.getName(); + }); + const nextMeta = { + fieldNames: _.uniq(fieldNames).sort(), + }; + const prevDataRequest = this._findDataRequestById(dataRequestId); + const canSkipUpdate = canSkipFormattersUpdate({ prevDataRequest, nextMeta }); + if (canSkipUpdate) { + return; + } + + const requestToken = Symbol(`layer-${this.getId()}-${dataRequestId}`); + try { + startLoading(dataRequestId, requestToken, nextMeta); + + const formatters = {}; + const promises = fields.map(async field => { + const fieldName = field.getName(); + formatters[fieldName] = await source.getFieldFormatter(fieldName); + }); + await Promise.all(promises); + + stopLoading(dataRequestId, requestToken, formatters, nextMeta); + } catch (error) { + onLoadError(dataRequestId, requestToken, error.message); + } + } + async syncData(syncContext) { if (!this.isVisible() || !this.showAtZoomLevel(syncContext.dataFilters.zoom)) { return; } await this._syncSourceStyleMeta(syncContext); + await this._syncSourceFormatters(syncContext); const sourceResult = await this._syncSource(syncContext); if ( !sourceResult.featureCollection || diff --git a/x-pack/legacy/plugins/maps/public/legacy.ts b/x-pack/legacy/plugins/maps/public/legacy.ts index 684d7b16fbb3b..6adab529daf86 100644 --- a/x-pack/legacy/plugins/maps/public/legacy.ts +++ b/x-pack/legacy/plugins/maps/public/legacy.ts @@ -16,11 +16,11 @@ const setupPlugins = { __LEGACY: { uiModules, }, - plugins: npSetup.plugins, + np: npSetup.plugins, }; const startPlugins = { - plugins: npStart.plugins, + np: npStart.plugins, }; export const setup = pluginInstance.setup(npSetup.core, setupPlugins); diff --git a/x-pack/legacy/plugins/maps/public/meta.js b/x-pack/legacy/plugins/maps/public/meta.js index c47c2847ffd14..d92b8713f0e70 100644 --- a/x-pack/legacy/plugins/maps/public/meta.js +++ b/x-pack/legacy/plugins/maps/public/meta.js @@ -8,7 +8,7 @@ import { GIS_API_PATH, EMS_CATALOGUE_PATH, EMS_GLYPHS_PATH } from '../common/con import chrome from 'ui/chrome'; import { i18n } from '@kbn/i18n'; import { EMSClient } from '@elastic/ems-client'; -import { xpackInfo } from './kibana_services'; +import { getLicenseId } from './kibana_services'; import fetch from 'node-fetch'; const GIS_API_RELATIVE = `../${GIS_API_PATH}`; @@ -68,9 +68,7 @@ export function getEMSClient() { }; } } - const xpackMapsFeature = xpackInfo.get('features.maps'); - const licenseId = - xpackMapsFeature && xpackMapsFeature.maps && xpackMapsFeature.uid ? xpackMapsFeature.uid : ''; + const licenseId = getLicenseId(); if (latestLicenseId !== licenseId) { latestLicenseId = licenseId; emsClient.addQueryParams({ license: licenseId }); diff --git a/x-pack/legacy/plugins/maps/public/meta.test.js b/x-pack/legacy/plugins/maps/public/meta.test.js index 07cf4a37b07cd..06f4071e3444b 100644 --- a/x-pack/legacy/plugins/maps/public/meta.test.js +++ b/x-pack/legacy/plugins/maps/public/meta.test.js @@ -33,10 +33,8 @@ jest.mock('ui/chrome', () => ({ jest.mock('./kibana_services', () => { return { - xpackInfo: { - get() { - return 'foobarlicenseid'; - }, + getLicenseId() { + return 'foobarlicenseid'; }, }; }); diff --git a/x-pack/legacy/plugins/maps/public/plugin.ts b/x-pack/legacy/plugins/maps/public/plugin.ts index 4e6d52d20db64..0df7109852486 100644 --- a/x-pack/legacy/plugins/maps/public/plugin.ts +++ b/x-pack/legacy/plugins/maps/public/plugin.ts @@ -9,6 +9,8 @@ import { Plugin, CoreStart } from 'src/core/public'; import { wrapInI18nContext } from 'ui/i18n'; // @ts-ignore import { MapListing } from './components/map_listing'; +// @ts-ignore +import { setLicenseId } from './kibana_services'; /** * These are the interfaces with your public contracts. You should export these @@ -21,10 +23,20 @@ export type MapsPluginStart = ReturnType; /** @internal */ export class MapsPlugin implements Plugin { public setup(core: any, plugins: any) { - const app = plugins.__LEGACY.uiModules.get('app/maps', ['ngRoute', 'react']); - app.directive('mapListing', function(reactDirective: any) { - return reactDirective(wrapInI18nContext(MapListing)); - }); + const { + __LEGACY: { uiModules }, + np: { licensing }, + } = plugins; + + uiModules + .get('app/maps', ['ngRoute', 'react']) + .directive('mapListing', function(reactDirective: any) { + return reactDirective(wrapInI18nContext(MapListing)); + }); + + if (licensing) { + licensing.license$.subscribe(({ uid }: { uid: string }) => setLicenseId(uid)); + } } public start(core: CoreStart, plugins: any) {} diff --git a/x-pack/legacy/plugins/maps/server/plugin.js b/x-pack/legacy/plugins/maps/server/plugin.js index 080796fd25d82..e6b474e1c78dd 100644 --- a/x-pack/legacy/plugins/maps/server/plugin.js +++ b/x-pack/legacy/plugins/maps/server/plugin.js @@ -5,16 +5,15 @@ */ import { i18n } from '@kbn/i18n'; import { APP_ID, APP_ICON, createMapPath, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; -import { initRoutes } from './routes'; import { getEcommerceSavedObjects } from './sample_data/ecommerce_saved_objects'; import { getFlightsSavedObjects } from './sample_data/flights_saved_objects.js'; import { getWebLogsSavedObjects } from './sample_data/web_logs_saved_objects.js'; -import { checkLicense } from '../check_license'; -import { watchStatusAndLicenseToInitialize } from '../../../server/lib/watch_status_and_license_to_initialize'; +import { LICENSE_CHECK_STATE } from '../../../../plugins/licensing/server'; +import { initRoutes } from './routes'; export class MapPlugin { setup(core, plugins, __LEGACY) { - const { featuresPlugin } = plugins; + const { featuresPlugin, licensing } = plugins; let routesInitialized = false; featuresPlugin.registerFeature({ @@ -44,20 +43,13 @@ export class MapPlugin { }, }); - watchStatusAndLicenseToInitialize( - __LEGACY.plugins.xpackMainPlugin, - __LEGACY.pluginRef, - async license => { - if (license && license.maps && !routesInitialized) { - routesInitialized = true; - initRoutes(__LEGACY, license.uid); - } + licensing.license$.subscribe(license => { + const { state } = license.check('maps', 'basic'); + if (state === LICENSE_CHECK_STATE.Valid && !routesInitialized) { + routesInitialized = true; + initRoutes(__LEGACY, license.uid); } - ); - - __LEGACY.plugins.xpackMainPlugin.info - .feature(APP_ID) - .registerLicenseCheckResultsGenerator(checkLicense); + }); const sampleDataLinkLabel = i18n.translate('xpack.maps.sampleDataLinkLabel', { defaultMessage: 'Map', diff --git a/x-pack/legacy/plugins/ml/server/models/data_recognizer/__tests__/data_recognizer.js b/x-pack/legacy/plugins/ml/server/models/data_recognizer/__tests__/data_recognizer.js index 8da91822fcd59..cb268ffede7fa 100644 --- a/x-pack/legacy/plugins/ml/server/models/data_recognizer/__tests__/data_recognizer.js +++ b/x-pack/legacy/plugins/ml/server/models/data_recognizer/__tests__/data_recognizer.js @@ -16,6 +16,7 @@ describe('ML - data recognizer', () => { 'auditbeat_process_docker_ecs', 'auditbeat_process_hosts_ecs', 'logs_ui_analysis', + 'logs_ui_categories', 'metricbeat_system_ecs', 'nginx_ecs', 'sample_data_ecommerce', diff --git a/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_analysis/ml/log_entry_rate.json b/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_analysis/ml/log_entry_rate.json index 5279b18fb03b3..42ba15591e5c4 100644 --- a/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_analysis/ml/log_entry_rate.json +++ b/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_analysis/ml/log_entry_rate.json @@ -1,6 +1,6 @@ { "job_type": "anomaly_detector", - "description": "Detect anomalies in the log entry ingestion rate", + "description": "Logs UI: Detects anomalies in the log entry ingestion rate", "groups": ["logs-ui"], "analysis_config": { "bucket_span": "15m", diff --git a/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/logo.json b/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/logo.json new file mode 100644 index 0000000000000..26b404ff331c6 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/logo.json @@ -0,0 +1,3 @@ +{ + "icon": "logsApp" +} diff --git a/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/manifest.json b/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/manifest.json new file mode 100644 index 0000000000000..b31ddacde8b6d --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/manifest.json @@ -0,0 +1,20 @@ +{ + "id": "logs_ui_categories", + "title": "Log entry categories", + "description": "Detect anomalies in count of log entries by category", + "type": "Logs", + "logoFile": "logo.json", + "jobs": [ + { + "id": "log-entry-categories-count", + "file": "log_entry_categories_count.json" + } + ], + "datafeeds": [ + { + "id": "datafeed-log-entry-categories-count", + "file": "datafeed_log_entry_categories_count.json", + "job_id": "log-entry-categories-count" + } + ] +} diff --git a/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/datafeed_log_entry_categories_count.json b/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/datafeed_log_entry_categories_count.json new file mode 100644 index 0000000000000..6e117b4de87ea --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/datafeed_log_entry_categories_count.json @@ -0,0 +1,15 @@ +{ + "job_id": "JOB_ID", + "indices": ["INDEX_PATTERN_NAME"], + "query": { + "bool": { + "filter": [ + { + "exists": { + "field": "message" + } + } + ] + } + } +} diff --git a/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/log_entry_categories_count.json b/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/log_entry_categories_count.json new file mode 100644 index 0000000000000..b4fb242f16522 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/log_entry_categories_count.json @@ -0,0 +1,34 @@ +{ + "job_type": "anomaly_detector", + "description": "Logs UI: Detects anomalies in count of log entries by category", + "groups": ["logs-ui"], + "analysis_config": { + "bucket_span": "15m", + "categorization_field_name": "message", + "detectors": [ + { + "by_field_name": "mlcategory", + "detector_description": "count by learned log entry category", + "function": "count", + "partition_field_name": "event.dataset", + "use_null": true + } + ], + "influencers": ["event.dataset", "mlcategory"] + }, + "analysis_limits": { + "model_memory_limit": "100mb", + "categorization_examples_limit": 1 + }, + "data_description": { + "time_field": "@timestamp", + "time_format": "epoch_ms" + }, + "model_plot_config": { + "enabled": true + }, + "custom_settings": { + "created_by": "ml-module-logs-ui-categories", + "job_revision": 0 + } +} diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/reason.test.js.snap b/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/reason.test.js.snap index cd317dee65c33..de4b888a48545 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/reason.test.js.snap +++ b/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/reason.test.js.snap @@ -67,7 +67,7 @@ exports[`Logs should render with a no cluster found reason 1`] = ` values={ Object { "link": setup @@ -92,7 +92,7 @@ exports[`Logs should render with a no index found reason 1`] = ` values={ Object { "link": setup @@ -142,7 +142,7 @@ exports[`Logs should render with a no node found reason 1`] = ` values={ Object { "link": setup @@ -154,6 +154,34 @@ exports[`Logs should render with a no node found reason 1`] = ` `; +exports[`Logs should render with a no structured logs reason 1`] = ` + +

+ + points to JSON logs + , + "varPaths": + var.paths + , + } + } + /> +

+
+`; + exports[`Logs should render with a no type found reason 1`] = ` { let title = i18n.translate('xpack.monitoring.logs.reason.defaultTitle', { @@ -91,6 +92,29 @@ export const Reason = ({ reason }) => { }} /> ); + } else if (false === reason.usingStructuredLogs) { + title = i18n.translate('xpack.monitoring.logs.reason.notUsingStructuredLogsTitle', { + defaultMessage: 'No structured logs found', + }); + message = ( + var.paths, + link: ( + + {i18n.translate('xpack.monitoring.logs.reason.notUsingStructuredLogsLink', { + defaultMessage: 'points to JSON logs', + })} + + ), + }} + /> + ); } else if (false === reason.clusterExists) { title = i18n.translate('xpack.monitoring.logs.reason.noClusterTitle', { defaultMessage: 'No logs for this cluster', @@ -103,7 +127,7 @@ export const Reason = ({ reason }) => { link: ( {i18n.translate('xpack.monitoring.logs.reason.noClusterLink', { defaultMessage: 'setup', @@ -125,7 +149,7 @@ export const Reason = ({ reason }) => { link: ( {i18n.translate('xpack.monitoring.logs.reason.noNodeLink', { defaultMessage: 'setup', @@ -147,7 +171,7 @@ export const Reason = ({ reason }) => { link: ( {i18n.translate('xpack.monitoring.logs.reason.noIndexLink', { defaultMessage: 'setup', diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/reason.test.js b/x-pack/legacy/plugins/monitoring/public/components/logs/reason.test.js index 2957de26f7189..c8ed05bd73ade 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/logs/reason.test.js +++ b/x-pack/legacy/plugins/monitoring/public/components/logs/reason.test.js @@ -29,6 +29,13 @@ describe('Logs', () => { expect(component).toMatchSnapshot(); }); + it('should render with a no structured logs reason', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); + it('should render with a no cluster found reason', () => { const component = shallow( diff --git a/x-pack/legacy/plugins/monitoring/server/lib/logs/detect_reason.js b/x-pack/legacy/plugins/monitoring/server/lib/logs/detect_reason.js index 16bd9d506dd0f..b3b835d6ba987 100644 --- a/x-pack/legacy/plugins/monitoring/server/lib/logs/detect_reason.js +++ b/x-pack/legacy/plugins/monitoring/server/lib/logs/detect_reason.js @@ -16,6 +16,7 @@ async function doesFilebeatIndexExist( const filter = [createTimeFilter({ start, end, metric })]; const typeFilter = { term: { 'service.type': 'elasticsearch' } }; + const structuredLogsFilter = { exists: { field: 'elasticsearch.cluster' } }; const clusterFilter = { term: { 'elasticsearch.cluster.uuid': clusterUuid } }; const nodeFilter = { term: { 'elasticsearch.node.id': nodeUuid } }; const indexFilter = { term: { 'elasticsearch.index.name': indexUuid } }; @@ -44,6 +45,14 @@ async function doesFilebeatIndexExist( }, }; + const usingStructuredLogsQuery = { + query: { + bool: { + filter: [...filter, typeFilter, structuredLogsFilter], + }, + }, + }; + const clusterExistsQuery = { query: { bool: { @@ -81,6 +90,8 @@ async function doesFilebeatIndexExist( { ...defaultParams, ...typeExistsAtAnyTimeQuery }, { index: filebeatIndexPattern }, { ...defaultParams, ...typeExistsQuery }, + { index: filebeatIndexPattern }, + { ...defaultParams, ...usingStructuredLogsQuery }, ]; if (clusterUuid) { @@ -102,6 +113,7 @@ async function doesFilebeatIndexExist( indexPatternExistsInTimeRangeResponse, typeExistsAtAnyTimeResponse, typeExistsResponse, + usingStructuredLogsResponse, clusterExistsResponse, nodeExistsResponse, indexExistsResponse, @@ -114,6 +126,7 @@ async function doesFilebeatIndexExist( get(indexPatternExistsInTimeRangeResponse, 'hits.total.value', 0) > 0, typeExistsAtAnyTime: get(typeExistsAtAnyTimeResponse, 'hits.total.value', 0) > 0, typeExists: get(typeExistsResponse, 'hits.total.value', 0) > 0, + usingStructuredLogs: get(usingStructuredLogsResponse, 'hits.total.value', 0) > 0, clusterExists: clusterUuid ? get(clusterExistsResponse, 'hits.total.value', 0) > 0 : null, nodeExists: nodeUuid ? get(nodeExistsResponse, 'hits.total.value', 0) > 0 : null, indexExists: indexUuid ? get(indexExistsResponse, 'hits.total.value', 0) > 0 : null, diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js b/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js index c1ef7db2efc5c..8f34e7d84a08b 100644 --- a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js +++ b/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js @@ -7,6 +7,8 @@ import { pageHelpers, nextTick, setupEnvironment } from './helpers'; import { NON_ALPHA_NUMERIC_CHARS, ACCENTED_CHARS } from './helpers/constants'; +jest.mock('ui/new_platform'); + const { setup } = pageHelpers.remoteClustersAdd; describe('Create Remote cluster', () => { diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js b/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js index c380eaadd8e64..95dc65a96e30a 100644 --- a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js +++ b/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +jest.mock('ui/new_platform'); import { RemoteClusterForm } from '../../public/app/sections/components/remote_cluster_form'; import { pageHelpers, setupEnvironment, nextTick } from './helpers'; import { REMOTE_CLUSTER_EDIT, REMOTE_CLUSTER_EDIT_NAME } from './helpers/constants'; diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js b/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js index 178dd341a75a7..699c00e450a1f 100644 --- a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js +++ b/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js @@ -15,6 +15,8 @@ import { import { getRouter } from '../../public/app/services'; import { getRemoteClusterMock } from '../../fixtures/remote_cluster'; +jest.mock('ui/new_platform'); + const { setup } = pageHelpers.remoteClustersList; describe('', () => { diff --git a/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js b/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js index 627091e0cf749..5c7d53efbd62c 100644 --- a/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js +++ b/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js @@ -9,7 +9,6 @@ import { rollupJobsStore } from '../../store'; import { JobList } from './job_list'; jest.mock('ui/new_platform'); - jest.mock('ui/chrome', () => ({ addBasePath: () => {}, breadcrumbs: { set: () => {} }, diff --git a/x-pack/legacy/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js b/x-pack/legacy/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js index 3cf50c358b3e6..07f2a674decc5 100644 --- a/x-pack/legacy/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js +++ b/x-pack/legacy/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js @@ -5,11 +5,12 @@ */ import React from 'react'; -import { IndexPatternCreationConfig } from '../../../../../../src/legacy/core_plugins/management/public'; +import { i18n } from '@kbn/i18n'; +import { npSetup } from 'ui/new_platform'; import { RollupPrompt } from './components/rollup_prompt'; import { setHttpClient, getRollupIndices } from '../services/api'; -import { i18n } from '@kbn/i18n'; +import { IndexPatternCreationConfig } from '../../../../../../src/legacy/core_plugins/management/public'; const rollupIndexPatternTypeName = i18n.translate( 'xpack.rollupJobs.editRollupIndexPattern.createIndex.defaultTypeName', @@ -63,7 +64,14 @@ export class RollupIndexPatternCreationConfig extends IndexPatternCreationConfig async setRollupIndices() { try { - this.rollupIndicesCapabilities = await getRollupIndices(); + // This is a hack intended to prevent the getRollupIndices() request from being sent if + // we're on /logout. There is a race condition that can arise on that page, whereby this + // request resolves after the logout request resolves, and un-clears the session ID. + const isAnonymous = npSetup.core.http.anonymousPaths.isAnonymous(window.location.pathname); + if (!isAnonymous) { + this.rollupIndicesCapabilities = await getRollupIndices(); + } + this.rollupIndices = Object.keys(this.rollupIndicesCapabilities); } catch (e) { // Silently swallow failure responses such as expired trials diff --git a/x-pack/legacy/plugins/security/index.js b/x-pack/legacy/plugins/security/index.js index a061424c62973..e505a8fb55d90 100644 --- a/x-pack/legacy/plugins/security/index.js +++ b/x-pack/legacy/plugins/security/index.js @@ -54,7 +54,7 @@ export const security = kibana => }, uiExports: { - chromeNavControls: ['plugins/security/views/nav_control'], + chromeNavControls: [], managementSections: ['plugins/security/views/management'], styleSheetPaths: resolve(__dirname, 'public/index.scss'), apps: [ @@ -88,6 +88,7 @@ export const security = kibana => hacks: [ 'plugins/security/hacks/on_session_timeout', 'plugins/security/hacks/on_unauthorized_response', + 'plugins/security/hacks/register_account_management_app', ], home: ['plugins/security/register_feature'], injectDefaultVars: server => { diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/index.ts b/x-pack/legacy/plugins/security/public/hacks/register_account_management_app.ts similarity index 87% rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/index.ts rename to x-pack/legacy/plugins/security/public/hacks/register_account_management_app.ts index eba07aaa17fd2..4fdc2358246b9 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/setup/index.ts +++ b/x-pack/legacy/plugins/security/public/hacks/register_account_management_app.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './setup_steps'; +import '../views/account/account'; diff --git a/x-pack/legacy/plugins/security/public/views/nav_control/nav_control.js b/x-pack/legacy/plugins/security/public/views/nav_control/nav_control.js deleted file mode 100644 index 3cf89a9aa7944..0000000000000 --- a/x-pack/legacy/plugins/security/public/views/nav_control/nav_control.js +++ /dev/null @@ -1,50 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { I18nContext } from 'ui/i18n'; -import React from 'react'; -import ReactDOM from 'react-dom'; - -import chrome from 'ui/chrome'; - -import 'plugins/security/services/shield_user'; -import '../account/account'; -import { Path } from 'plugins/xpack_main/services/path'; -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; - -import { - chromeHeaderNavControlsRegistry, - NavControlSide, -} from 'ui/registry/chrome_header_nav_controls'; -import { SecurityNavControl } from './nav_control_component'; - -chromeHeaderNavControlsRegistry.register((ShieldUser, kbnBaseUrl) => ({ - name: 'security', - order: 1000, - side: NavControlSide.Right, - render(el) { - const showSecurityLinks = xpackInfo.get('features.security.showLinks'); - if (Path.isUnauthenticated() || !showSecurityLinks) return null; - - const props = { - user: ShieldUser.getCurrent(), - editProfileUrl: chrome.addBasePath(`${kbnBaseUrl}#/account`), - logoutUrl: chrome.addBasePath(`/logout`), - }; - - props.user.$promise.then(() => { - // Wait for the user to be propogated before rendering into the DOM. - ReactDOM.render( - - - , - el - ); - }); - - return () => ReactDOM.unmountComponentAtNode(el); - }, -})); diff --git a/x-pack/legacy/plugins/siem/common/constants.ts b/x-pack/legacy/plugins/siem/common/constants.ts index 7b5015c34de14..3b5cdec4dc4d9 100644 --- a/x-pack/legacy/plugins/siem/common/constants.ts +++ b/x-pack/legacy/plugins/siem/common/constants.ts @@ -46,12 +46,14 @@ export const SIGNALS_ID = `${APP_ID}.signals`; */ export const INTERNAL_IDENTIFIER = '__internal'; export const INTERNAL_RULE_ID_KEY = `${INTERNAL_IDENTIFIER}_rule_id`; +export const INTERNAL_IMMUTABLE_KEY = `${INTERNAL_IDENTIFIER}_immutable`; /** * Detection engine routes */ export const DETECTION_ENGINE_URL = '/api/detection_engine'; export const DETECTION_ENGINE_RULES_URL = `${DETECTION_ENGINE_URL}/rules`; +export const DETECTION_ENGINE_PREPACKAGED_URL = `${DETECTION_ENGINE_RULES_URL}/prepackaged`; export const DETECTION_ENGINE_PRIVILEGES_URL = `${DETECTION_ENGINE_URL}/privileges`; export const DETECTION_ENGINE_INDEX_URL = `${DETECTION_ENGINE_URL}/index`; export const DETECTION_ENGINE_TAGS_URL = `${DETECTION_ENGINE_URL}/tags`; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts index 8ba20b3ec0048..655418fc98bf8 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts @@ -49,28 +49,28 @@ export const mlNetworkKqlQuery = // Single host name with a null for the Query: export const mlHostSingleHostNullKqlQuery = - "/app/siem#/ml-hosts/siem-windows?_g=()&query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/siem-windows?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Single host name with a variable in the Query: export const mlHostSingleHostKqlQueryVariable = - "/app/siem#/ml-hosts/siem-windows?_g=()&query=(language:kuery,query:'process.name%20:%20%22$process.name$%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22$process.name$%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Single host name with a value for Query: export const mlHostSingleHostKqlQuery = - "/app/siem#/ml-hosts/siem-windows?_g=()&query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Multiple host names with null for Query: export const mlHostMultiHostNullKqlQuery = - "/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&query=!n&&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/siem-windows,siem-suricata?query=!n&&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Multiple host names with a value for Query: export const mlHostMultiHostKqlQuery = - "/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/siem-windows,siem-suricata?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Undefined/null host name with a null for the KQL: export const mlHostVariableHostNullKqlQuery = - "/app/siem#/ml-hosts/$host.name$?_g=()&query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/$host.name$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Undefined/null host name but with a value for Query: export const mlHostVariableHostKqlQuery = - "/app/siem#/ml-hosts/$host.name$?_g=()&query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/$host.name$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts index ef1892b3d382c..99d90e3c42aca 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts @@ -35,10 +35,10 @@ export const ABSOLUTE_DATE_RANGE = { urlUnlinked: '/app/siem#/network/?timerange=(global:(linkTo:!(),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(),timerange:(from:1564776209186,kind:absolute,to:1564779809186)))', - urlKqlNetworkNetwork: `/app/siem#/network/?_g=()&query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, - urlKqlNetworkHosts: `/app/siem#/network/?_g=()&query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, - urlKqlHostsNetwork: `/app/siem#/hosts/allHosts?_g=()&query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, - urlKqlHostsHosts: `/app/siem#/hosts/allHosts?_g=()&query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + urlKqlNetworkNetwork: `/app/siem#/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + urlKqlNetworkHosts: `/app/siem#/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + urlKqlHostsNetwork: `/app/siem#/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + urlKqlHostsHosts: `/app/siem#/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, urlHost: '/app/siem#/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))', }; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts index a03ff0c1845f8..4c29c081b3e69 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts @@ -104,7 +104,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlNetworkSingleIpNullKqlQuery); cy.url().should( 'include', - '/app/siem#/network/ip/127.0.0.1?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' + '/app/siem#/network/ip/127.0.0.1?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' ); }); @@ -112,7 +112,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlNetworkSingleIpKqlQuery); cy.url().should( 'include', - "/app/siem#/network/ip/127.0.0.1?_g=()&query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + "/app/siem#/network/ip/127.0.0.1?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" ); }); @@ -120,7 +120,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlNetworkMultipleIpNullKqlQuery); cy.url().should( 'include', - "app/siem#/network/flows?_g=()&query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999))" + "app/siem#/network/flows?query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999))" ); }); @@ -128,7 +128,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlNetworkMultipleIpKqlQuery); cy.url().should( 'include', - "/app/siem#/network/flows?_g=()&query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + "/app/siem#/network/flows?query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" ); }); @@ -136,7 +136,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlNetworkNullKqlQuery); cy.url().should( 'include', - '/app/siem#/network/flows?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' + '/app/siem#/network/flows?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' ); }); @@ -144,7 +144,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlNetworkKqlQuery); cy.url().should( 'include', - "/app/siem#/network/flows?_g=()&query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + "/app/siem#/network/flows?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" ); }); @@ -152,7 +152,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlHostSingleHostNullKqlQuery); cy.url().should( 'include', - '/app/siem#/hosts/siem-windows/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + '/app/siem#/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); @@ -160,7 +160,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlHostSingleHostKqlQueryVariable); cy.url().should( 'include', - '/app/siem#/hosts/siem-windows/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + '/app/siem#/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); @@ -168,7 +168,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlHostSingleHostKqlQuery); cy.url().should( 'include', - "/app/siem#/hosts/siem-windows/anomalies?_g=()&query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/siem#/hosts/siem-windows/anomalies?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); @@ -176,7 +176,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlHostMultiHostNullKqlQuery); cy.url().should( 'include', - "/app/siem#/hosts/anomalies?_g=()&query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/siem#/hosts/anomalies?query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); @@ -184,7 +184,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlHostMultiHostKqlQuery); cy.url().should( 'include', - "/app/siem#/hosts/anomalies?_g=()&query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/siem#/hosts/anomalies?query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); @@ -192,7 +192,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlHostVariableHostNullKqlQuery); cy.url().should( 'include', - '/app/siem#/hosts/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + '/app/siem#/hosts/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); @@ -200,7 +200,7 @@ describe('ml conditional links', () => { loginAndWaitForPage(mlHostVariableHostKqlQuery); cy.url().should( 'include', - "/app/siem#/hosts/anomalies?_g=()&query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/siem#/hosts/anomalies?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/flow_controls/flow_target_select.tsx b/x-pack/legacy/plugins/siem/public/components/flow_controls/flow_target_select.tsx index b1c463fdc32f4..f5dd4650159cd 100644 --- a/x-pack/legacy/plugins/siem/public/components/flow_controls/flow_target_select.tsx +++ b/x-pack/legacy/plugins/siem/public/components/flow_controls/flow_target_select.tsx @@ -5,7 +5,7 @@ */ import { EuiSuperSelect } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback } from 'react'; import { ActionCreator } from 'typescript-fsa'; import { FlowDirection, FlowTarget } from '../../graphql/types'; @@ -65,22 +65,27 @@ export const FlowTargetSelect = React.memo( selectedTarget, displayTextOverride = [], updateFlowTargetAction, - }) => ( - - option.directions.includes(selectedDirection) - ) - : toggleTargetOptions(id, displayTextOverride) - } - valueOfSelected={selectedTarget} - onChange={(newFlowTarget: FlowTarget) => - onChangeTarget(newFlowTarget, updateFlowTargetAction) - } - isLoading={isLoading} - /> - ) + }) => { + const handleChange = useCallback( + (newFlowTarget: FlowTarget) => onChangeTarget(newFlowTarget, updateFlowTargetAction), + [updateFlowTargetAction] + ); + + return ( + + option.directions.includes(selectedDirection) + ) + : toggleTargetOptions(id, displayTextOverride) + } + valueOfSelected={selectedTarget} + onChange={handleChange} + isLoading={isLoading} + /> + ); + } ); FlowTargetSelect.displayName = 'FlowTargetSelect'; diff --git a/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx b/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx index 6908aba542e4c..04d6d94d6624d 100644 --- a/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx @@ -6,7 +6,7 @@ import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; import { getOr } from 'lodash/fp'; -import React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { ActionCreator } from 'typescript-fsa'; import styled from 'styled-components'; @@ -72,72 +72,71 @@ const InspectButtonComponent = React.memo( setIsInspected, show, title = '', - }: InspectButtonProps) => ( - - {inputId === 'timeline' && !compact && ( - { - setIsInspected({ - id: queryId, - inputId, - isInspected: true, - selectedInspectIndex: inspectIndex, - }); - }} - > - {i18n.INSPECT} - - )} - {(inputId === 'global' || compact) && ( - { - setIsInspected({ - id: queryId, - inputId, - isInspected: true, - selectedInspectIndex: inspectIndex, - }); - }} - /> - )} - { - if (onCloseInspect != null) { - onCloseInspect(); + }: InspectButtonProps) => { + const handleClick = useCallback(() => { + setIsInspected({ + id: queryId, + inputId, + isInspected: true, + selectedInspectIndex: inspectIndex, + }); + }, [setIsInspected, queryId, inputId, inspectIndex]); + + const handleCloseModal = useCallback(() => { + if (onCloseInspect != null) { + onCloseInspect(); + } + setIsInspected({ + id: queryId, + inputId, + isInspected: false, + selectedInspectIndex: inspectIndex, + }); + }, [onCloseInspect, setIsInspected, queryId, inputId, inspectIndex]); + + return ( + + {inputId === 'timeline' && !compact && ( + + {i18n.INSPECT} + + )} + {(inputId === 'global' || compact) && ( + + )} + 0 ? inspect.dsl[inspectIndex] : null} + response={ + inspect != null && inspect.response.length > 0 ? inspect.response[inspectIndex] : null } - setIsInspected({ - id: queryId, - inputId, - isInspected: false, - selectedInspectIndex: inspectIndex, - }); - }} - isShowing={!loading && selectedInspectIndex === inspectIndex && isInspected} - request={inspect != null && inspect.dsl.length > 0 ? inspect.dsl[inspectIndex] : null} - response={ - inspect != null && inspect.response.length > 0 ? inspect.response[inspectIndex] : null - } - title={title} - data-test-subj="inspect-modal" - /> - - ) + title={title} + data-test-subj="inspect-modal" + /> + + ); + } ); InspectButtonComponent.displayName = 'InspectButtonComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx index f79c61a29c26b..e1ccfd79a89a0 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { ScaleType } from '@elastic/charts'; import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; @@ -54,14 +54,17 @@ export const MatrixHistogram = ({ if (totalCount >= 0 && loadingInitial) { setLoadingInitial(false); } - }, [loading]); + }, [loading, loadingInitial, totalCount]); + + const handleOnMouseEnter = useCallback(() => setShowInspect(true), []); + const handleOnMouseLeave = useCallback(() => setShowInspect(false), []); return ( setShowInspect(true)} - onMouseLeave={() => setShowInspect(false)} + onMouseEnter={handleOnMouseEnter} + onMouseLeave={handleOnMouseLeave} > { path: '/_ml/anomaly_detectors/siem-api-suspicious_login_activity_ecs', query: {}, body: - '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually high number of authentication attempts (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"high number of authentication attempts","function":"high_non_zero_count","partition_field_name":"host.name"}],"influencers":["host.name","user.name","source.ip"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"IP Address Details","url_value":"siem#/ml-network/ip/$source.ip$?_g=()&query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', + '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually high number of authentication attempts (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"high number of authentication attempts","function":"high_non_zero_count","partition_field_name":"host.name"}],"influencers":["host.name","user.name","source.ip"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"IP Address Details","url_value":"siem#/ml-network/ip/$source.ip$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', statusCode: 400, response: '{"error":{"root_cause":[{"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index"}],"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index","caused_by":{"type":"illegal_argument_exception","reason":"mapper [multi_bucket_impact] of different type, current_type [keyword], merged_type [double]"}},"status":400}', @@ -245,7 +245,7 @@ describe('throw_if_not_ok', () => { path: '/_ml/anomaly_detectors/siem-api-rare_process_linux_ecs', query: {}, body: - '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually rare processes on Linux (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare process executions on Linux","function":"rare","by_field_name":"process.name","partition_field_name":"host.name"}],"influencers":["host.name","process.name","user.name"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?_g=()&query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?_g=()&query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?_g=()&query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?_g=()&query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', + '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually rare processes on Linux (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare process executions on Linux","function":"rare","by_field_name":"process.name","partition_field_name":"host.name"}],"influencers":["host.name","process.name","user.name"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', statusCode: 400, response: '{"error":{"root_cause":[{"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index"}],"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index","caused_by":{"type":"illegal_argument_exception","reason":"mapper [multi_bucket_impact] of different type, current_type [keyword], merged_type [double]"}},"status":400}', @@ -320,7 +320,7 @@ describe('throw_if_not_ok', () => { path: '/_ml/anomaly_detectors/siem-api-suspicious_login_activity_ecs', query: {}, body: - '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually high number of authentication attempts (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"high number of authentication attempts","function":"high_non_zero_count","partition_field_name":"host.name"}],"influencers":["host.name","user.name","source.ip"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"IP Address Details","url_value":"siem#/ml-network/ip/$source.ip$?_g=()&query=!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', + '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually high number of authentication attempts (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"high number of authentication attempts","function":"high_non_zero_count","partition_field_name":"host.name"}],"influencers":["host.name","user.name","source.ip"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"IP Address Details","url_value":"siem#/ml-network/ip/$source.ip$?query=!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', statusCode: 400, response: '{"error":{"root_cause":[{"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index"}],"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index","caused_by":{"type":"illegal_argument_exception","reason":"mapper [multi_bucket_impact] of different type, current_type [keyword], merged_type [double]"}},"status":400}', @@ -335,7 +335,7 @@ describe('throw_if_not_ok', () => { path: '/_ml/anomaly_detectors/siem-api-rare_process_linux_ecs', query: {}, body: - '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually rare processes on Linux (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare process executions on Linux","function":"rare","by_field_name":"process.name","partition_field_name":"host.name"}],"influencers":["host.name","process.name","user.name"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?_g=()&query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?_g=()&query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?_g=()&query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?_g=()&query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', + '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually rare processes on Linux (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare process executions on Linux","function":"rare","by_field_name":"process.name","partition_field_name":"host.name"}],"influencers":["host.name","process.name","user.name"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', statusCode: 400, response: '{"error":{"root_cause":[{"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index"}],"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index","caused_by":{"type":"illegal_argument_exception","reason":"mapper [multi_bucket_impact] of different type, current_type [keyword], merged_type [double]"}},"status":400}', diff --git a/x-pack/legacy/plugins/siem/public/components/ml/score/draggable_score.tsx b/x-pack/legacy/plugins/siem/public/components/ml/score/draggable_score.tsx index 6ae31c0ac1fb9..732eaf4bc5e78 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/score/draggable_score.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/score/draggable_score.tsx @@ -20,41 +20,45 @@ export const DraggableScoreComponent = ({ id: string; index?: number; score: Anomaly; -}): JSX.Element => ( - - snapshot.isDragging ? ( - - - - ) : ( - <> - {index !== 0 && ( - <> - {','} - - - )} - {getScoreString(score.severity)} - - ) - } - /> -); +}): JSX.Element => { + const scoreString = getScoreString(score.severity); + + return ( + + snapshot.isDragging ? ( + + + + ) : ( + <> + {index !== 0 && ( + <> + {','} + + + )} + {scoreString} + + ) + } + /> + ); +}; DraggableScoreComponent.displayName = 'DraggableScoreComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/__mocks__/api.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/__mocks__/api.tsx index 76c276cf69b63..54bb0a96207e1 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/__mocks__/api.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/__mocks__/api.tsx @@ -145,22 +145,22 @@ export const mockGetModuleResponse: Module[] = [ { url_name: 'Host Details by process name', url_value: - "siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts/$host.name$?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, { url_name: 'Host Details by user name', url_value: - "siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts/$host.name$?kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, { url_name: 'Hosts Overview by process name', url_value: - "siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, { url_name: 'Hosts Overview by user name', url_value: - "siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts?kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, ], }, @@ -221,22 +221,22 @@ export const mockGetModuleResponse: Module[] = [ { url_name: 'Host Details by process name', url_value: - "siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts/$host.name$?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, { url_name: 'Host Details by user name', url_value: - "siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts/$host.name$?kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, { url_name: 'Hosts Overview by process name', url_value: - "siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, { url_name: 'Hosts Overview by user name', url_value: - "siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts?kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, ], }, @@ -268,22 +268,22 @@ export const mockGetModuleResponse: Module[] = [ { url_name: 'Host Details by process name', url_value: - "siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts/$host.name$?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, { url_name: 'Host Details by user name', url_value: - "siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts/$host.name$?kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, { url_name: 'Hosts Overview by process name', url_value: - "siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, { url_name: 'Hosts Overview by user name', url_value: - "siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + "siem#/ml-hosts?kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", }, ], }, @@ -360,7 +360,7 @@ export const mockSetupMlJobAllError: SetupMlResponse = { path: '/_ml/anomaly_detectors/linux_anomalous_network_url_activity_ecs', query: {}, body: - '{"job_type":"anomaly_detector","groups":["siem","auditbeat","process"],"description":"SIEM Auditbeat: Looks for an unusual web URL request from a Linux instance. Curl and wget web request activity is very common but unusual web requests from a Linux server can sometimes be malware delivery or execution (beta)","analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare by \\"process.title\\"","function":"rare","by_field_name":"process.title"}],"influencers":["host.name","destination.ip","destination.port"]},"analysis_limits":{"model_memory_limit":"32mb"},"data_description":{"time_field":"@timestamp"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details","url_value":"siem#/ml-hosts/$host.name$?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]},"results_index_name":"linux_anomalous_network_url_activity_ecs"}', + '{"job_type":"anomaly_detector","groups":["siem","auditbeat","process"],"description":"SIEM Auditbeat: Looks for an unusual web URL request from a Linux instance. Curl and wget web request activity is very common but unusual web requests from a Linux server can sometimes be malware delivery or execution (beta)","analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare by \\"process.title\\"","function":"rare","by_field_name":"process.title"}],"influencers":["host.name","destination.ip","destination.port"]},"analysis_limits":{"model_memory_limit":"32mb"},"data_description":{"time_field":"@timestamp"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details","url_value":"siem#/ml-hosts/$host.name$?timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]},"results_index_name":"linux_anomalous_network_url_activity_ecs"}', statusCode: 400, response: '{"error":{"root_cause":[{"type":"resource_already_exists_exception","reason":"The job cannot be created with the Id \'linux_anomalous_network_url_activity_ecs\'. The Id is already used."}],"type":"resource_already_exists_exception","reason":"The job cannot be created with the Id \'linux_anomalous_network_url_activity_ecs\'. The Id is already used."},"status":400}', @@ -375,7 +375,7 @@ export const mockSetupMlJobAllError: SetupMlResponse = { path: '/_ml/anomaly_detectors/linux_anomalous_network_port_activity_ecs', query: {}, body: - '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Looks for unusual destination port activity that could indicate command-and-control, persistence mechanism, or data exfiltration activity (beta)","groups":["siem","auditbeat","process"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare by \\"destination.port\\"","function":"rare","by_field_name":"destination.port"}],"influencers":["host.name","process.name","user.name","destination.ip"]},"analysis_limits":{"model_memory_limit":"32mb"},"data_description":{"time_field":"@timestamp"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]},"results_index_name":"linux_anomalous_network_port_activity_ecs"}', + '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Looks for unusual destination port activity that could indicate command-and-control, persistence mechanism, or data exfiltration activity (beta)","groups":["siem","auditbeat","process"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare by \\"destination.port\\"","function":"rare","by_field_name":"destination.port"}],"influencers":["host.name","process.name","user.name","destination.ip"]},"analysis_limits":{"model_memory_limit":"32mb"},"data_description":{"time_field":"@timestamp"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]},"results_index_name":"linux_anomalous_network_port_activity_ecs"}', statusCode: 400, response: '{"error":{"root_cause":[{"type":"resource_already_exists_exception","reason":"The job cannot be created with the Id \'linux_anomalous_network_port_activity_ecs\'. The Id is already used."}],"type":"resource_already_exists_exception","reason":"The job cannot be created with the Id \'linux_anomalous_network_port_activity_ecs\'. The Id is already used."},"status":400}', @@ -430,7 +430,7 @@ export const mockSetupMlJobSingleErrorSingleSuccess: SetupMlResponse = { path: '/_ml/anomaly_detectors/linux_anomalous_network_activity_ecs', query: {}, body: - '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Looks for unusual processes using the network which could indicate command-and-control, lateral movement, persistence, or data exfiltration activity (beta)","groups":["siem","auditbeat","network"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare by \\"process.name\\"","function":"rare","by_field_name":"process.name"}],"influencers":["host.name","process.name","user.name","destination.ip"]},"analysis_limits":{"model_memory_limit":"64mb"},"data_description":{"time_field":"@timestamp"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]},"results_index_name":"linux_anomalous_network_activity_ecs"}', + '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Looks for unusual processes using the network which could indicate command-and-control, lateral movement, persistence, or data exfiltration activity (beta)","groups":["siem","auditbeat","network"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare by \\"process.name\\"","function":"rare","by_field_name":"process.name"}],"influencers":["host.name","process.name","user.name","destination.ip"]},"analysis_limits":{"model_memory_limit":"64mb"},"data_description":{"time_field":"@timestamp"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]},"results_index_name":"linux_anomalous_network_activity_ecs"}', statusCode: 400, response: '{"error":{"root_cause":[{"type":"resource_already_exists_exception","reason":"The job cannot be created with the Id \'linux_anomalous_network_activity_ecs\'. The Id is already used."}],"type":"resource_already_exists_exception","reason":"The job cannot be created with the Id \'linux_anomalous_network_activity_ecs\'. The Id is already used."},"status":400}', diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/filters/jobs_table_filters.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/filters/jobs_table_filters.tsx index 74e61f27fb2d1..551ed5f08bd76 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/filters/jobs_table_filters.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/filters/jobs_table_filters.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import React, { Dispatch, SetStateAction, useEffect, useState, useCallback } from 'react'; import { EuiFilterButton, @@ -40,7 +40,22 @@ export const JobsTableFiltersComponent = ({ siemJobs, onFilterChanged }: JobsTab // Propagate filter changes to parent useEffect(() => { onFilterChanged({ filterQuery, showCustomJobs, showElasticJobs, selectedGroups }); - }, [filterQuery, selectedGroups.sort().join(), showCustomJobs, showElasticJobs]); + }, [filterQuery, selectedGroups, showCustomJobs, showElasticJobs, onFilterChanged]); + + const handleChange = useCallback( + (query: EuiSearchBarQuery) => setFilterQuery(query.queryText.trim()), + [setFilterQuery] + ); + + const handleElasticJobsClick = useCallback(() => { + setShowElasticJobs(!showElasticJobs); + setShowCustomJobs(false); + }, [setShowElasticJobs, showElasticJobs, setShowCustomJobs]); + + const handleCustomJobsClick = useCallback(() => { + setShowCustomJobs(!showCustomJobs); + setShowElasticJobs(false); + }, [setShowElasticJobs, showCustomJobs, setShowCustomJobs]); return ( @@ -51,7 +66,7 @@ export const JobsTableFiltersComponent = ({ siemJobs, onFilterChanged }: JobsTab placeholder: i18n.FILTER_PLACEHOLDER, incremental: true, }} - onChange={(query: EuiSearchBarQuery) => setFilterQuery(query.queryText.trim())} + onChange={handleChange} />
@@ -65,10 +80,7 @@ export const JobsTableFiltersComponent = ({ siemJobs, onFilterChanged }: JobsTab { - setShowElasticJobs(!showElasticJobs); - setShowCustomJobs(false); - }} + onClick={handleElasticJobsClick} data-test-subj="show-elastic-jobs-filter-button" withNext > @@ -76,10 +88,7 @@ export const JobsTableFiltersComponent = ({ siemJobs, onFilterChanged }: JobsTab { - setShowCustomJobs(!showCustomJobs); - setShowElasticJobs(false); - }} + onClick={handleCustomJobsClick} data-test-subj="show-custom-jobs-filter-button" > {i18n.SHOW_CUSTOM_JOBS} diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx index c915bf04126ec..0d503e2db3d9d 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx @@ -5,7 +5,7 @@ */ import styled from 'styled-components'; -import React, { useState } from 'react'; +import React, { useState, useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSwitch } from '@elastic/eui'; import { SiemJob } from '../types'; @@ -47,6 +47,13 @@ export const JobSwitchComponent = ({ onJobStateChange, }: JobSwitchProps) => { const [isLoading, setIsLoading] = useState(false); + const handleChange = useCallback( + e => { + setIsLoading(true); + onJobStateChange(job, job.latestTimestampMs || 0, e.target.checked); + }, + [job, setIsLoading, onJobStateChange] + ); return ( @@ -58,10 +65,7 @@ export const JobSwitchComponent = ({ data-test-subj="job-switch" disabled={isFailure(job.jobState, job.datafeedState)} checked={isChecked(job.jobState, job.datafeedState)} - onChange={e => { - setIsLoading(true); - onJobStateChange(job, job.latestTimestampMs || 0, e.target.checked); - }} + onChange={handleChange} showLabel={false} label="" /> diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx index 00b1d4c066d4a..840e2bf3f42dc 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { mount, shallow } from 'enzyme'; +import { mount } from 'enzyme'; import * as React from 'react'; import { navTabs } from '../../../pages/home/home_navigations'; @@ -62,13 +62,13 @@ describe('Tab Navigation', () => { }, }; test('it mounts with correct tab highlighted', () => { - const wrapper = shallow(); - const hostsTab = wrapper.find('[data-test-subj="navigation-hosts"]'); + const wrapper = mount(); + const hostsTab = wrapper.find('EuiTab[data-test-subj="navigation-hosts"]'); expect(hostsTab.prop('isSelected')).toBeTruthy(); }); test('it changes active tab when nav changes by props', () => { const wrapper = mount(); - const networkTab = () => wrapper.find('[data-test-subj="navigation-network"]').first(); + const networkTab = () => wrapper.find('EuiTab[data-test-subj="navigation-network"]').first(); expect(networkTab().prop('isSelected')).toBeFalsy(); wrapper.setProps({ pageName: 'network', @@ -79,8 +79,8 @@ describe('Tab Navigation', () => { expect(networkTab().prop('isSelected')).toBeTruthy(); }); test('it carries the url state in the link', () => { - const wrapper = shallow(); - const firstTab = wrapper.find('[data-test-subj="navigation-network"]'); + const wrapper = mount(); + const firstTab = wrapper.find('EuiTab[data-test-subj="navigation-network"]'); expect(firstTab.props().href).toBe( "#/link-to/network?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))" ); @@ -126,9 +126,9 @@ describe('Tab Navigation', () => { }, }; test('it mounts with correct tab highlighted', () => { - const wrapper = shallow(); + const wrapper = mount(); const tableNavigationTab = wrapper.find( - `[data-test-subj="navigation-${HostsTableType.authentications}"]` + `EuiTab[data-test-subj="navigation-${HostsTableType.authentications}"]` ); expect(tableNavigationTab.prop('isSelected')).toBeTruthy(); @@ -147,9 +147,9 @@ describe('Tab Navigation', () => { expect(tableNavigationTab().prop('isSelected')).toBeTruthy(); }); test('it carries the url state in the link', () => { - const wrapper = shallow(); + const wrapper = mount(); const firstTab = wrapper.find( - `[data-test-subj="navigation-${HostsTableType.authentications}"]` + `EuiTab[data-test-subj="navigation-${HostsTableType.authentications}"]` ); expect(firstTab.props().href).toBe( `#/${pageName}/${hostName}/${HostsTableType.authentications}?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))` diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx index d405ec404b111..b653624ec1f67 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx @@ -5,23 +5,52 @@ */ import { EuiTab, EuiTabs } from '@elastic/eui'; import { getOr } from 'lodash/fp'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback, useMemo } from 'react'; import { trackUiAction as track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../lib/track_usage'; import { getSearch } from '../helpers'; -import { TabNavigationProps } from './types'; +import { TabNavigationProps, TabNavigationItemProps } from './types'; + +const TabNavigationItemComponent = ({ + href, + hrefWithSearch, + id, + disabled, + name, + isSelected, +}: TabNavigationItemProps) => { + const handleClick = useCallback(() => { + track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.TAB_CLICKED}${id}`); + }, [id]); + + return ( + + {name} + + ); +}; + +const TabNavigationItem = React.memo(TabNavigationItemComponent); export const TabNavigationComponent = (props: TabNavigationProps) => { const { display, navTabs, pageName, tabName } = props; - const mapLocationToTab = (): string => { - return getOr( - '', - 'id', - Object.values(navTabs).find(item => tabName === item.id || pageName === item.id) - ); - }; - + const mapLocationToTab = useCallback( + (): string => + getOr( + '', + 'id', + Object.values(navTabs).find(item => tabName === item.id || pageName === item.id) + ), + [pageName, tabName, navTabs] + ); const [selectedTabId, setSelectedTabId] = useState(mapLocationToTab()); useEffect(() => { const currentTabSelected = mapLocationToTab(); @@ -31,26 +60,30 @@ export const TabNavigationComponent = (props: TabNavigationProps) => { } // we do need navTabs in case the selectedTabId appears after initial load (ex. checking permissions for anomalies) - }, [pageName, tabName, navTabs]); - - const renderTabs = (): JSX.Element[] => - Object.values(navTabs).map(tab => ( - { - track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.TAB_CLICKED}${tab.id}`); - }} - > - {tab.name} - - )); - - return {renderTabs()}; + }, [pageName, tabName, navTabs, mapLocationToTab, selectedTabId]); + + const renderTabs = useMemo( + () => + Object.values(navTabs).map(tab => { + const isSelected = selectedTabId === tab.id; + const hrefWithSearch = tab.href + getSearch(tab, props); + + return ( + + ); + }), + [navTabs, selectedTabId, props] + ); + + return {renderTabs}; }; TabNavigationComponent.displayName = 'TabNavigationComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts index 1283691e65806..3fac783b55047 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts @@ -22,3 +22,12 @@ export interface TabNavigationProps extends SiemNavigationProps { [CONSTANTS.timerange]: UrlInputsModel; [CONSTANTS.timeline]: Timeline; } + +export interface TabNavigationItemProps { + href: string; + hrefWithSearch: string; + id: string; + disabled: boolean; + name: string; + isSelected: boolean; +} diff --git a/x-pack/legacy/plugins/siem/public/components/notes/add_note/index.tsx b/x-pack/legacy/plugins/siem/public/components/notes/add_note/index.tsx index 6e13538cf9bea..28cab2b46755f 100644 --- a/x-pack/legacy/plugins/siem/public/components/notes/add_note/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/notes/add_note/index.tsx @@ -5,7 +5,7 @@ */ import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import * as React from 'react'; +import React, { useCallback } from 'react'; import styled from 'styled-components'; import { MarkdownHint } from '../../markdown/markdown_hint'; @@ -49,38 +49,44 @@ export const AddNote = React.memo<{ onCancelAddNote?: () => void; updateNewNote: UpdateInternalNewNote; updateNote: UpdateNote; -}>(({ associateNote, getNewNoteId, newNote, onCancelAddNote, updateNewNote, updateNote }) => ( - - - - 0} /> - - - {onCancelAddNote != null ? ( +}>(({ associateNote, getNewNoteId, newNote, onCancelAddNote, updateNewNote, updateNote }) => { + const handleClick = useCallback( + () => + updateAndAssociateNode({ + associateNote, + getNewNoteId, + newNote, + updateNewNote, + updateNote, + }), + [associateNote, getNewNoteId, newNote, updateNewNote, updateNote] + ); + + return ( + + + + 0} /> + + + {onCancelAddNote != null ? ( + + + + ) : null} - + + {i18n.ADD_NOTE} + - ) : null} - - - updateAndAssociateNode({ - associateNote, - getNewNoteId, - newNote, - updateNewNote, - updateNote, - }) - } - > - {i18n.ADD_NOTE} - - - - -)); + + + ); +}); AddNote.displayName = 'AddNote'; diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx index 7efcde5b89c67..ad5755068e662 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx @@ -5,7 +5,7 @@ */ import { has } from 'lodash/fp'; -import React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { ActionCreator } from 'typescript-fsa'; @@ -98,39 +98,49 @@ const AuthenticationTableComponent = React.memo( type, updateTableActivePage, updateTableLimit, - }) => ( - loadPage(newActivePage)} - pageOfItems={data} - showMorePagesIndicator={showMorePagesIndicator} - totalCount={fakeTotalCount} - updateLimitPagination={newLimit => + }) => { + const updateLimitPagination = useCallback( + newLimit => updateTableLimit({ hostsType: type, limit: newLimit, tableType, - }) - } - updateActivePage={newPage => + }), + [type, updateTableLimit] + ); + + const updateActivePage = useCallback( + newPage => updateTableActivePage({ activePage: newPage, hostsType: type, tableType, - }) - } - /> - ) + }), + [type, updateTableActivePage] + ); + + return ( + + ); + } ); AuthenticationTableComponent.displayName = 'AuthenticationTableComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx index ba953d8e74b29..437d14edeb5c8 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx @@ -8,7 +8,7 @@ import { EuiFlexItem } from '@elastic/eui'; import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; import { getOr } from 'lodash/fp'; -import React, { useContext, useState } from 'react'; +import React, { useContext, useState, useCallback } from 'react'; import { DEFAULT_DARK_MODE } from '../../../../../common/constants'; import { DescriptionList } from '../../../../../common/utility_types'; @@ -165,11 +165,11 @@ export const HostOverview = React.memo( ], ]; + const handleOnMouseEnter = useCallback(() => setShowInspect(true), []); + const handleOnMouseLeave = useCallback(() => setShowInspect(false), []); + return ( - setShowInspect(true)} - onMouseLeave={() => setShowInspect(false)} - > + ( updateTableActivePage, updateTableLimit, }) => { + const updateLimitPagination = useCallback( + newLimit => + updateTableLimit({ + hostsType: type, + limit: newLimit, + tableType, + }), + [type, updateTableLimit] + ); + + const updateActivePage = useCallback( + newPage => + updateTableActivePage({ + activePage: newPage, + hostsType: type, + tableType, + }), + [type, updateTableActivePage] + ); + const onChange = useCallback( (criteria: Criteria) => { if (criteria.sort != null) { @@ -130,7 +150,7 @@ const HostsTableComponent = React.memo( } } }, - [direction, sortField, type] + [direction, sortField, type, updateHostsSort] ); const hostsColumns = useMemo(() => getHostsColumns(), []); @@ -153,26 +173,14 @@ const HostsTableComponent = React.memo( itemsPerRow={rowItems} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} onChange={onChange} pageOfItems={data} showMorePagesIndicator={showMorePagesIndicator} sorting={sorting} totalCount={fakeTotalCount} - updateLimitPagination={newLimit => - updateTableLimit({ - hostsType: type, - limit: newLimit, - tableType, - }) - } - updateActivePage={newPage => - updateTableActivePage({ - activePage: newPage, - hostsType: type, - tableType, - }) - } + updateLimitPagination={updateLimitPagination} + updateActivePage={updateActivePage} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx index a4fd4d636b35e..a8b9b2a1f61ce 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { ActionCreator } from 'typescript-fsa'; @@ -96,39 +96,49 @@ const UncommonProcessTableComponent = React.memo( updateTableActivePage, updateTableLimit, type, - }) => ( - loadPage(newActivePage)} - pageOfItems={data} - showMorePagesIndicator={showMorePagesIndicator} - totalCount={fakeTotalCount} - updateLimitPagination={newLimit => + }) => { + const updateLimitPagination = useCallback( + newLimit => updateTableLimit({ hostsType: type, limit: newLimit, tableType, - }) - } - updateActivePage={newPage => + }), + [type, updateTableLimit] + ); + + const updateActivePage = useCallback( + newPage => updateTableActivePage({ activePage: newPage, hostsType: type, tableType, - }) - } - /> - ) + }), + [type, updateTableActivePage] + ); + + return ( + + ); + } ); UncommonProcessTableComponent.displayName = 'UncommonProcessTableComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx index 70b4d03362438..8cb55f0d0fb58 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx @@ -7,7 +7,7 @@ import { EuiFlexItem } from '@elastic/eui'; import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; -import React, { useContext, useState } from 'react'; +import React, { useContext, useState, useCallback } from 'react'; import { DEFAULT_DARK_MODE } from '../../../../../common/constants'; import { DescriptionList } from '../../../../../common/utility_types'; @@ -139,11 +139,12 @@ export const IpOverview = React.memo( { title: i18n.REPUTATION, description: reputationRenderer(ip) }, ], ]; + + const handleOnMouseEnter = useCallback(() => setShowInspect(true), []); + const handleOnMouseLeave = useCallback(() => setShowInspect(false), []); + return ( - setShowInspect(true)} - onMouseLeave={() => setShowInspect(false)} - > + ( type, updateNetworkTable, }) => { + const updateLimitPagination = useCallback( + newLimit => + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }), + [type, updateNetworkTable] + ); + + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [type, updateNetworkTable] + ); + const onChange = useCallback( (criteria: Criteria) => { if (criteria.sort != null) { @@ -93,7 +113,7 @@ export const NetworkDnsTableComponent = React.memo( } } }, - [sort, type] + [sort, type, updateNetworkTable] ); const onChangePtrIncluded = useCallback( @@ -103,7 +123,7 @@ export const NetworkDnsTableComponent = React.memo( tableType, updates: { isPtrIncluded: !isPtrIncluded }, }), - [type, isPtrIncluded] + [type, updateNetworkTable, isPtrIncluded] ); return ( @@ -123,7 +143,7 @@ export const NetworkDnsTableComponent = React.memo( isInspect={isInspect} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} onChange={onChange} pageOfItems={data} showMorePagesIndicator={showMorePagesIndicator} @@ -132,20 +152,8 @@ export const NetworkDnsTableComponent = React.memo( direction: sort.direction, }} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ - networkType: type, - tableType, - updates: { limit: newLimit }, - }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx index e5ad39b814caa..d25d94a06b818 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { compose } from 'redux'; import { ActionCreator } from 'typescript-fsa'; @@ -76,23 +76,50 @@ const NetworkHttpTableComponent = React.memo( type, updateNetworkTable, }) => { - const onChange = (criteria: Criteria, tableType: networkModel.HttpTableType) => { - if (criteria.sort != null && criteria.sort.direction !== sort.direction) { - updateNetworkTable({ - networkType: type, - tableType, - updates: { - sort: { - direction: criteria.sort.direction, - }, - }, - }); - } - }; const tableType = type === networkModel.NetworkType.page ? networkModel.NetworkTableType.http : networkModel.IpDetailsTableType.http; + + const updateLimitPagination = useCallback( + newLimit => + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }), + [type, updateNetworkTable, tableType] + ); + + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [type, updateNetworkTable, tableType] + ); + + const onChange = useCallback( + (criteria: Criteria) => { + if (criteria.sort != null && criteria.sort.direction !== sort.direction) { + updateNetworkTable({ + networkType: type, + tableType, + updates: { + sort: { + direction: criteria.sort.direction, + }, + }, + }); + } + }, + [tableType, sort.direction, type, updateNetworkTable] + ); + + const sorting = { field: `node.${NetworkHttpFields.requestCount}`, direction: sort.direction }; + return ( ( isInspect={isInspect} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} - onChange={criteria => onChange(criteria, tableType)} + loadPage={loadPage} + onChange={onChange} pageOfItems={data} showMorePagesIndicator={showMorePagesIndicator} - sorting={{ field: `node.${NetworkHttpFields.requestCount}`, direction: sort.direction }} + sorting={sorting} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ - networkType: type, - tableType, - updates: { limit: newLimit }, - }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx index 81a48ca162e15..8fd5cc9b3f3c0 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx @@ -5,7 +5,7 @@ */ import { isEqual, last } from 'lodash/fp'; -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import { connect } from 'react-redux'; import { compose } from 'redux'; import { ActionCreator } from 'typescript-fsa'; @@ -88,27 +88,6 @@ const NetworkTopCountriesTableComponent = React.memo { - const onChange = (criteria: Criteria, tableType: networkModel.TopCountriesTableType) => { - if (criteria.sort != null) { - const splitField = criteria.sort.field.split('.'); - const field = last(splitField); - const newSortDirection = field !== sort.field ? Direction.desc : criteria.sort.direction; // sort by desc on init click - const newTopCountriesSort: NetworkTopTablesSortField = { - field: field as NetworkTopTablesFields, - direction: newSortDirection, - }; - if (!isEqual(newTopCountriesSort, sort)) { - updateNetworkTable({ - networkType: type, - tableType, - updates: { - sort: newTopCountriesSort, - }, - }); - } - } - }; - let tableType: networkModel.TopCountriesTableType; const headerTitle: string = flowTargeted === FlowTargetSourceDest.source @@ -133,15 +112,61 @@ const NetworkTopCountriesTableComponent = React.memo + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }), + [type, updateNetworkTable, tableType] + ); + + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [type, updateNetworkTable, tableType] + ); + + const onChange = useCallback( + (criteria: Criteria) => { + if (criteria.sort != null) { + const splitField = criteria.sort.field.split('.'); + const lastField = last(splitField); + const newSortDirection = + lastField !== sort.field ? Direction.desc : criteria.sort.direction; // sort by desc on init click + const newTopCountriesSort: NetworkTopTablesSortField = { + field: lastField as NetworkTopTablesFields, + direction: newSortDirection, + }; + if (!isEqual(newTopCountriesSort, sort)) { + updateNetworkTable({ + networkType: type, + tableType, + updates: { + sort: newTopCountriesSort, + }, + }); + } + } + }, + [type, sort, tableType, updateNetworkTable] + ); + + const columns = useMemo( + () => + getCountriesColumnsCurated(indexPattern, flowTargeted, type, NetworkTopCountriesTableId), + [indexPattern, flowTargeted, type] + ); + return ( loadPage(newActivePage)} - onChange={criteria => onChange(criteria, tableType)} + loadPage={loadPage} + onChange={onChange} pageOfItems={data} showMorePagesIndicator={showMorePagesIndicator} sorting={{ field, direction: sort.direction }} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ - networkType: type, - tableType, - updates: { limit: newLimit }, - }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx index 12b87a517b4f7..c38d809458163 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { isEqual, last } from 'lodash/fp'; -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { connect } from 'react-redux'; import { compose } from 'redux'; import { ActionCreator } from 'typescript-fsa'; @@ -87,8 +87,29 @@ const NetworkTopNFlowTableComponent = React.memo( type, updateNetworkTable, }) => { + const columns = useMemo( + () => getNFlowColumnsCurated(indexPattern, flowTargeted, type, NetworkTopNFlowTableId), + [indexPattern, flowTargeted, type] + ); + + let tableType: networkModel.TopNTableType; + const headerTitle: string = + flowTargeted === FlowTargetSourceDest.source ? i18n.SOURCE_IP : i18n.DESTINATION_IP; + + if (type === networkModel.NetworkType.page) { + tableType = + flowTargeted === FlowTargetSourceDest.source + ? networkModel.NetworkTableType.topNFlowSource + : networkModel.NetworkTableType.topNFlowDestination; + } else { + tableType = + flowTargeted === FlowTargetSourceDest.source + ? networkModel.IpDetailsTableType.topNFlowSource + : networkModel.IpDetailsTableType.topNFlowDestination; + } + const onChange = useCallback( - (criteria: Criteria, tableType: networkModel.TopNTableType) => { + (criteria: Criteria) => { if (criteria.sort != null) { const splitField = criteria.sort.field.split('.'); const field = last(splitField); @@ -108,35 +129,35 @@ const NetworkTopNFlowTableComponent = React.memo( } } }, - [sort, type] + [sort, type, tableType, updateNetworkTable] ); - let tableType: networkModel.TopNTableType; - const headerTitle: string = - flowTargeted === FlowTargetSourceDest.source ? i18n.SOURCE_IP : i18n.DESTINATION_IP; - - if (type === networkModel.NetworkType.page) { - tableType = - flowTargeted === FlowTargetSourceDest.source - ? networkModel.NetworkTableType.topNFlowSource - : networkModel.NetworkTableType.topNFlowDestination; - } else { - tableType = - flowTargeted === FlowTargetSourceDest.source - ? networkModel.IpDetailsTableType.topNFlowSource - : networkModel.IpDetailsTableType.topNFlowDestination; - } - const field = sort.field === NetworkTopTablesFields.bytes_out || sort.field === NetworkTopTablesFields.bytes_in ? `node.network.${sort.field}` : `node.${flowTargeted}.${sort.field}`; + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [updateNetworkTable, type, tableType] + ); + + const updateLimitPagination = useCallback( + newLimit => + updateNetworkTable({ networkType: type, tableType, updates: { limit: newLimit } }), + [updateNetworkTable, type, tableType] + ); + return ( ( itemsPerRow={rowItems} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} - onChange={criteria => onChange(criteria, tableType)} + loadPage={loadPage} + onChange={onChange} pageOfItems={data} showMorePagesIndicator={showMorePagesIndicator} sorting={{ field, direction: sort.direction }} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ networkType: type, tableType, updates: { limit: newLimit } }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx index 7dd9ca0273c5b..026ab9537d3d7 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx @@ -79,6 +79,26 @@ const TlsTableComponent = React.memo( ? networkModel.NetworkTableType.tls : networkModel.IpDetailsTableType.tls; + const updateLimitPagination = useCallback( + newLimit => + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }), + [type, updateNetworkTable, tableType] + ); + + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [type, updateNetworkTable, tableType] + ); + const onChange = useCallback( (criteria: Criteria) => { if (criteria.sort != null) { @@ -96,8 +116,9 @@ const TlsTableComponent = React.memo( } } }, - [sort, type] + [sort, type, tableType, updateNetworkTable] ); + return ( ( itemsPerRow={rowItems} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} onChange={onChange} pageOfItems={data} sorting={getSortField(sort)} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ - networkType: type, - tableType, - updates: { limit: newLimit }, - }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx index 8da41fca8f384..14fbf4860f7c0 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx @@ -78,6 +78,26 @@ const UsersTableComponent = React.memo( updateNetworkTable, sort, }) => { + const updateLimitPagination = useCallback( + newLimit => + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }), + [type, updateNetworkTable] + ); + + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [type, updateNetworkTable] + ); + const onChange = useCallback( (criteria: Criteria) => { if (criteria.sort != null) { @@ -95,7 +115,7 @@ const UsersTableComponent = React.memo( } } }, - [sort, type] + [sort, type, updateNetworkTable] ); return ( @@ -112,25 +132,13 @@ const UsersTableComponent = React.memo( itemsPerRow={rowItems} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} onChange={onChange} pageOfItems={data} sorting={getSortField(sort)} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ - networkType: type, - tableType, - updates: { limit: newLimit }, - }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx index 077a2d9aebe52..302917c3de93e 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx @@ -6,7 +6,7 @@ import { EuiButton, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { useState } from 'react'; +import React, { useState, useCallback } from 'react'; import { HeaderSection } from '../../../header_section'; import { manageQuery } from '../../../page/manage_query'; @@ -38,9 +38,12 @@ const OverviewHostStatsManage = manageQuery(OverviewHostStats); type OverviewHostProps = OwnProps; export const OverviewHost = React.memo(({ endDate, startDate, setQuery }) => { const [isHover, setIsHover] = useState(false); + const handleMouseEnter = useCallback(() => setIsHover(true), [setIsHover]); + const handleMouseLeave = useCallback(() => setIsHover(false), [setIsHover]); + return ( - setIsHover(true)} onMouseLeave={() => setIsHover(false)}> + (({ endDate, startDate, setQuery }) => { const [isHover, setIsHover] = useState(false); + const handleMouseEnter = useCallback(() => setIsHover(true), [setIsHover]); + const handleMouseLeave = useCallback(() => setIsHover(false), [setIsHover]); + return ( - setIsHover(true)} onMouseLeave={() => setIsHover(false)}> + diff --git a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx index 91ae271d1ccac..0444360d2b965 100644 --- a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx @@ -48,11 +48,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={1} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -75,11 +75,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={1} loading={true} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={[]} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -104,11 +104,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={1} loading={true} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -131,7 +131,7 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={1} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} @@ -168,11 +168,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={2} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -199,11 +199,11 @@ describe('Paginated Table Component', () => { itemsPerRow={[]} limit={2} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -233,7 +233,7 @@ describe('Paginated Table Component', () => { showMorePagesIndicator={true} sorting={{ direction: Direction.asc, field: 'node.host.name' }} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -256,11 +256,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={DEFAULT_MAX_TABLE_QUERY_SIZE} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={DEFAULT_MAX_TABLE_QUERY_SIZE * 3} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -286,11 +286,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={DEFAULT_MAX_TABLE_QUERY_SIZE} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={30} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -312,11 +312,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={DEFAULT_MAX_TABLE_QUERY_SIZE} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={1} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -340,11 +340,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={1} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -370,11 +370,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={2} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -408,11 +408,11 @@ describe('Paginated Table Component', () => { itemsPerRow: rowItems, limit: 1, loading: false, - loadPage: newActivePage => loadPage(newActivePage), + loadPage, pageOfItems: mockData.Hosts.edges, showMorePagesIndicator: true, totalCount: 10, - updateActivePage: activePage => updateActivePage(activePage), + updateActivePage, updateLimitPagination: limit => updateLimitPagination({ limit }), }; @@ -460,11 +460,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={2} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -503,7 +503,7 @@ describe('Paginated Table Component', () => { showMorePagesIndicator={true} sorting={{ direction: Direction.asc, field: 'node.host.name' }} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> diff --git a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx index aedec1a340bfd..7a7a8ee5dfa7d 100644 --- a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx @@ -16,7 +16,7 @@ import { EuiPopover, } from '@elastic/eui'; import { noop } from 'lodash/fp'; -import React, { memo, useState, useEffect } from 'react'; +import React, { memo, useState, useEffect, useCallback } from 'react'; import styled from 'styled-components'; import { Direction } from '../../graphql/types'; @@ -226,13 +226,15 @@ export const PaginatedTable = memo( )); const PaginationWrapper = showMorePagesIndicator ? PaginationEuiFlexItem : EuiFlexItem; + const handleOnMouseEnter = useCallback(() => setShowInspect(true), []); + const handleOnMouseLeave = useCallback(() => setShowInspect(false), []); return ( setShowInspect(true)} - onMouseLeave={() => setShowInspect(false)} + onMouseEnter={handleOnMouseEnter} + onMouseLeave={handleOnMouseLeave} > ( to, }) => { const [isHover, setIsHover] = useState(false); + const handleMouseEnter = useCallback(() => setIsHover(true), [setIsHover]); + const handleMouseLeave = useCallback(() => setIsHover(false), [setIsHover]); const isBarChartDataAvailable = barChart && barChart.length && @@ -218,9 +220,10 @@ export const StatItemsComponent = React.memo( areaChart && areaChart.length && areaChart.every(item => item.value != null && item.value.length > 0); + return ( - setIsHover(true)} onMouseLeave={() => setIsHover(false)}> + diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx index 5697a8c3a0e59..63412302fedfb 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx @@ -146,8 +146,8 @@ describe('UrlStateContainer', () => { hash: '', pathname: examplePath, search: [CONSTANTS.overviewPage, CONSTANTS.timelinePage].includes(page) - ? '?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))' - : `?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, + ? '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))' + : `?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, state: '', }); } diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx index d20a4257a4472..705b2106be315 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx @@ -76,7 +76,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - "?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)),timeline:(linkTo:!(global),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)))", + "?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)),timeline:(linkTo:!(global),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)))", state: '', }); }); @@ -107,7 +107,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - "?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))", + "?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))", state: '', }); }); @@ -140,7 +140,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - '?_g=()&timeline=(id:hello_timeline_id,isOpen:!t)&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '?timeline=(id:hello_timeline_id,isOpen:!t)&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', state: '', }); }); @@ -159,7 +159,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => expect(mockHistory.replace.mock.calls[0][0]).toEqual({ hash: '', pathname: examplePath, - search: '?_g=()', + search: '?', state: '', }); @@ -169,7 +169,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: examplePath, search: - '?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', state: '', }); } @@ -197,7 +197,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => expect( mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0].search ).toEqual( - '?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))' + '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))' ); wrapper.setProps({ hookProps: updatedProps }); @@ -206,7 +206,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => expect( mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0].search ).toEqual( - "?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))" + "?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))" ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts b/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts index d58295de5ce9d..4dd92ac58b0a3 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts @@ -171,7 +171,7 @@ export const getMockPropsObj = ({ { hash: '', pathname: examplePath, - search: '?_g=()', + search: '?', state: '', }, page, @@ -183,7 +183,7 @@ export const getMockPropsObj = ({ { hash: '', pathname: examplePath, - search: '?_g=()', + search: '?', state: '', }, page, @@ -197,7 +197,7 @@ export const getMockPropsObj = ({ { hash: '', pathname: examplePath, - search: `?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, + search: `?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, state: '', }, page, @@ -209,7 +209,7 @@ export const getMockPropsObj = ({ { hash: '', pathname: examplePath, - search: `?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, + search: `?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, state: '', }, page, @@ -224,7 +224,7 @@ export const getMockPropsObj = ({ hash: '', pathname: examplePath, search: - '?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1556736012685,kind:absolute,to:1556822416082)),timeline:(linkTo:!(global),timerange:(from:1556736012685,kind:absolute,to:1556822416082)))', + '?timerange=(global:(linkTo:!(timeline),timerange:(from:1556736012685,kind:absolute,to:1556822416082)),timeline:(linkTo:!(global),timerange:(from:1556736012685,kind:absolute,to:1556822416082)))', state: '', }, page, @@ -237,7 +237,7 @@ export const getMockPropsObj = ({ hash: '', pathname: examplePath, search: - '?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1556736012685,kind:absolute,to:1556822416082)),timeline:(linkTo:!(global),timerange:(from:1556736012685,kind:absolute,to:1556822416082)))', + '?timerange=(global:(linkTo:!(timeline),timerange:(from:1556736012685,kind:absolute,to:1556822416082)),timeline:(linkTo:!(global),timerange:(from:1556736012685,kind:absolute,to:1556822416082)))', state: '', }, page, @@ -251,7 +251,7 @@ export const getMockPropsObj = ({ { hash: '', pathname: examplePath, - search: `?_g=()&query=(query:'host.name:%22siem-es%22',language:kuery)&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, + search: `?query=(query:'host.name:%22siem-es%22',language:kuery)&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, state: '', }, page, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/activity_monitor/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/activity_monitor/index.tsx index d7306b8630bc2..2d48d64acaecf 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/activity_monitor/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/activity_monitor/index.tsx @@ -5,7 +5,7 @@ */ import { EuiBasicTable, EuiPanel, EuiSpacer } from '@elastic/eui'; -import React, { useState } from 'react'; +import React, { useState, useCallback } from 'react'; import { HeaderSection } from '../../../../components/header_section'; import { UtilityBar, @@ -278,6 +278,14 @@ export const ActivityMonitor = React.memo(() => { // const [selectedState, setSelectedState] = useState([]); const [sortState, setSortState] = useState({ field: 'ran', direction: 'desc' }); + const handleChange = useCallback( + ({ page, sort }: { page: PageTypes; sort: SortTypes }) => { + setPageState(page); + setSortState(sort); + }, + [setPageState, setSortState] + ); + return ( <> @@ -308,10 +316,7 @@ export const ActivityMonitor = React.memo(() => { isSelectable itemId="id" items={sampleTableData} - onChange={({ page, sort }: { page: PageTypes; sort: SortTypes }) => { - setPageState(page); - setSortState(sort); - }} + onChange={handleChange} pagination={{ pageIndex: pageState.index, pageSize: pageState.size, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all_rules/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all_rules/columns.tsx index cae0fb3eaf906..9575ee736dea3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all_rules/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all_rules/columns.tsx @@ -21,7 +21,7 @@ import { Action } from './reducer'; import { TableData } from '../types'; import * as i18n from '../translations'; import { PreferenceFormattedDate } from '../../../../components/formatted_date'; -import { RuleSwitch } from '../components/rule_switch'; +import { RuleSwitch, RuleStateChangeCallback } from '../components/rule_switch'; const getActions = (dispatch: React.Dispatch, kbnVersion: string) => [ { @@ -147,16 +147,20 @@ export const getColumns = (dispatch: React.Dispatch, kbnVersion: string) align: 'center', field: 'activate', name: i18n.COLUMN_ACTIVATE, - render: (value: TableData['activate'], item: TableData) => ( - { - await enableRulesAction([id], enabled, dispatch, kbnVersion); - }} - /> - ), + render: (value: TableData['activate'], item: TableData) => { + const handleRuleStateChange: RuleStateChangeCallback = async (enabled, id) => { + await enableRulesAction([id], enabled, dispatch, kbnVersion); + }; + + return ( + + ); + }, sortable: true, width: '85px', }, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_switch/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_switch/index.tsx index 19523752f4f4a..6d9b0a36f8548 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_switch/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_switch/index.tsx @@ -5,7 +5,7 @@ */ import styled from 'styled-components'; -import React from 'react'; +import React, { useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSwitch } from '@elastic/eui'; const StaticSwitch = styled(EuiSwitch)` @@ -17,11 +17,13 @@ const StaticSwitch = styled(EuiSwitch)` StaticSwitch.displayName = 'StaticSwitch'; +export type RuleStateChangeCallback = (isEnabled: boolean, id: string) => void; + export interface RuleSwitchProps { id: string; enabled: boolean; isLoading: boolean; - onRuleStateChange: (isEnabled: boolean, id: string) => void; + onRuleStateChange: RuleStateChangeCallback; } /** @@ -32,26 +34,32 @@ export const RuleSwitchComponent = ({ enabled, isLoading, onRuleStateChange, -}: RuleSwitchProps) => ( - - - {isLoading ? ( - - ) : ( - { - onRuleStateChange(e.target.checked!, id); - }} - /> - )} - - -); +}: RuleSwitchProps) => { + const handleChange = useCallback( + e => { + onRuleStateChange(e.target.checked!, id); + }, + [onRuleStateChange, id] + ); + return ( + + + {isLoading ? ( + + ) : ( + + )} + + + ); +}; export const RuleSwitch = React.memo(RuleSwitchComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx index 97f0a21928a8a..a545be447796d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as React from 'react'; +import React from 'react'; import { Redirect, Route, Switch } from 'react-router-dom'; import styled from 'styled-components'; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx index febf9630e968a..8d45bbbe34d33 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx @@ -63,7 +63,7 @@ describe('body', () => { from={0} isInitializing={false} detailName={'host-1'} - setQuery={() => {}} + setQuery={jest.fn()} to={0} setAbsoluteRangeDatePicker={(jest.fn() as unknown) as SetAbsoluteRangeDatePicker} hostDetailsPagePath={hostDetailsPagePath} diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx index 1252c7031e8a5..a09e21f2d1a35 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx @@ -45,14 +45,14 @@ const HostDetailsTabs = React.memo( to: fromTo.to, }); }, - [setAbsoluteRangeDatePicker, scoreIntervalToDateTime] + [setAbsoluteRangeDatePicker] ); const updateDateRange = useCallback( (min: number, max: number) => { setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, - [setAbsoluteRangeDatePicker, scoreIntervalToDateTime] + [setAbsoluteRangeDatePicker] ); const tabProps = { diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx index d30665c5a2142..d8bcc6fe3c294 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx @@ -5,7 +5,7 @@ */ import { EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; -import React, { useContext, useEffect } from 'react'; +import React, { useContext, useEffect, useCallback } from 'react'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; import { compose } from 'redux'; @@ -61,9 +61,15 @@ const HostDetailsComponent = React.memo( }) => { useEffect(() => { setHostDetailsTablesActivePageToZero(null); - }, [detailName]); + }, [setHostDetailsTablesActivePageToZero, detailName]); const capabilities = useContext(MlCapabilitiesContext); const core = useKibanaCore(); + const narrowDateRange = useCallback( + (min: number, max: number) => { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }, + [setAbsoluteRangeDatePicker] + ); return ( <> @@ -176,9 +182,7 @@ const HostDetailsComponent = React.memo( refetch={refetch} setQuery={setQuery} to={to} - narrowDateRange={(min: number, max: number) => { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }} + narrowDateRange={narrowDateRange} /> )} diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx index 4ff666464404e..0c058f25854c0 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx @@ -5,7 +5,7 @@ */ import { EuiSpacer } from '@elastic/eui'; -import * as React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; import { compose } from 'redux'; @@ -52,6 +52,12 @@ const HostsComponent = React.memo( }) => { const capabilities = React.useContext(MlCapabilitiesContext); const core = useKibanaCore(); + const narrowDateRange = useCallback( + (min: number, max: number) => { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }, + [setAbsoluteRangeDatePicker] + ); return ( <> @@ -93,9 +99,7 @@ const HostsComponent = React.memo( refetch={refetch} setQuery={setQuery} to={to} - narrowDateRange={(min: number, max: number) => { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }} + narrowDateRange={narrowDateRange} /> )} diff --git a/x-pack/legacy/plugins/siem/public/pages/network/index.tsx b/x-pack/legacy/plugins/siem/public/pages/network/index.tsx index 009a89bb3889f..1bc3d9a054bb8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React, { useContext, useMemo } from 'react'; import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; import { MlCapabilitiesContext } from '../../components/ml/permissions/ml_capabilities_provider'; @@ -24,6 +24,14 @@ const ipDetailsPagePath = `${networkPagePath}/ip/:detailName`; export const NetworkContainer = React.memo(() => { const capabilities = useContext(MlCapabilitiesContext); + const capabilitiesFetched = capabilities.capabilitiesFetched; + const userHasMlUserPermissions = useMemo(() => hasMlUserPermissions(capabilities), [ + capabilities, + ]); + const networkRoutePath = useMemo( + () => getNetworkRoutePath(networkPagePath, capabilitiesFetched, userHasMlUserPermissions), + [capabilitiesFetched, userHasMlUserPermissions] + ); return ( @@ -31,11 +39,7 @@ export const NetworkContainer = React.memo(() => { ( (() => { deleteQuery={deleteQuery} isInitializing={isInitializing} capabilitiesFetched={capabilities.capabilitiesFetched} - hasMlUserPermissions={hasMlUserPermissions(capabilities)} + hasMlUserPermissions={userHasMlUserPermissions} /> )} /> diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx index 75ca5a5dfe1a6..97db422b539e8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx @@ -68,13 +68,13 @@ export const IPDetailsComponent = ({ to: fromTo.to, }); }, - [scoreIntervalToDateTime, setAbsoluteRangeDatePicker] + [setAbsoluteRangeDatePicker] ); const core = useKibanaCore(); useEffect(() => { setIpDetailsTablesActivePageToZero(null); - }, [detailName]); + }, [detailName, setIpDetailsTablesActivePageToZero]); return ( <> @@ -134,14 +134,7 @@ export const IPDetailsComponent = ({ setQuery={setQuery} startDate={from} endDate={to} - narrowDateRange={(score, interval) => { - const fromTo = scoreIntervalToDateTime(score, interval); - setAbsoluteRangeDatePicker({ - id: 'global', - from: fromTo.from, - to: fromTo.to, - }); - }} + narrowDateRange={narrowDateRange} /> )} diff --git a/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx b/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx index 681e1f8e1e34d..d1c792ade0985 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx @@ -42,13 +42,13 @@ export const NetworkRoutes = ({ to: fromTo.to, }); }, - [scoreIntervalToDateTime, setAbsoluteRangeDatePicker] + [setAbsoluteRangeDatePicker] ); const updateDateRange = useCallback( (min: number, max: number) => { setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, - [from, to] + [setAbsoluteRangeDatePicker] ); const networkAnomaliesFilterQuery = { diff --git a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx index aa7572e5741bd..116664fef6ddc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx @@ -5,7 +5,7 @@ */ import { EuiSpacer } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; @@ -49,6 +49,12 @@ const NetworkComponent = React.memo( capabilitiesFetched, }) => { const core = useKibanaCore(); + const narrowDateRange = useCallback( + (min: number, max: number) => { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }, + [setAbsoluteRangeDatePicker] + ); return ( <> @@ -101,9 +107,7 @@ const NetworkComponent = React.memo( loading={loading} from={from} to={to} - narrowDateRange={(min: number, max: number) => { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }} + narrowDateRange={narrowDateRange} /> )} diff --git a/x-pack/legacy/plugins/siem/scripts/convert_saved_search_to_rules.js b/x-pack/legacy/plugins/siem/scripts/convert_saved_search_to_rules.js index b282a8bf1e861..9e24e93b0c391 100644 --- a/x-pack/legacy/plugins/siem/scripts/convert_saved_search_to_rules.js +++ b/x-pack/legacy/plugins/siem/scripts/convert_saved_search_to_rules.js @@ -8,6 +8,8 @@ require('../../../../../src/setup_node_env'); const fs = require('fs'); const path = require('path'); +// eslint-disable-next-line import/no-extraneous-dependencies +const uuid = require('uuid'); /* * This script is used to parse a set of saved searches on a file system @@ -36,8 +38,17 @@ const TO = 'now'; const IMMUTABLE = true; const RISK_SCORE = 50; const ENABLED = false; -let allRules = ''; -const allRulesNdJson = 'all_rules.ndjson'; +let allRules = `/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// Auto generated file from scripts/convert_saved_search_rules.js +// Do not hand edit. Run the script against a set of saved searches instead + +`; +const allRulesNdJson = 'index.ts'; // For converting, if you want to use these instead of rely on the defaults then // comment these in and use them for the script. Otherwise this is commented out @@ -62,14 +73,26 @@ const walk = dir => { //clean up the file system characters const cleanupFileName = file => { - return path - .basename(file, path.extname(file)) + const fileWithoutSpecialChars = file + .trim() + .replace(/\./g, '') + .replace(/\//g, '') .replace(/\s+/g, '_') .replace(/,/g, '') + .replace(/\[/g, '') + .replace(/\]/g, '') + .replace(/\(/g, '') + .replace(/\)/g, '') + .replace(/\@/g, '') + .replace(/\:/g, '') .replace(/\+s/g, '') .replace(/-/g, '') .replace(/__/g, '_') .toLowerCase(); + return path.basename( + fileWithoutSpecialChars.trim(), + path.extname(fileWithoutSpecialChars.trim()) + ); }; async function main() { @@ -89,22 +112,20 @@ async function main() { const savedSearchesParsed = savedSearchesJson.reduce((accum, json) => { const jsonFile = fs.readFileSync(json, 'utf8'); const jsonLines = jsonFile.split(/\r{0,1}\n/); - const parsedLines = jsonLines.reduce((accum, line, index) => { + const parsedLines = jsonLines.reduce((accum, line) => { try { const parsedLine = JSON.parse(line); - if (index !== 0) { - parsedLine._file = `${json.substring(0, json.length - '.ndjson'.length)}_${String( - index - )}.ndjson`; - } else { - parsedLine._file = json; + // don't try to parse out any exported count records + if (parsedLine.exportedCount != null) { + return accum; } + parsedLine._file = parsedLine.attributes.title; parsedLine.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.parse( parsedLine.attributes.kibanaSavedObjectMeta.searchSourceJSON ); return [...accum, parsedLine]; } catch (err) { - console.log('error parsing a line in this file:', json); + console.log('error parsing a line in this file:', json, line); return accum; } }, []); @@ -112,51 +133,64 @@ async function main() { }, []); savedSearchesParsed.forEach( - ({ - _file, - attributes: { - description, - title, - kibanaSavedObjectMeta: { - searchSourceJSON: { - query: { query, language }, - filter, + ( + { + _file, + attributes: { + description, + title, + kibanaSavedObjectMeta: { + searchSourceJSON: { + query: { query, language }, + filter, + }, }, }, }, - }) => { + index + ) => { const fileToWrite = cleanupFileName(_file); - if (query != null && query.trim() !== '') { - const outputMessage = { - rule_id: fileToWrite, - risk_score: RISK_SCORE, - description: description || title, - immutable: IMMUTABLE, - interval: INTERVAL, - name: title, - severity: SEVERITY, - type: TYPE, - from: FROM, - to: TO, - query, - language, - filters: filter, - enabled: ENABLED, - // comment these in if you want to use these for input output, otherwise - // with these two commented out, we will use the default saved objects from spaces. - // index: INDEX, - // output_index: OUTPUT_INDEX, - }; - - fs.writeFileSync( - `${outputDir}/${fileToWrite}.json`, - JSON.stringify(outputMessage, null, 2) - ); - allRules += `${JSON.stringify(outputMessage)}\n`; - } + // remove meta value from the filter + const filterWithoutMeta = filter.map(filterValue => { + filterValue.$state; + return filterValue; + }); + const outputMessage = { + rule_id: uuid.v4(), + risk_score: RISK_SCORE, + description: description || title, + immutable: IMMUTABLE, + interval: INTERVAL, + name: title, + severity: SEVERITY, + type: TYPE, + from: FROM, + to: TO, + query, + language, + filters: filterWithoutMeta, + enabled: ENABLED, + version: 1, + // comment these in if you want to use these for input output, otherwise + // with these two commented out, we will use the default saved objects from spaces. + // index: INDEX, + // output_index: OUTPUT_INDEX, + }; + + fs.writeFileSync( + `${outputDir}/${fileToWrite}.json`, + `${JSON.stringify(outputMessage, null, 2)}\n` + ); + allRules += `import rule${index + 1} from './${fileToWrite}.json';\n`; } ); + allRules += '\n'; + allRules += 'export const rawRules = [\n'; + savedSearchesParsed.forEach((_, index) => { + allRules += ` rule${index + 1},\n`; + }); + allRules += '];\n'; fs.writeFileSync(`${outputDir}/${allRulesNdJson}`, allRules); } diff --git a/x-pack/legacy/plugins/siem/server/kibana.index.ts b/x-pack/legacy/plugins/siem/server/kibana.index.ts index e90e6366dd9ec..647894e9e7187 100644 --- a/x-pack/legacy/plugins/siem/server/kibana.index.ts +++ b/x-pack/legacy/plugins/siem/server/kibana.index.ts @@ -21,6 +21,7 @@ import { deleteIndexRoute } from './lib/detection_engine/routes/index/delete_ind import { isAlertExecutor } from './lib/detection_engine/signals/types'; import { readTagsRoute } from './lib/detection_engine/routes/tags/read_tags_route'; import { readPrivilegesRoute } from './lib/detection_engine/routes/privileges/read_privileges_route'; +import { addPrepackedRulesRoute } from './lib/detection_engine/routes/rules/add_prepackaged_rules_route'; const APP_ID = 'siem'; @@ -42,6 +43,7 @@ export const initServerWithKibana = (context: PluginInitializerContext, __legacy updateRulesRoute(__legacy); deleteRulesRoute(__legacy); findRulesRoute(__legacy); + addPrepackedRulesRoute(__legacy); // Detection Engine Signals routes that have the REST endpoints of /api/detection_engine/signals // POST /api/detection_engine/signals/status @@ -57,6 +59,7 @@ export const initServerWithKibana = (context: PluginInitializerContext, __legacy // Detection Engine tags routes that have the REST endpoints of /api/detection_engine/tags readTagsRoute(__legacy); + // Privileges API to get the generic user privileges readPrivilegesRoute(__legacy); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 7acdb3e03de88..3c5182b5178b3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -13,14 +13,48 @@ import { DETECTION_ENGINE_PRIVILEGES_URL, DETECTION_ENGINE_QUERY_SIGNALS_URL, INTERNAL_RULE_ID_KEY, + INTERNAL_IMMUTABLE_KEY, } from '../../../../../common/constants'; import { RuleAlertType } from '../../rules/types'; import { RuleAlertParamsRest } from '../../types'; -// The Omit of filter is because of a Hapi Server Typing issue that I am unclear -// where it comes from. I would hope to remove the "filter" as an omit at some point -// when we upgrade and Hapi Server is ok with the filter. -export const typicalPayload = (): Partial> => ({ +export const fullRuleAlertParamsRest = (): RuleAlertParamsRest => ({ + rule_id: 'rule-1', + description: 'Detecting root and admin users', + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + name: 'Detect Root/Admin Users', + output_index: '.siem-signals', + risk_score: 50, + type: 'query', + from: 'now-6m', + to: 'now', + severity: 'high', + query: 'user.name: root or user.name: admin', + language: 'kuery', + threats: [ + { + framework: 'fake', + tactic: { id: 'fakeId', name: 'fakeName', reference: 'fakeRef' }, + techniques: [{ id: 'techniqueId', name: 'techniqueName', reference: 'techniqueRef' }], + }, + ], + enabled: true, + filters: [], + immutable: false, + references: [], + meta: {}, + tags: [], + version: 1, + false_positives: [], + saved_id: 'some-id', + max_signals: 100, + created_at: '2019-12-13T16:40:33.400Z', + updated_at: '2019-12-13T16:40:33.400Z', + timeline_id: 'timeline-id', +}); + +export const typicalPayload = (): Partial => ({ rule_id: 'rule-1', description: 'Detecting root and admin users', index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], @@ -109,12 +143,24 @@ export const getFindResultWithSingleHit = (): FindHit => ({ data: [getResult()], }); -export const getFindResultWithMultiHits = (data: RuleAlertType[]): FindHit => ({ - page: 1, - perPage: 1, - total: 2, +export const getFindResultWithMultiHits = ({ data, -}); + page = 1, + perPage = 1, + total, +}: { + data: RuleAlertType[]; + page?: number; + perPage?: number; + total?: number; +}) => { + return { + page, + perPage, + total: total != null ? total : data.length, + data, + }; +}; export const getDeleteRequest = (): ServerInjectOptions => ({ method: 'DELETE', @@ -172,22 +218,32 @@ export const createActionResult = (): ActionResult => ({ export const getResult = (): RuleAlertType => ({ id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', name: 'Detect Root/Admin Users', - tags: [`${INTERNAL_RULE_ID_KEY}:rule-1`], + tags: [`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:false`], alertTypeId: 'siem.signals', params: { + createdAt: '2019-12-13T16:40:33.400Z', + updatedAt: '2019-12-13T16:40:33.400Z', description: 'Detecting root and admin users', ruleId: 'rule-1', index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], falsePositives: [], from: 'now-6m', - filter: null, immutable: false, query: 'user.name: root or user.name: admin', language: 'kuery', outputIndex: '.siem-signals', - savedId: null, - meta: null, - filters: null, + savedId: 'some-id', + timelineId: 'some-timeline-id', + meta: { someMeta: 'someField' }, + filters: [ + { + query: { + match_phrase: { + 'host.name': 'some-host', + }, + }, + }, + ], riskScore: 50, maxSignals: 100, size: 1, @@ -213,6 +269,7 @@ export const getResult = (): RuleAlertType => ({ }, ], references: ['http://www.example.com', 'https://ww.example.com'], + version: 1, }, schedule: { interval: '5m' }, enabled: true, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json index 501522105bdbc..afe9bac9d87fe 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json @@ -33,6 +33,9 @@ "saved_id": { "type": "keyword" }, + "timeline_id": { + "type": "keyword" + }, "max_signals": { "type": "keyword" }, @@ -96,11 +99,20 @@ "filters": { "type": "object" }, + "created_at": { + "type": "date" + }, + "updated_at": { + "type": "date" + }, "created_by": { "type": "keyword" }, "updated_by": { "type": "keyword" + }, + "version": { + "type": "keyword" } } }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts new file mode 100644 index 0000000000000..922b70e87467e --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Hapi from 'hapi'; +import { isFunction } from 'lodash/fp'; +import Boom from 'boom'; + +import { DETECTION_ENGINE_PREPACKAGED_URL } from '../../../../../common/constants'; +import { ServerFacade, RequestFacade } from '../../../../types'; +import { getIndexExists } from '../../index/get_index_exists'; +import { callWithRequestFactory, getIndex, transformError } from '../utils'; +import { getPrepackagedRules } from '../../rules/get_prepackaged_rules'; +import { installPrepackagedRules } from '../../rules/install_prepacked_rules'; +import { updatePrepackagedRules } from '../../rules/update_prepacked_rules'; +import { getRulesToInstall } from '../../rules/get_rules_to_install'; +import { getRulesToUpdate } from '../../rules/get_rules_to_update'; +import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules'; + +export const createAddPrepackedRulesRoute = (server: ServerFacade): Hapi.ServerRoute => { + return { + method: 'PUT', + path: DETECTION_ENGINE_PREPACKAGED_URL, + options: { + tags: ['access:siem'], + validate: { + options: { + abortEarly: false, + }, + }, + }, + async handler(request: RequestFacade, headers) { + const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; + const actionsClient = isFunction(request.getActionsClient) + ? request.getActionsClient() + : null; + + if (!alertsClient || !actionsClient) { + return headers.response().code(404); + } + + try { + const callWithRequest = callWithRequestFactory(request, server); + const rulesFromFileSystem = getPrepackagedRules(); + + const prepackedRules = await getExistingPrepackagedRules({ alertsClient }); + const rulesToInstall = getRulesToInstall(rulesFromFileSystem, prepackedRules); + const rulesToUpdate = getRulesToUpdate(rulesFromFileSystem, prepackedRules); + + const spaceIndex = getIndex(request, server); + if (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0) { + const spaceIndexExists = await getIndexExists(callWithRequest, spaceIndex); + if (!spaceIndexExists) { + throw new Boom( + `Pre-packaged rules cannot be installed until the space index is created: ${spaceIndex}` + ); + } + } + await installPrepackagedRules(alertsClient, actionsClient, rulesToInstall, spaceIndex); + await updatePrepackagedRules(alertsClient, actionsClient, rulesToUpdate, spaceIndex); + return { + rules_installed: rulesToInstall.length, + rules_updated: rulesToUpdate.length, + }; + } catch (err) { + return transformError(err); + } + }, + }; +}; + +export const addPrepackedRulesRoute = (server: ServerFacade): void => { + server.route(createAddPrepackedRulesRoute(server)); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts index 0dc213e9e2173..476d5b8a49ba2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -33,6 +33,7 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = }, async handler(request: RulesRequest, headers) { const { + created_at: createdAt, description, enabled, false_positives: falsePositives, @@ -42,6 +43,7 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = language, output_index: outputIndex, saved_id: savedId, + timeline_id: timelineId, meta, filters, rule_id: ruleId, @@ -55,6 +57,7 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = threats, to, type, + updated_at: updatedAt, references, } = request.payload; const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; @@ -87,6 +90,7 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = const createdRule = await createRules({ alertsClient, actionsClient, + createdAt, description, enabled, falsePositives, @@ -96,6 +100,7 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = language, outputIndex: finalIndex, savedId, + timelineId, meta, filters, ruleId: ruleId != null ? ruleId : uuid.v4(), @@ -109,7 +114,9 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = to, type, threats, + updatedAt, references, + version: 1, }); return transformOrError(createdRule); } catch (err) { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts index 2e7b48afbb5d9..ec3d9514fa5db 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -37,6 +37,7 @@ export const createUpdateRulesRoute: Hapi.ServerRoute = { language, output_index: outputIndex, saved_id: savedId, + timeline_id: timelineId, meta, filters, rule_id: ruleId, @@ -52,6 +53,7 @@ export const createUpdateRulesRoute: Hapi.ServerRoute = { type, threats, references, + version, } = request.payload; const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; @@ -74,6 +76,7 @@ export const createUpdateRulesRoute: Hapi.ServerRoute = { language, outputIndex, savedId, + timelineId, meta, filters, id, @@ -89,6 +92,7 @@ export const createUpdateRulesRoute: Hapi.ServerRoute = { type, threats, references, + version, }); if (rule != null) { return transformOrError(rule); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts index a2312ce25e72a..e22c873741392 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts @@ -15,14 +15,17 @@ import { } from './utils'; import { getResult } from '../__mocks__/request_responses'; import { INTERNAL_IDENTIFIER } from '../../../../../common/constants'; +import { OutputRuleAlertRest } from '../../types'; describe('utils', () => { describe('transformAlertToRule', () => { test('should work with a full data set', () => { const fullRule = getResult(); const rule = transformAlertToRule(fullRule); - expect(rule).toEqual({ + const expected: OutputRuleAlertRest = { created_by: 'elastic', + created_at: '2019-12-13T16:40:33.400Z', + updated_at: '2019-12-13T16:40:33.400Z', description: 'Detecting root and admin users', enabled: true, false_positives: [], @@ -59,16 +62,34 @@ describe('utils', () => { ], }, ], + filters: [ + { + query: { + match_phrase: { + 'host.name': 'some-host', + }, + }, + }, + ], + meta: { + someMeta: 'someField', + }, + saved_id: 'some-id', + timeline_id: 'some-timeline-id', to: 'now', type: 'query', - }); + version: 1, + }; + expect(rule).toEqual(expected); }); test('should work with a partial data set missing data', () => { const fullRule = getResult(); const { from, language, ...omitData } = transformAlertToRule(fullRule); - expect(omitData).toEqual({ + const expected: Partial = { created_by: 'elastic', + created_at: '2019-12-13T16:40:33.400Z', + updated_at: '2019-12-13T16:40:33.400Z', description: 'Detecting root and admin users', enabled: true, false_positives: [], @@ -103,17 +124,35 @@ describe('utils', () => { ], }, ], + filters: [ + { + query: { + match_phrase: { + 'host.name': 'some-host', + }, + }, + }, + ], + meta: { + someMeta: 'someField', + }, + saved_id: 'some-id', + timeline_id: 'some-timeline-id', to: 'now', type: 'query', - }); + version: 1, + }; + expect(omitData).toEqual(expected); }); test('should omit query if query is null', () => { const fullRule = getResult(); fullRule.params.query = null; const rule = transformAlertToRule(fullRule); - expect(rule).toEqual({ + const expected: Partial = { created_by: 'elastic', + created_at: '2019-12-13T16:40:33.400Z', + updated_at: '2019-12-13T16:40:33.400Z', description: 'Detecting root and admin users', enabled: true, false_positives: [], @@ -149,17 +188,35 @@ describe('utils', () => { ], }, ], + filters: [ + { + query: { + match_phrase: { + 'host.name': 'some-host', + }, + }, + }, + ], + meta: { + someMeta: 'someField', + }, + saved_id: 'some-id', + timeline_id: 'some-timeline-id', to: 'now', type: 'query', - }); + version: 1, + }; + expect(rule).toEqual(expected); }); test('should omit query if query is undefined', () => { const fullRule = getResult(); fullRule.params.query = undefined; const rule = transformAlertToRule(fullRule); - expect(rule).toEqual({ + const expected: Partial = { created_by: 'elastic', + created_at: '2019-12-13T16:40:33.400Z', + updated_at: '2019-12-13T16:40:33.400Z', description: 'Detecting root and admin users', enabled: true, false_positives: [], @@ -195,9 +252,25 @@ describe('utils', () => { ], }, ], + filters: [ + { + query: { + match_phrase: { + 'host.name': 'some-host', + }, + }, + }, + ], + meta: { + someMeta: 'someField', + }, + saved_id: 'some-id', + timeline_id: 'some-timeline-id', to: 'now', type: 'query', - }); + version: 1, + }; + expect(rule).toEqual(expected); }); test('should omit a mix of undefined, null, and missing fields', () => { @@ -205,8 +278,10 @@ describe('utils', () => { fullRule.params.query = undefined; fullRule.params.language = null; const { from, enabled, ...omitData } = transformAlertToRule(fullRule); - expect(omitData).toEqual({ + const expected: Partial = { created_by: 'elastic', + created_at: '2019-12-13T16:40:33.400Z', + updated_at: '2019-12-13T16:40:33.400Z', description: 'Detecting root and admin users', false_positives: [], id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', @@ -239,17 +314,35 @@ describe('utils', () => { ], }, ], + filters: [ + { + query: { + match_phrase: { + 'host.name': 'some-host', + }, + }, + }, + ], + meta: { + someMeta: 'someField', + }, + saved_id: 'some-id', + timeline_id: 'some-timeline-id', to: 'now', type: 'query', - }); + version: 1, + }; + expect(omitData).toEqual(expected); }); test('should return enabled is equal to false', () => { const fullRule = getResult(); fullRule.enabled = false; const ruleWithEnabledFalse = transformAlertToRule(fullRule); - expect(ruleWithEnabledFalse).toEqual({ + const expected: OutputRuleAlertRest = { created_by: 'elastic', + created_at: '2019-12-13T16:40:33.400Z', + updated_at: '2019-12-13T16:40:33.400Z', description: 'Detecting root and admin users', enabled: false, from: 'now-6m', @@ -286,17 +379,35 @@ describe('utils', () => { ], }, ], + filters: [ + { + query: { + match_phrase: { + 'host.name': 'some-host', + }, + }, + }, + ], + meta: { + someMeta: 'someField', + }, + saved_id: 'some-id', + timeline_id: 'some-timeline-id', to: 'now', type: 'query', - }); + version: 1, + }; + expect(ruleWithEnabledFalse).toEqual(expected); }); test('should return immutable is equal to false', () => { const fullRule = getResult(); fullRule.params.immutable = false; const ruleWithEnabledFalse = transformAlertToRule(fullRule); - expect(ruleWithEnabledFalse).toEqual({ + const expected: OutputRuleAlertRest = { created_by: 'elastic', + created_at: '2019-12-13T16:40:33.400Z', + updated_at: '2019-12-13T16:40:33.400Z', description: 'Detecting root and admin users', enabled: true, from: 'now-6m', @@ -333,16 +444,34 @@ describe('utils', () => { ], }, ], + filters: [ + { + query: { + match_phrase: { + 'host.name': 'some-host', + }, + }, + }, + ], + meta: { + someMeta: 'someField', + }, + saved_id: 'some-id', + timeline_id: 'some-timeline-id', to: 'now', type: 'query', - }); + version: 1, + }; + expect(ruleWithEnabledFalse).toEqual(expected); }); test('should work with tags but filter out any internal tags', () => { const fullRule = getResult(); fullRule.tags = ['tag 1', 'tag 2', `${INTERNAL_IDENTIFIER}_some_other_value`]; const rule = transformAlertToRule(fullRule); - expect(rule).toEqual({ + const expected: OutputRuleAlertRest = { + created_at: '2019-12-13T16:40:33.400Z', + updated_at: '2019-12-13T16:40:33.400Z', created_by: 'elastic', description: 'Detecting root and admin users', enabled: true, @@ -380,9 +509,25 @@ describe('utils', () => { ], }, ], + filters: [ + { + query: { + match_phrase: { + 'host.name': 'some-host', + }, + }, + }, + ], + meta: { + someMeta: 'someField', + }, + saved_id: 'some-id', + timeline_id: 'some-timeline-id', to: 'now', type: 'query', - }); + version: 1, + }; + expect(rule).toEqual(expected); }); }); @@ -438,50 +583,66 @@ describe('utils', () => { const output = transformFindAlertsOrError({ data: [getResult()], }); - expect(output).toEqual({ - data: [ + const expected: OutputRuleAlertRest = { + created_by: 'elastic', + created_at: '2019-12-13T16:40:33.400Z', + updated_at: '2019-12-13T16:40:33.400Z', + description: 'Detecting root and admin users', + enabled: true, + false_positives: [], + from: 'now-6m', + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + immutable: false, + output_index: '.siem-signals', + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + risk_score: 50, + rule_id: 'rule-1', + language: 'kuery', + max_signals: 100, + name: 'Detect Root/Admin Users', + query: 'user.name: root or user.name: admin', + references: ['http://www.example.com', 'https://ww.example.com'], + severity: 'high', + updated_by: 'elastic', + tags: [], + to: 'now', + type: 'query', + threats: [ { - created_by: 'elastic', - description: 'Detecting root and admin users', - enabled: true, - false_positives: [], - from: 'now-6m', - id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', - immutable: false, - output_index: '.siem-signals', - index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - interval: '5m', - risk_score: 50, - rule_id: 'rule-1', - language: 'kuery', - max_signals: 100, - name: 'Detect Root/Admin Users', - query: 'user.name: root or user.name: admin', - references: ['http://www.example.com', 'https://ww.example.com'], - severity: 'high', - updated_by: 'elastic', - tags: [], - to: 'now', - type: 'query', - threats: [ + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0040', + name: 'impact', + reference: 'https://attack.mitre.org/tactics/TA0040/', + }, + techniques: [ { - framework: 'MITRE ATT&CK', - tactic: { - id: 'TA0040', - name: 'impact', - reference: 'https://attack.mitre.org/tactics/TA0040/', - }, - techniques: [ - { - id: 'T1499', - name: 'endpoint denial of service', - reference: 'https://attack.mitre.org/techniques/T1499/', - }, - ], + id: 'T1499', + name: 'endpoint denial of service', + reference: 'https://attack.mitre.org/techniques/T1499/', }, ], }, ], + filters: [ + { + query: { + match_phrase: { + 'host.name': 'some-host', + }, + }, + }, + ], + meta: { + someMeta: 'someField', + }, + saved_id: 'some-id', + timeline_id: 'some-timeline-id', + version: 1, + }; + expect(output).toEqual({ + data: [expected], }); }); @@ -494,8 +655,10 @@ describe('utils', () => { describe('transformOrError', () => { test('outputs 200 if the data is of type siem alert', () => { const output = transformOrError(getResult()); - expect(output).toEqual({ + const expected: OutputRuleAlertRest = { created_by: 'elastic', + created_at: '2019-12-13T16:40:33.400Z', + updated_at: '2019-12-13T16:40:33.400Z', description: 'Detecting root and admin users', enabled: true, false_positives: [], @@ -534,7 +697,23 @@ describe('utils', () => { ], }, ], - }); + filters: [ + { + query: { + match_phrase: { + 'host.name': 'some-host', + }, + }, + }, + ], + meta: { + someMeta: 'someField', + }, + saved_id: 'some-id', + timeline_id: 'some-timeline-id', + version: 1, + }; + expect(output).toEqual(expected); }); test('returns 500 if the data is not of type siem alert', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts index 7ec5c426f41ff..dad22c74398d2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts @@ -34,6 +34,8 @@ export const transformTags = (tags: string[]): string[] => { // those on the export export const transformAlertToRule = (alert: RuleAlertType): Partial => { return pickBy((value: unknown) => value != null, { + created_at: alert.params.createdAt, + updated_at: alert.params.updatedAt, created_by: alert.createdBy, description: alert.params.description, enabled: alert.enabled, @@ -53,6 +55,7 @@ export const transformAlertToRule = (alert: RuleAlertType): Partial { + test('empty objects do not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({}).error + ).toBeTruthy(); + }); + + test('made up values do not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + madeUp: 'hi', + }).error + ).toBeTruthy(); + }); + + test('[rule_id] does not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description] does not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description, from] does not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description, from, to] does not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description, from, to, name] does not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description, from, to, name, severity] does not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + severity: 'severity', + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description, from, to, name, severity, type] does not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + severity: 'severity', + type: 'query', + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description, from, to, name, severity, type, interval] does not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description, from, to, name, severity, type, interval, index] does not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + severity: 'severity', + type: 'query', + interval: '5m', + index: ['index-1'], + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description, from, to, name, severity, type, query, index, interval, version] does validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + severity: 'severity', + type: 'query', + query: 'some query', + index: ['index-1'], + interval: '5m', + version: 1, + }).error + ).toBeFalsy(); + }); + + test('[rule_id, description, from, to, index, name, severity, interval, type, query, language] does not validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some query', + language: 'kuery', + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description, from, to, index, name, severity, interval, type, query, language, risk_score, version] does validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some query', + language: 'kuery', + version: 1, + }).error + ).toBeFalsy(); + }); + + test('[rule_id, description, from, to, index, name, severity, interval, type, query, language, risk_score, output_index] does not validate because output_index is not allowed', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some query', + language: 'kuery', + version: 1, + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, version] does validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + risk_score: 50, + version: 1, + }).error + ).toBeFalsy(); + }); + + test('You can send in an empty array to threats', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [], + version: 1, + }).error + ).toBeFalsy(); + }); + test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, version, threats] does validate', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + threats: [ + { + framework: 'someFramework', + tactic: { + id: 'fakeId', + name: 'fakeName', + reference: 'fakeRef', + }, + techniques: [ + { + id: 'techniqueId', + name: 'techniqueName', + reference: 'techniqueRef', + }, + ], + }, + ], + version: 1, + }).error + ).toBeFalsy(); + }); + + test('allows references to be sent as valid', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + version: 1, + }).error + ).toBeFalsy(); + }); + + test('defaults references to an array', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some-query', + language: 'kuery', + version: 1, + }).value.references + ).toEqual([]); + }); + + test('defaults immutable to true', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some-query', + language: 'kuery', + version: 1, + }).value.immutable + ).toEqual(true); + }); + + test('defaults enabled to false', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some-query', + language: 'kuery', + version: 1, + }).value.enabled + ).toEqual(false); + }); + + test('rule_id is required', () => { + expect( + addPrepackagedRulesSchema.validate>({ + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some-query', + language: 'kuery', + version: 1, + }).error + ).toBeTruthy(); + }); + + test('references cannot be numbers', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial> & { references: number[] } + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some-query', + language: 'kuery', + references: [5], + version: 1, + }).error + ).toBeTruthy(); + }); + + test('indexes cannot be numbers', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial> & { index: number[] } + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: [5], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some-query', + language: 'kuery', + version: 1, + }).error + ).toBeTruthy(); + }); + + test('defaults interval to 5 min', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + type: 'query', + version: 1, + }).value.interval + ).toEqual('5m'); + }); + + test('defaults max signals to 100', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + version: 1, + }).value.max_signals + ).toEqual(100); + }); + + test('saved_id is required when type is saved_query and will not validate without out', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'saved_query', + version: 1, + }).error + ).toBeTruthy(); + }); + + test('saved_id is required when type is saved_query and validates with it', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'saved_query', + saved_id: 'some id', + version: 1, + }).error + ).toBeFalsy(); + }); + + test('saved_query type can have filters with it', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'saved_query', + saved_id: 'some id', + filters: [], + version: 1, + }).error + ).toBeFalsy(); + }); + + test('filters cannot be a string', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial & { filters: string }> + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'saved_query', + saved_id: 'some id', + filters: 'some string', + version: 1, + }).error + ).toBeTruthy(); + }); + + test('language validates with kuery', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + version: 1, + }).error + ).toBeFalsy(); + }); + + test('language validates with lucene', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'lucene', + version: 1, + }).error + ).toBeFalsy(); + }); + + test('language does not validate with something made up', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'something-made-up', + version: 1, + }).error + ).toBeTruthy(); + }); + + test('max_signals cannot be negative', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: -1, + version: 1, + }).error + ).toBeTruthy(); + }); + + test('max_signals cannot be zero', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 0, + version: 1, + }).error + ).toBeTruthy(); + }); + + test('max_signals can be 1', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + version: 1, + }).error + ).toBeFalsy(); + }); + + test('You can optionally send in an array of tags', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + tags: ['tag_1', 'tag_2'], + version: 1, + }).error + ).toBeFalsy(); + }); + + test('You cannot send in an array of tags that are numbers', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial> & { tags: number[] } + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + tags: [0, 1, 2], + version: 1, + }).error + ).toBeTruthy(); + }); + + test('You cannot send in an array of threats that are missing "framework"', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial> & { + threats: Array>>; + } + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [ + { + tactic: { + id: 'fakeId', + name: 'fakeName', + reference: 'fakeRef', + }, + techniques: [ + { + id: 'techniqueId', + name: 'techniqueName', + reference: 'techniqueRef', + }, + ], + }, + ], + version: 1, + }).error + ).toBeTruthy(); + }); + test('You cannot send in an array of threats that are missing "tactic"', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial> & { + threats: Array>>; + } + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [ + { + framework: 'fake', + techniques: [ + { + id: 'techniqueId', + name: 'techniqueName', + reference: 'techniqueRef', + }, + ], + }, + ], + version: 1, + }).error + ).toBeTruthy(); + }); + test('You cannot send in an array of threats that are missing "techniques"', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial> & { + threats: Array>>; + } + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [ + { + framework: 'fake', + tactic: { + id: 'fakeId', + name: 'fakeName', + reference: 'fakeRef', + }, + }, + ], + version: 1, + }).error + ).toBeTruthy(); + }); + + test('You can optionally send in an array of false positives', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + false_positives: ['false_1', 'false_2'], + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + version: 1, + }).error + ).toBeFalsy(); + }); + + test('You cannot send in an array of false positives that are numbers', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial> & { false_positives: number[] } + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + false_positives: [5, 4], + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + version: 1, + }).error + ).toBeTruthy(); + }); + + test('You can optionally set the immutable to be true', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: true, + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + version: 1, + }).error + ).toBeFalsy(); + }); + + test('You cannot set the immutable to be a number', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial> & { immutable: number } + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: 5, + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + version: 1, + }).error + ).toBeTruthy(); + }); + + test('You cannot set the risk_score to 101', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 101, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: true, + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + version: 1, + }).error + ).toBeTruthy(); + }); + + test('You cannot set the risk_score to -1', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: -1, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: true, + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + version: 1, + }).error + ).toBeTruthy(); + }); + + test('You can set the risk_score to 0', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 0, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: true, + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + version: 1, + }).error + ).toBeFalsy(); + }); + + test('You can set the risk_score to 100', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 100, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: true, + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + version: 1, + }).error + ).toBeFalsy(); + }); + + test('You can set meta to any object you want', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: true, + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + meta: { + somethingMadeUp: { somethingElse: true }, + }, + version: 1, + }).error + ).toBeFalsy(); + }); + + test('You cannot create meta as a string', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial & { meta: string }> + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: true, + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + meta: 'should not work', + version: 1, + }).error + ).toBeTruthy(); + }); + + test('You can omit the query string when filters are present', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial & { meta: string }> + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: true, + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + language: 'kuery', + filters: [], + max_signals: 1, + version: 1, + }).error + ).toBeFalsy(); + }); + + test('validates with timeline_id', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + version: 1, + timeline_id: 'timeline-id', + }).error + ).toBeFalsy(); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts new file mode 100644 index 0000000000000..c993b05cb5f29 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import Joi from 'joi'; + +/* eslint-disable @typescript-eslint/camelcase */ +import { + enabled, + description, + false_positives, + filters, + from, + immutable, + index, + rule_id, + interval, + query, + language, + saved_id, + timeline_id, + meta, + risk_score, + max_signals, + name, + severity, + tags, + to, + type, + threats, + references, + version, +} from './schemas'; +/* eslint-enable @typescript-eslint/camelcase */ + +import { DEFAULT_MAX_SIGNALS } from '../../../../../common/constants'; + +/** + * Big differences between this schema and the createRulesSchema + * - rule_id is required here + * - output_index is not allowed (and instead the space index must be used) + * - immutable defaults to true instead of to false + * - enabled defaults to false instead of true + * - version is a required field that must exist + */ +export const addPrepackagedRulesSchema = Joi.object({ + description: description.required(), + enabled: enabled.default(false), + false_positives: false_positives.default([]), + filters, + from: from.required(), + rule_id: rule_id.required(), + immutable: immutable.default(true), + index, + interval: interval.default('5m'), + query: query.allow('').default(''), + language: language.default('kuery'), + saved_id: saved_id.when('type', { + is: 'saved_query', + then: Joi.required(), + otherwise: Joi.forbidden(), + }), + timeline_id, + meta, + risk_score: risk_score.required(), + max_signals: max_signals.default(DEFAULT_MAX_SIGNALS), + name: name.required(), + severity: severity.required(), + tags: tags.default([]), + to: to.required(), + type: type.required(), + threats: threats.default([]), + references: references.default([]), + version: version.required(), +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts index 4efea69db1f41..8dc00b66e97a3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts @@ -1044,4 +1044,26 @@ describe('create rules schema', () => { }).error ).toBeFalsy(); }); + + test('timeline_id validates', () => { + expect( + createRulesSchema.validate>({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + timeline_id: 'some_id', + }).error + ).toBeFalsy(); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts index ccda7256d2eeb..614451312d04d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts @@ -21,6 +21,7 @@ import { language, output_index, saved_id, + timeline_id, meta, risk_score, max_signals, @@ -31,6 +32,7 @@ import { type, threats, references, + version, } from './schemas'; /* eslint-enable @typescript-eslint/camelcase */ @@ -54,6 +56,7 @@ export const createRulesSchema = Joi.object({ then: Joi.required(), otherwise: Joi.forbidden(), }), + timeline_id, meta, risk_score: risk_score.required(), max_signals: max_signals.default(DEFAULT_MAX_SIGNALS), @@ -64,4 +67,5 @@ export const createRulesSchema = Joi.object({ type: type.required(), threats: threats.default([]), references: references.default([]), + version: version.default(1), }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts index 5ab8ea3b8af3e..68d3166c74d6d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts @@ -23,6 +23,7 @@ export const query = Joi.string(); export const language = Joi.string().valid('kuery', 'lucene'); export const output_index = Joi.string(); export const saved_id = Joi.string(); +export const timeline_id = Joi.string(); export const meta = Joi.object(); export const max_signals = Joi.number().greater(0); export const name = Joi.string(); @@ -77,3 +78,5 @@ export const threats = Joi.array().items( techniques: threat_techniques.required(), }) ); + +export const version = Joi.number().min(1); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts index 606a30309b2ab..1f00e0a13866a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts @@ -866,4 +866,22 @@ describe('update rules schema', () => { }).error ).toBeTruthy(); }); + + test('timeline_id validates', () => { + expect( + updateRulesSchema.validate>({ + id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'saved_query', + saved_id: 'some id', + timeline_id: 'some-id', + }).error + ).toBeFalsy(); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts index 244d8d1f5cc77..afd8a5fce4833 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts @@ -21,6 +21,7 @@ import { language, output_index, saved_id, + timeline_id, meta, risk_score, max_signals, @@ -32,6 +33,7 @@ import { threats, references, id, + version, } from './schemas'; /* eslint-enable @typescript-eslint/camelcase */ @@ -50,6 +52,7 @@ export const updateRulesSchema = Joi.object({ language, output_index, saved_id, + timeline_id, meta, risk_score, max_signals, @@ -60,4 +63,5 @@ export const updateRulesSchema = Joi.object({ type, threats, references, + version, }).xor('id', 'rule_id'); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts index 8ca5c24d88100..437c0a4362430 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts @@ -44,5 +44,17 @@ describe('utils', () => { const transformed = transformError(error); expect(Boom.isBoom(transformed)).toBe(false); }); + + test('it detects a TypeError and returns a Boom', () => { + const error: TypeError = new TypeError('I have a type error'); + const transformed = transformError(error); + expect(Boom.isBoom(transformed)).toBe(true); + }); + + test('it detects a TypeError and returns a Boom status of 400', () => { + const error: TypeError = new TypeError('I have a type error'); + const transformed = transformError(error) as Boom; + expect(transformed.output.statusCode).toBe(400); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts index aed0ced5cdeb5..62281c7ebaacb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts @@ -14,6 +14,10 @@ export const transformError = (err: Error & { statusCode?: number }) => { } else { if (err.statusCode != null) { return new Boom(err.message, { statusCode: err.statusCode }); + } else if (err instanceof TypeError) { + // allows us to throw type errors instead of booms in some conditions + // where we don't want to mingle Boom with the rest of the code + return new Boom(err.message, { statusCode: 400 }); } else { // natively return the err and allow the regular framework // to deal with the error when it is a non Boom diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_rule_id_to_tags.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_rule_id_to_tags.test.ts deleted file mode 100644 index 5a92c8ef42ed7..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_rule_id_to_tags.test.ts +++ /dev/null @@ -1,35 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { addRuleIdToTags } from './add_rule_id_to_tags'; -import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; - -describe('add_rule_id_to_tags', () => { - test('it should add a rule id as an internal structure to a single tag', () => { - const tags = addRuleIdToTags(['tag 1'], 'rule-1'); - expect(tags).toEqual(['tag 1', `${INTERNAL_RULE_ID_KEY}:rule-1`]); - }); - - test('it should add a rule id as an internal structure to two tags', () => { - const tags = addRuleIdToTags(['tag 1', 'tag 2'], 'rule-1'); - expect(tags).toEqual(['tag 1', 'tag 2', `${INTERNAL_RULE_ID_KEY}:rule-1`]); - }); - - test('it should add a rule id as an internal structure with empty tags', () => { - const tags = addRuleIdToTags([], 'rule-1'); - expect(tags).toEqual([`${INTERNAL_RULE_ID_KEY}:rule-1`]); - }); - - test('it should add not add an internal structure if rule id is undefined', () => { - const tags = addRuleIdToTags(['tag 1'], undefined); - expect(tags).toEqual(['tag 1']); - }); - - test('it should add not add an internal structure if rule id is null', () => { - const tags = addRuleIdToTags(['tag 1'], null); - expect(tags).toEqual(['tag 1']); - }); -}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_rule_id_to_tags.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_rule_id_to_tags.ts deleted file mode 100644 index 1cf97881d514b..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_rule_id_to_tags.ts +++ /dev/null @@ -1,15 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; - -export const addRuleIdToTags = (tags: string[], ruleId: string | null | undefined): string[] => { - if (ruleId == null) { - return tags; - } else { - return [...tags, `${INTERNAL_RULE_ID_KEY}:${ruleId}`]; - } -}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_tags.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_tags.test.ts new file mode 100644 index 0000000000000..8bfd9bef63733 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_tags.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { addTags } from './add_tags'; +import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; + +describe('add_tags', () => { + test('if given a null everything this returns a new array for tags', () => { + const tags = addTags(null, null, null); + expect(tags).toEqual([]); + }); + + test('if given a undefined everything this returns a new array for tags', () => { + const tags = addTags(undefined, undefined, undefined); + expect(tags).toEqual([]); + }); + + test('it should add a rule id as an internal structure to a single tag', () => { + const tags = addTags(['tag 1'], 'rule-1', null); + expect(tags).toEqual(['tag 1', `${INTERNAL_RULE_ID_KEY}:rule-1`]); + }); + + test('it should add a rule id as an internal structure to a single tag if the input tags is null', () => { + const tags = addTags(null, 'rule-1', null); + expect(tags).toEqual([`${INTERNAL_RULE_ID_KEY}:rule-1`]); + }); + + test('it should add a rule id as an internal structure to two tags', () => { + const tags = addTags(['tag 1', 'tag 2'], 'rule-1', null); + expect(tags).toEqual(['tag 1', 'tag 2', `${INTERNAL_RULE_ID_KEY}:rule-1`]); + }); + + test('it should add a rule id as an internal structure with empty tags', () => { + const tags = addTags([], 'rule-1', null); + expect(tags).toEqual([`${INTERNAL_RULE_ID_KEY}:rule-1`]); + }); + + test('it should add a immutable true as an internal structure with empty tags', () => { + const tags = addTags([], null, true); + expect(tags).toEqual([`${INTERNAL_IMMUTABLE_KEY}:true`]); + }); + + test('it should add a immutable false as an internal structure with empty tags', () => { + const tags = addTags([], null, false); + expect(tags).toEqual([`${INTERNAL_IMMUTABLE_KEY}:false`]); + }); + + test('it should add a rule id as an internal structure with immutable true', () => { + const tags = addTags([], 'rule-1', true); + expect(tags).toEqual([`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:true`]); + }); + + test('it should add a rule id as an internal structure with immutable false', () => { + const tags = addTags([], 'rule-1', false); + expect(tags).toEqual([`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:false`]); + }); + + test('it should add not add an internal structure if only a tag is given', () => { + const tags = addTags(['tag 1'], undefined, null); + expect(tags).toEqual(['tag 1']); + }); + + test('it should add not add an internal structure if everything is null', () => { + const tags = addTags(['tag 1'], null, null); + expect(tags).toEqual(['tag 1']); + }); + + test('it should add not add an internal structure if everything is undefined', () => { + const tags = addTags(['tag 1'], undefined, undefined); + expect(tags).toEqual(['tag 1']); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_tags.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_tags.ts new file mode 100644 index 0000000000000..f33f6ada83190 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/add_tags.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; + +export const addTags = ( + tags: string[] | null | undefined, + ruleId: string | null | undefined, + immutable: boolean | null | undefined +): string[] => { + const defaultedTags = tags != null ? tags : []; + if (ruleId != null && immutable != null) { + return [ + ...defaultedTags, + `${INTERNAL_RULE_ID_KEY}:${ruleId}`, + `${INTERNAL_IMMUTABLE_KEY}:${immutable}`, + ]; + } else if (ruleId != null && immutable == null) { + return [...defaultedTags, `${INTERNAL_RULE_ID_KEY}:${ruleId}`]; + } else if (ruleId == null && immutable != null) { + return [...defaultedTags, `${INTERNAL_IMMUTABLE_KEY}:${immutable}`]; + } else { + return defaultedTags; + } +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts index 8299821a942b3..84a0566bfa092 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts @@ -6,7 +6,7 @@ import { SIGNALS_ID } from '../../../../common/constants'; import { RuleParams } from './types'; -import { addRuleIdToTags } from './add_rule_id_to_tags'; +import { addTags } from './add_tags'; export const createRules = async ({ alertsClient, @@ -18,6 +18,7 @@ export const createRules = async ({ query, language, savedId, + timelineId, meta, filters, ruleId, @@ -34,13 +35,15 @@ export const createRules = async ({ to, type, references, + version, }: RuleParams) => { return alertsClient.create({ data: { name, - tags: addRuleIdToTags(tags, ruleId), + tags: addTags(tags, ruleId, immutable), alertTypeId: SIGNALS_ID, params: { + createdAt: new Date().toISOString(), description, ruleId, index, @@ -51,6 +54,7 @@ export const createRules = async ({ language, outputIndex, savedId, + timelineId, meta, filters, maxSignals, @@ -59,7 +63,9 @@ export const createRules = async ({ threats, to, type, + updatedAt: new Date().toISOString(), references, + version, }, schedule: { interval }, enabled, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts new file mode 100644 index 0000000000000..bb28a5575f51e --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock'; +import { AlertsClient } from '../../../../../alerting'; +import { + getResult, + getFindResultWithSingleHit, + getFindResultWithMultiHits, +} from '../routes/__mocks__/request_responses'; +import { getExistingPrepackagedRules } from './get_existing_prepackaged_rules'; + +describe('get_existing_prepackaged_rules', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('getExistingPrepackagedRules', () => { + test('should return a single item in a single page', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const rules = await getExistingPrepackagedRules({ + alertsClient: unsafeCast, + }); + expect(rules).toEqual([getResult()]); + }); + + test('should return 2 items over two pages, one per page', async () => { + const alertsClient = alertsClientMock.create(); + + const result1 = getResult(); + result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + + const result2 = getResult(); + result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + + alertsClient.find.mockResolvedValueOnce( + getFindResultWithMultiHits({ data: [result1], perPage: 1, page: 1, total: 2 }) + ); + alertsClient.find.mockResolvedValueOnce( + getFindResultWithMultiHits({ data: [result2], perPage: 1, page: 2, total: 2 }) + ); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const rules = await getExistingPrepackagedRules({ + alertsClient: unsafeCast, + }); + expect(rules).toEqual([result1, result2]); + }); + + test('should return 3 items with over 3 pages one per page', async () => { + const alertsClient = alertsClientMock.create(); + + const result1 = getResult(); + result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + + const result2 = getResult(); + result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + + const result3 = getResult(); + result3.id = 'f3e1bf0b-b95f-43da-b1de-5d2f0af2287a'; + + alertsClient.find.mockResolvedValueOnce( + getFindResultWithMultiHits({ data: [result1], perPage: 1, page: 1, total: 3 }) + ); + + alertsClient.find.mockResolvedValueOnce( + getFindResultWithMultiHits({ data: [result2], perPage: 1, page: 2, total: 3 }) + ); + + alertsClient.find.mockResolvedValueOnce( + getFindResultWithMultiHits({ data: [result3], perPage: 1, page: 2, total: 3 }) + ); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const rules = await getExistingPrepackagedRules({ + alertsClient: unsafeCast, + }); + expect(rules).toEqual([result1, result2, result3]); + }); + + test('should return 3 items over 1 pages with all on one page', async () => { + const alertsClient = alertsClientMock.create(); + + const result1 = getResult(); + result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + + const result2 = getResult(); + result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + + const result3 = getResult(); + result3.id = 'f3e1bf0b-b95f-43da-b1de-5d2f0af2287a'; + + alertsClient.find.mockResolvedValueOnce( + getFindResultWithMultiHits({ + data: [result1, result2, result3], + perPage: 3, + page: 1, + total: 3, + }) + ); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const rules = await getExistingPrepackagedRules({ + alertsClient: unsafeCast, + }); + expect(rules).toEqual([result1, result2, result3]); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts new file mode 100644 index 0000000000000..fa2e2124d0539 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; +import { AlertsClient } from '../../../../../alerting'; +import { RuleAlertType, isAlertTypes } from './types'; +import { findRules } from './find_rules'; + +export const DEFAULT_PER_PAGE: number = 100; + +export const getExistingPrepackagedRules = async ({ + alertsClient, + perPage = DEFAULT_PER_PAGE, +}: { + alertsClient: AlertsClient; + perPage?: number; +}): Promise => { + const firstPrepackedRules = await findRules({ + alertsClient, + filter: `alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`, + perPage, + page: 1, + }); + const totalPages = Math.ceil(firstPrepackedRules.total / firstPrepackedRules.perPage); + if (totalPages <= 1) { + if (isAlertTypes(firstPrepackedRules.data)) { + return firstPrepackedRules.data; + } else { + // If this was ever true, you have a really messed up system. + // This is keep typescript happy since we have an unknown with data + return []; + } + } else { + const returnPrepackagedRules = await Array(totalPages - 1) + .fill({}) + .map((_, page) => { + // page index starts at 2 as we already got the first page and we have more pages to go + return findRules({ + alertsClient, + filter: `alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`, + perPage, + page: page + 2, + }); + }) + .reduce>(async (accum, nextPage) => { + return [...(await accum), ...(await nextPage).data]; + }, Promise.resolve(firstPrepackedRules.data)); + + if (isAlertTypes(returnPrepackagedRules)) { + return returnPrepackagedRules; + } else { + // If this was ever true, you have a really messed up system. + // This is keep typescript happy since we have an unknown with data + return []; + } + } +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_prepackaged_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_prepackaged_rules.test.ts new file mode 100644 index 0000000000000..24184b023bee3 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_prepackaged_rules.test.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getPrepackagedRules } from './get_prepackaged_rules'; + +describe('get_existing_prepackaged_rules', () => { + test('should not throw any errors with the existing checked in pre-packaged rules', () => { + expect(() => getPrepackagedRules()).not.toThrow(); + }); + + test('should throw an exception if a pre-packaged rule is not valid', () => { + expect(() => getPrepackagedRules([{ not_valid_made_up_key: true }])).toThrow( + 'name: "(rule_name unknown)", rule_id: "(rule_id unknown)" within the folder rules/prepackaged_rules is not a valid detection engine rule. Expect the system to not work with pre-packaged rules until this rule is fixed or the file is removed. Error is: child "description" fails because ["description" is required]' + ); + }); + + test('should throw an exception with a message having rule_id and name in it', () => { + expect(() => getPrepackagedRules([{ name: 'rule name', rule_id: 'id-123' }])).toThrow( + 'name: "rule name", rule_id: "id-123" within the folder rules/prepackaged_rules is not a valid detection engine rule. Expect the system to not work with pre-packaged rules until this rule is fixed or the file is removed. Error is: child "description" fails because ["description" is required]' + ); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_prepackaged_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_prepackaged_rules.ts new file mode 100644 index 0000000000000..376ad4eb287d5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_prepackaged_rules.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RuleAlertParamsRest } from '../types'; +import { addPrepackagedRulesSchema } from '../routes/schemas/add_prepackaged_rules_schema'; +import { rawRules } from './prepackaged_rules'; + +/** + * Validate the rules from the file system and throw any errors indicating to the developer + * that they are adding incorrect schema rules. Also this will auto-flush in all the default + * aspects such as default interval of 5 minutes, default arrays, etc... + */ +export const validateAllPrepackagedRules = ( + rules: RuleAlertParamsRest[] +): RuleAlertParamsRest[] => { + return rules.map(rule => { + const validatedRule = addPrepackagedRulesSchema.validate(rule); + if (validatedRule.error != null) { + const ruleName = rule.name ? rule.name : '(rule_name unknown)'; + const ruleId = rule.rule_id ? rule.rule_id : '(rule_id unknown)'; + throw new TypeError( + `name: "${ruleName}", rule_id: "${ruleId}" within the folder rules/prepackaged_rules ` + + `is not a valid detection engine rule. Expect the system ` + + `to not work with pre-packaged rules until this rule is fixed ` + + `or the file is removed. Error is: ${validatedRule.error.message}` + ); + } else { + return validatedRule.value; + } + }); +}; + +export const getPrepackagedRules = (rules = rawRules): RuleAlertParamsRest[] => { + return validateAllPrepackagedRules(rules); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_install.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_install.test.ts new file mode 100644 index 0000000000000..1a2bd4a10ac2d --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_install.test.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getRulesToInstall } from './get_rules_to_install'; +import { getResult, fullRuleAlertParamsRest } from '../routes/__mocks__/request_responses'; + +describe('get_rules_to_install', () => { + test('should return empty array if both rule sets are empty', () => { + const update = getRulesToInstall([], []); + expect(update).toEqual([]); + }); + + test('should return empty array if the two rule ids match', () => { + const ruleFromFileSystem = fullRuleAlertParamsRest(); + ruleFromFileSystem.rule_id = 'rule-1'; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-1'; + const update = getRulesToInstall([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([]); + }); + + test('should return the rule to install if the id of the two rules do not match', () => { + const ruleFromFileSystem = fullRuleAlertParamsRest(); + ruleFromFileSystem.rule_id = 'rule-1'; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-2'; + const update = getRulesToInstall([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([ruleFromFileSystem]); + }); + + test('should return two rules to install if both the ids of the two rules do not match', () => { + const ruleFromFileSystem1 = fullRuleAlertParamsRest(); + ruleFromFileSystem1.rule_id = 'rule-1'; + + const ruleFromFileSystem2 = fullRuleAlertParamsRest(); + ruleFromFileSystem2.rule_id = 'rule-2'; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-3'; + const update = getRulesToInstall([ruleFromFileSystem1, ruleFromFileSystem2], [installedRule]); + expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]); + }); + + test('should return two rules of three to install if both the ids of the two rules do not match but the third does', () => { + const ruleFromFileSystem1 = fullRuleAlertParamsRest(); + ruleFromFileSystem1.rule_id = 'rule-1'; + + const ruleFromFileSystem2 = fullRuleAlertParamsRest(); + ruleFromFileSystem2.rule_id = 'rule-2'; + + const ruleFromFileSystem3 = fullRuleAlertParamsRest(); + ruleFromFileSystem3.rule_id = 'rule-3'; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-3'; + const update = getRulesToInstall( + [ruleFromFileSystem1, ruleFromFileSystem2, ruleFromFileSystem3], + [installedRule] + ); + expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_install.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_install.ts new file mode 100644 index 0000000000000..1c795941cbb83 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_install.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RuleAlertParamsRest } from '../types'; +import { RuleAlertType } from './types'; + +export const getRulesToInstall = ( + rulesFromFileSystem: RuleAlertParamsRest[], + installedRules: RuleAlertType[] +): RuleAlertParamsRest[] => { + return rulesFromFileSystem.filter( + rule => !installedRules.some(installedRule => installedRule.params.ruleId === rule.rule_id) + ); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_update.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_update.test.ts new file mode 100644 index 0000000000000..7f1b64d33cd9b --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_update.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getRulesToUpdate } from './get_rules_to_update'; +import { getResult, fullRuleAlertParamsRest } from '../routes/__mocks__/request_responses'; + +describe('get_rules_to_update', () => { + test('should return empty array if both rule sets are empty', () => { + const update = getRulesToUpdate([], []); + expect(update).toEqual([]); + }); + + test('should return empty array if the id of the two rules do not match', () => { + const ruleFromFileSystem = fullRuleAlertParamsRest(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-2'; + installedRule.params.version = 1; + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([]); + }); + + test('should return empty array if the id of file system rule is less than the installed version', () => { + const ruleFromFileSystem = fullRuleAlertParamsRest(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 1; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 2; + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([]); + }); + + test('should return empty array if the id of file system rule is the same as the installed version', () => { + const ruleFromFileSystem = fullRuleAlertParamsRest(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 1; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 1; + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([]); + }); + + test('should return the rule to update if the id of file system rule is greater than the installed version', () => { + const ruleFromFileSystem = fullRuleAlertParamsRest(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 1; + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([ruleFromFileSystem]); + }); + + test('should return 1 rule out of 2 to update if the id of file system rule is greater than the installed version of just one', () => { + const ruleFromFileSystem = fullRuleAlertParamsRest(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + + const installedRule2 = getResult(); + installedRule2.params.ruleId = 'rule-2'; + installedRule2.params.version = 1; + + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule1, installedRule2]); + expect(update).toEqual([ruleFromFileSystem]); + }); + + test('should return 2 rules out of 2 to update if the id of file system rule is greater than the installed version of both', () => { + const ruleFromFileSystem1 = fullRuleAlertParamsRest(); + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const ruleFromFileSystem2 = fullRuleAlertParamsRest(); + ruleFromFileSystem2.rule_id = 'rule-2'; + ruleFromFileSystem2.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + + const installedRule2 = getResult(); + installedRule2.params.ruleId = 'rule-2'; + installedRule2.params.version = 1; + + const update = getRulesToUpdate( + [ruleFromFileSystem1, ruleFromFileSystem2], + [installedRule1, installedRule2] + ); + expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_update.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_update.ts new file mode 100644 index 0000000000000..10b849493858a --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_rules_to_update.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RuleAlertParamsRest } from '../types'; +import { RuleAlertType } from './types'; + +export const getRulesToUpdate = ( + rulesFromFileSystem: RuleAlertParamsRest[], + installedRules: RuleAlertType[] +): RuleAlertParamsRest[] => { + return rulesFromFileSystem.filter(rule => + installedRules.some(installedRule => { + return ( + rule.rule_id === installedRule.params.ruleId && rule.version > installedRule.params.version + ); + }) + ); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts new file mode 100644 index 0000000000000..9acfbf8c43221 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ActionsClient } from '../../../../../actions'; +import { AlertsClient } from '../../../../../alerting'; +import { createRules } from './create_rules'; +import { RuleAlertParamsRest } from '../types'; + +export const installPrepackagedRules = async ( + alertsClient: AlertsClient, + actionsClient: ActionsClient, + rules: RuleAlertParamsRest[], + outputIndex: string +): Promise => { + await rules.forEach(async rule => { + const { + description, + enabled, + false_positives: falsePositives, + from, + immutable, + query, + language, + saved_id: savedId, + timeline_id: timelineId, + meta, + filters, + rule_id: ruleId, + index, + interval, + max_signals: maxSignals, + risk_score: riskScore, + name, + severity, + tags, + to, + type, + threats, + references, + version, + } = rule; + createRules({ + alertsClient, + actionsClient, + description, + enabled, + falsePositives, + from, + immutable, + query, + language, + outputIndex, + savedId, + timelineId, + meta, + filters, + ruleId, + index, + interval, + maxSignals, + riskScore, + name, + severity, + tags, + to, + type, + threats, + references, + version, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }); + }); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_internet_explorer.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_internet_explorer.json new file mode 100644 index 0000000000000..1fd9fc0bb0d32 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_internet_explorer.json @@ -0,0 +1,68 @@ +{ + "rule_id": "a0b554d2-85ed-4998-ada3-4ca58b508b35", + "risk_score": 50, + "description": "Command shell started by Internet Explorer", + "immutable": true, + "interval": "5m", + "name": "Command shell started by Internet Explorer", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.parent.name:iexplore.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "process.name", + "value": "cmd.exe", + "params": { + "query": "cmd.exe" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "process.name": { + "query": "cmd.exe", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_powershell.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_powershell.json new file mode 100644 index 0000000000000..594e3d5f650f9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_powershell.json @@ -0,0 +1,68 @@ +{ + "rule_id": "ab4bbfa5-4127-40bf-852f-bdc6afdb2a06", + "risk_score": 50, + "description": "Command shell started by Powershell", + "immutable": true, + "interval": "5m", + "name": "Command shell started by Powershell", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.parent.name:powershell.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "process.name", + "value": "cmd.exe", + "params": { + "query": "cmd.exe" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "process.name": { + "query": "cmd.exe", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_svchost.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_svchost.json new file mode 100644 index 0000000000000..02f7516d5cd79 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_svchost.json @@ -0,0 +1,68 @@ +{ + "rule_id": "2e4f8a5e-ce68-44e0-9243-1f57d44c4f30", + "risk_score": 50, + "description": "Command shell started by Svchost", + "immutable": true, + "interval": "5m", + "name": "Command shell started by Svchost", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.parent.name:svchost.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "process.name", + "value": "cmd.exe", + "params": { + "query": "cmd.exe" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "process.name": { + "query": "cmd.exe", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/index.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/index.ts new file mode 100644 index 0000000000000..406432bcbda00 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/index.ts @@ -0,0 +1,294 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// Auto generated file from scripts/convert_saved_search_rules.js +// Do not hand edit. Run the script against a set of saved searches instead + +import rule1 from './windows_powershell_connecting_to_the_internet.json'; +import rule2 from './windows_net_user_command_activity.json'; +import rule3 from './windows_image_load_from_a_temp_directory.json'; +import rule4 from './network_ssh_secure_shell_to_the_internet.json'; +import rule5 from './suricata_nonhttp_traffic_on_tcp_port_80.json'; +import rule6 from './windows_misc_lolbin_connecting_to_the_internet.json'; +import rule7 from './linux_strace_activity.json'; +import rule8 from './suricata_directory_reversal_characters_in_an_http_request.json'; +import rule9 from './suricata_dns_traffic_on_unusual_udp_port.json'; +import rule10 from './network_telnet_port_activity.json'; +import rule11 from './suricata_directory_traversal_in_downloaded_zip_file.json'; +import rule12 from './windows_execution_via_microsoft_html_application_hta.json'; +import rule13 from './windows_credential_dumping_commands.json'; +import rule14 from './windows_net_command_activity_by_the_system_account.json'; +import rule15 from './windows_register_server_program_connecting_to_the_internet.json'; +import rule16 from './linux_java_process_connecting_to_the_internet.json'; +import rule17 from './suricata_imap_traffic_on_unusual_port_internet_destination.json'; +import rule18 from './suricata_double_encoded_characters_in_a_uri.json'; +import rule19 from './network_tor_activity_to_the_internet.json'; +import rule20 from './windows_registry_query_local.json'; +import rule21 from './linux_netcat_network_connection.json'; +import rule22 from './windows_defense_evasion_via_filter_manager.json'; +import rule23 from './suricata_nondns_traffic_on_udp_port_53.json'; +import rule24 from './suricata_double_encoded_characters_in_an_http_post.json'; +import rule25 from './command_shell_started_by_internet_explorer.json'; +import rule26 from './network_vnc_virtual_network_computing_from_the_internet.json'; +import rule27 from './windows_nmap_activity.json'; +import rule28 from './suspicious_process_started_by_a_script.json'; +import rule29 from './windows_network_anomalous_windows_process_using_https_ports.json'; +import rule30 from './powershell_network_connection.json'; +import rule31 from './windows_signed_binary_proxy_execution.json'; +import rule32 from './linux_kernel_module_activity.json'; +import rule33 from './network_vnc_virtual_network_computing_to_the_internet.json'; +import rule34 from './suricata_mimikatz_string_detected_in_http_response.json'; +import rule35 from './command_shell_started_by_svchost.json'; +import rule36 from './linux_tcpdump_activity.json'; +import rule37 from './process_started_by_ms_office_program_possible_payload.json'; +import rule38 from './windows_signed_binary_proxy_execution_download.json'; +import rule39 from './suricata_base64_encoded_startprocess_powershell_execution.json'; +import rule40 from './suricata_base64_encoded_invokecommand_powershell_execution.json'; +import rule41 from './suricata_directory_traversal_characters_in_http_response.json'; +import rule42 from './windows_microsoft_html_application_hta_connecting_to_the_internet.json'; +import rule43 from './suricata_tls_traffic_on_unusual_port_internet_destination.json'; +import rule44 from './process_started_by_acrobat_reader_possible_payload.json'; +import rule45 from './suricata_http_traffic_on_unusual_port_internet_destination.json'; +import rule46 from './windows_persistence_via_modification_of_existing_service.json'; +import rule47 from './windows_defense_evasion_or_persistence_via_hidden_files.json'; +import rule48 from './windows_execution_via_compiled_html_file.json'; +import rule49 from './linux_ptrace_activity.json'; +import rule50 from './suricata_nonimap_traffic_on_port_1443_imap.json'; +import rule51 from './windows_scheduled_task_activity.json'; +import rule52 from './suricata_ftp_traffic_on_unusual_port_internet_destination.json'; +import rule53 from './windows_wireshark_activity.json'; +import rule54 from './windows_execution_via_trusted_developer_utilities.json'; +import rule55 from './suricata_rpc_traffic_on_http_ports.json'; +import rule56 from './windows_process_discovery_via_tasklist_command.json'; +import rule57 from './suricata_cobaltstrike_artifact_in_an_dns_request.json'; +import rule58 from './suricata_serialized_php_detected.json'; +import rule59 from './windows_background_intelligent_transfer_service_bits_connecting_to_the_internet.json'; +import rule60 from './windows_registry_query_network.json'; +import rule61 from './windows_persistence_via_application_shimming.json'; +import rule62 from './network_proxy_port_activity_to_the_internet.json'; +import rule63 from './windows_whoami_command_activity.json'; +import rule64 from './suricata_shell_exec_php_function_in_an_http_post.json'; +import rule65 from './windump_activity.json'; +import rule66 from './windows_management_instrumentation_wmi_execution.json'; +import rule67 from './network_rdp_remote_desktop_protocol_from_the_internet.json'; +import rule68 from './windows_priv_escalation_via_accessibility_features.json'; +import rule69 from './psexec_activity.json'; +import rule70 from './linux_rawshark_activity.json'; +import rule71 from './suricata_nonftp_traffic_on_port_21.json'; +import rule72 from './network_ftp_file_transfer_protocol_activity_to_the_internet.json'; +import rule73 from './windows_certutil_connecting_to_the_internet.json'; +import rule74 from './suricata_nonsmb_traffic_on_tcp_port_139_smb.json'; +import rule75 from './network_rdp_remote_desktop_protocol_to_the_internet.json'; +import rule76 from './linux_whoami_commmand.json'; +import rule77 from './windows_persistence_or_priv_escalation_via_hooking.json'; +import rule78 from './linux_lzop_activity_possible_julianrunnels.json'; +import rule79 from './suricata_nontls_on_tls_port.json'; +import rule80 from './network_irc_internet_relay_chat_protocol_activity_to_the_internet.json'; +import rule81 from './linux_network_anomalous_process_using_https_ports.json'; +import rule82 from './windows_credential_dumping_via_registry_save.json'; +import rule83 from './network_rpc_remote_procedure_call_from_the_internet.json'; +import rule84 from './windows_credential_dumping_via_imageload.json'; +import rule85 from './windows_burp_ce_activity.json'; +import rule86 from './linux_hping_activity.json'; +import rule87 from './windows_command_prompt_connecting_to_the_internet.json'; +import rule88 from './network_nat_traversal_port_activity.json'; +import rule89 from './network_rpc_remote_procedure_call_to_the_internet.json'; +import rule90 from './suricata_possible_cobalt_strike_malleable_c2_null_response.json'; +import rule91 from './windows_remote_management_execution.json'; +import rule92 from './suricata_lazagne_artifact_in_an_http_post.json'; +import rule93 from './windows_netcat_network_activity.json'; +import rule94 from './windows_iodine_activity.json'; +import rule95 from './network_port_26_activity.json'; +import rule96 from './windows_execution_via_connection_manager.json'; +import rule97 from './linux_process_started_in_temp_directory.json'; +import rule98 from './suricata_eval_php_function_in_an_http_request.json'; +import rule99 from './linux_web_download.json'; +import rule100 from './suricata_ssh_traffic_not_on_port_22_internet_destination.json'; +import rule101 from './network_port_8000_activity.json'; +import rule102 from './windows_process_started_by_the_java_runtime.json'; +import rule103 from './suricata_possible_sql_injection_sql_commands_in_http_transactions.json'; +import rule104 from './network_smb_windows_file_sharing_activity_to_the_internet.json'; +import rule105 from './network_port_8000_activity_to_the_internet.json'; +import rule106 from './command_shell_started_by_powershell.json'; +import rule107 from './linux_nmap_activity.json'; +import rule108 from './search_windows_10.json'; +import rule109 from './network_smtp_to_the_internet.json'; +import rule110 from './windows_payload_obfuscation_via_certutil.json'; +import rule111 from './network_pptp_point_to_point_tunneling_protocol_activity.json'; +import rule112 from './linux_unusual_shell_activity.json'; +import rule113 from './linux_mknod_activity.json'; +import rule114 from './network_sql_server_port_activity_to_the_internet.json'; +import rule115 from './suricata_commonly_abused_dns_domain_detected.json'; +import rule116 from './linux_iodine_activity.json'; +import rule117 from './suricata_mimikatz_artifacts_in_an_http_post.json'; +import rule118 from './windows_execution_via_net_com_assemblies.json'; +import rule119 from './suricata_dns_traffic_on_unusual_tcp_port.json'; +import rule120 from './suricata_base64_encoded_newobject_powershell_execution.json'; +import rule121 from './windows_netcat_activity.json'; +import rule122 from './windows_persistence_via_bits_jobs.json'; +import rule123 from './linux_nping_activity.json'; +import rule124 from './windows_execution_via_regsvr32.json'; +import rule125 from './process_started_by_windows_defender.json'; +import rule126 from './windows_indirect_command_execution.json'; +import rule127 from './network_ssh_secure_shell_from_the_internet.json'; +import rule128 from './windows_html_help_executable_program_connecting_to_the_internet.json'; +import rule129 from './suricata_windows_executable_served_by_jpeg_web_content.json'; +import rule130 from './network_dns_directly_to_the_internet.json'; +import rule131 from './windows_defense_evasion_via_windows_event_log_tools.json'; +import rule132 from './suricata_nondns_traffic_on_tcp_port_53.json'; +import rule133 from './windows_persistence_via_netshell_helper_dll.json'; +import rule134 from './windows_script_interpreter_connecting_to_the_internet.json'; +import rule135 from './windows_defense_evasion_decoding_using_certutil.json'; +import rule136 from './linux_shell_activity_by_web_server.json'; +import rule137 from './linux_ldso_process_activity.json'; +import rule138 from './windows_mimikatz_activity.json'; +import rule139 from './suricata_nonssh_traffic_on_port_22.json'; +import rule140 from './windows_data_compression_using_powershell.json'; +import rule141 from './windows_nmap_scan_activity.json'; + +export const rawRules = [ + rule1, + rule2, + rule3, + rule4, + rule5, + rule6, + rule7, + rule8, + rule9, + rule10, + rule11, + rule12, + rule13, + rule14, + rule15, + rule16, + rule17, + rule18, + rule19, + rule20, + rule21, + rule22, + rule23, + rule24, + rule25, + rule26, + rule27, + rule28, + rule29, + rule30, + rule31, + rule32, + rule33, + rule34, + rule35, + rule36, + rule37, + rule38, + rule39, + rule40, + rule41, + rule42, + rule43, + rule44, + rule45, + rule46, + rule47, + rule48, + rule49, + rule50, + rule51, + rule52, + rule53, + rule54, + rule55, + rule56, + rule57, + rule58, + rule59, + rule60, + rule61, + rule62, + rule63, + rule64, + rule65, + rule66, + rule67, + rule68, + rule69, + rule70, + rule71, + rule72, + rule73, + rule74, + rule75, + rule76, + rule77, + rule78, + rule79, + rule80, + rule81, + rule82, + rule83, + rule84, + rule85, + rule86, + rule87, + rule88, + rule89, + rule90, + rule91, + rule92, + rule93, + rule94, + rule95, + rule96, + rule97, + rule98, + rule99, + rule100, + rule101, + rule102, + rule103, + rule104, + rule105, + rule106, + rule107, + rule108, + rule109, + rule110, + rule111, + rule112, + rule113, + rule114, + rule115, + rule116, + rule117, + rule118, + rule119, + rule120, + rule121, + rule122, + rule123, + rule124, + rule125, + rule126, + rule127, + rule128, + rule129, + rule130, + rule131, + rule132, + rule133, + rule134, + rule135, + rule136, + rule137, + rule138, + rule139, + rule140, + rule141, +]; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_hping_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_hping_activity.json new file mode 100644 index 0000000000000..92308283717a5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_hping_activity.json @@ -0,0 +1,68 @@ +{ + "rule_id": "90169566-2260-4824-b8e4-8615c3b4ed52", + "risk_score": 50, + "description": "Linux: Hping Activity", + "immutable": true, + "interval": "5m", + "name": "Linux: Hping Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: hping", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.dataset", + "value": "process", + "params": { + "query": "process" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.dataset": { + "query": "process", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_iodine_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_iodine_activity.json new file mode 100644 index 0000000000000..ded4b72fcbfc4 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_iodine_activity.json @@ -0,0 +1,68 @@ +{ + "rule_id": "041d4d41-9589-43e2-ba13-5680af75ebc2", + "risk_score": 50, + "description": "Linux: Iodine Activity", + "immutable": true, + "interval": "5m", + "name": "Linux: Iodine Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: (iodine or iodined)", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.dataset", + "value": "process", + "params": { + "query": "process" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.dataset": { + "query": "process", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_java_process_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_java_process_connecting_to_the_internet.json new file mode 100644 index 0000000000000..aba4954e3552a --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_java_process_connecting_to_the_internet.json @@ -0,0 +1,118 @@ +{ + "rule_id": "7f65b8c5-27ed-4cf6-a088-3a20d2f84bf5", + "risk_score": 50, + "description": "Linux: Java Process Connecting to the Internet", + "immutable": true, + "interval": "5m", + "name": "Linux: Java Process Connecting to the Internet", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "not destination.ip: 10.0.0.0/8 and not 172.16.0.0/12", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "process.name", + "value": "java", + "params": { + "query": "java" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "process.name": { + "query": "java", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "socket_opened", + "params": { + "query": "socket_opened" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "socket_opened", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": true, + "type": "phrase", + "key": "destination.ip", + "value": "127.0.0.1", + "params": { + "query": "127.0.0.1" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index" + }, + "query": { + "match": { + "destination.ip": { + "query": "127.0.0.1", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": true, + "type": "phrase", + "key": "destination.ip", + "value": "::1", + "params": { + "query": "::1" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[3].meta.index" + }, + "query": { + "match": { + "destination.ip": { + "query": "::1", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_kernel_module_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_kernel_module_activity.json new file mode 100644 index 0000000000000..4564d1afccf79 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_kernel_module_activity.json @@ -0,0 +1,68 @@ +{ + "rule_id": "81cc58f5-8062-49a2-ba84-5cc4b4d31c40", + "risk_score": 50, + "description": "Linux: Kernel Module Activity", + "immutable": true, + "interval": "5m", + "name": "Linux: Kernel Module Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: (insmod or kmod or modprobe or rmod)", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.dataset", + "value": "process", + "params": { + "query": "process" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.dataset": { + "query": "process", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ldso_process_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ldso_process_activity.json new file mode 100644 index 0000000000000..2db76834061b9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ldso_process_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "3f31a31c-f7cf-4268-a0df-ec1a98099e7f", + "risk_score": 50, + "description": "Linux ld.so process activity", + "immutable": true, + "interval": "5m", + "name": "Linux ld.so process activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:ld.so", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_lzop_activity_possible_julianrunnels.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_lzop_activity_possible_julianrunnels.json new file mode 100644 index 0000000000000..5b3a978813b79 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_lzop_activity_possible_julianrunnels.json @@ -0,0 +1,17 @@ +{ + "rule_id": "d89b05b1-9b2b-45ea-9876-4a74550af6a6", + "risk_score": 50, + "description": "Linux lzop activity - possible @JulianRunnels", + "immutable": true, + "interval": "5m", + "name": "Linux lzop activity - possible @JulianRunnels", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:lzop", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json new file mode 100644 index 0000000000000..04ddc409c1efe --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json @@ -0,0 +1,68 @@ +{ + "rule_id": "61c31c14-507f-4627-8c31-072556b89a9c", + "risk_score": 50, + "description": "Linux: Mknod Activity", + "immutable": true, + "interval": "5m", + "name": "Linux: Mknod Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: mknod", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.dataset", + "value": "process", + "params": { + "query": "process" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.dataset": { + "query": "process", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_netcat_network_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_netcat_network_connection.json new file mode 100644 index 0000000000000..1ba35bec8f517 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_netcat_network_connection.json @@ -0,0 +1,93 @@ +{ + "rule_id": "adb961e0-cb74-42a0-af9e-29fc41f88f5f", + "risk_score": 50, + "description": "Linux: Netcat Network Connection", + "immutable": true, + "interval": "5m", + "name": "Linux: Netcat Network Connection", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: (nc or ncat or netcat or netcat.openbsd or netcat.traditional)", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "socket_opened", + "params": { + "query": "socket_opened" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "socket_opened", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": true, + "type": "phrase", + "key": "destination.ip", + "value": "127.0.0.1", + "params": { + "query": "127.0.0.1" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "destination.ip": { + "query": "127.0.0.1", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": true, + "type": "phrase", + "key": "destination.ip", + "value": "::1", + "params": { + "query": "::1" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index" + }, + "query": { + "match": { + "destination.ip": { + "query": "::1", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_network_anomalous_process_using_https_ports.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_network_anomalous_process_using_https_ports.json new file mode 100644 index 0000000000000..d5bf37daab0f4 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_network_anomalous_process_using_https_ports.json @@ -0,0 +1,17 @@ +{ + "rule_id": "be40c674-1799-4a00-934d-0b2d54495913", + "risk_score": 50, + "description": "Linux Network - Anomalous Process Using HTTP/S Ports", + "immutable": true, + "interval": "5m", + "name": "Linux Network - Anomalous Process Using HTTP/S Ports", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(destination.port:443 or destination.port:80) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16 and not process.name:curl and not process.name:http and not process.name:https and not process.name:nginx and not process.name:packetbeat and not process.name:python2 and not process.name:snapd and not process.name:wget", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json new file mode 100644 index 0000000000000..430d6b6984d6c --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json @@ -0,0 +1,68 @@ +{ + "rule_id": "c87fca17-b3a9-4e83-b545-f30746c53920", + "risk_score": 50, + "description": "Linux: Nmap Activity", + "immutable": true, + "interval": "5m", + "name": "Linux: Nmap Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: nmap", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.dataset", + "value": "process", + "params": { + "query": "process" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.dataset": { + "query": "process", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nping_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nping_activity.json new file mode 100644 index 0000000000000..a87f42f1774bf --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nping_activity.json @@ -0,0 +1,68 @@ +{ + "rule_id": "0d69150b-96f8-467c-a86d-a67a3378ce77", + "risk_score": 50, + "description": "Linux: Nping Activity", + "immutable": true, + "interval": "5m", + "name": "Linux: Nping Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: nmap", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.dataset", + "value": "process", + "params": { + "query": "process" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.dataset": { + "query": "process", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_process_started_in_temp_directory.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_process_started_in_temp_directory.json new file mode 100644 index 0000000000000..2a83ff8c5d2c6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_process_started_in_temp_directory.json @@ -0,0 +1,68 @@ +{ + "rule_id": "df959768-b0c9-4d45-988c-5606a2be8e5a", + "risk_score": 50, + "description": "Linux: Process Started in Temp Directory", + "immutable": true, + "interval": "5m", + "name": "Linux: Process Started in Temp Directory", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.working_directory: /tmp", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.dataset", + "value": "process", + "params": { + "query": "process" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.dataset": { + "query": "process", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ptrace_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ptrace_activity.json new file mode 100644 index 0000000000000..0ac4365ae8b7e --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ptrace_activity.json @@ -0,0 +1,43 @@ +{ + "rule_id": "1bff9259-e160-4920-bf72-4c96b6dbb7af", + "risk_score": 50, + "description": "Linux: Ptrace Activity", + "immutable": true, + "interval": "5m", + "name": "Linux: Ptrace Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: ptrace", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_rawshark_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_rawshark_activity.json new file mode 100644 index 0000000000000..ff74ba8e51b87 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_rawshark_activity.json @@ -0,0 +1,68 @@ +{ + "rule_id": "30eb2b9d-b53b-4ba5-bfab-7119a8b84029", + "risk_score": 50, + "description": "Linux: Rawshark Activity", + "immutable": true, + "interval": "5m", + "name": "Linux: Rawshark Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: rawshark", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.dataset", + "value": "process", + "params": { + "query": "process" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.dataset": { + "query": "process", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_shell_activity_by_web_server.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_shell_activity_by_web_server.json new file mode 100644 index 0000000000000..7499f6bc17ac1 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_shell_activity_by_web_server.json @@ -0,0 +1,17 @@ +{ + "rule_id": "231876e7-4d1f-4d63-a47c-47dd1acdc1cb", + "risk_score": 50, + "description": "Linux: Shell Activity By Web Server", + "immutable": true, + "interval": "5m", + "name": "Linux: Shell Activity By Web Server", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: bash and (user.name: apache or www)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_strace_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_strace_activity.json new file mode 100644 index 0000000000000..5c813fbb62eb7 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_strace_activity.json @@ -0,0 +1,68 @@ +{ + "rule_id": "d6450d4e-81c6-46a3-bd94-079886318ed5", + "risk_score": 50, + "description": "Linux: Strace Activity", + "immutable": true, + "interval": "5m", + "name": "Linux: Strace Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: strace", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.dataset", + "value": "process", + "params": { + "query": "process" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.dataset": { + "query": "process", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_tcpdump_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_tcpdump_activity.json new file mode 100644 index 0000000000000..1df4ad8b469b9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_tcpdump_activity.json @@ -0,0 +1,43 @@ +{ + "rule_id": "7a137d76-ce3d-48e2-947d-2747796a78c0", + "risk_score": 50, + "description": "Linux: Tcpdump Activity", + "immutable": true, + "interval": "5m", + "name": "Linux: Tcpdump Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: tcpdump", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_unusual_shell_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_unusual_shell_activity.json new file mode 100644 index 0000000000000..efa84c22f928c --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_unusual_shell_activity.json @@ -0,0 +1,93 @@ +{ + "rule_id": "4cc78842-f8a9-4a20-b703-a596c4f24e4f", + "risk_score": 50, + "description": "Linux unusual shell activity", + "immutable": true, + "interval": "5m", + "name": "Linux unusual shell activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:*sh", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": true, + "type": "phrase", + "key": "process.name", + "value": "bash", + "params": { + "query": "bash" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "process.name": { + "query": "bash", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": true, + "type": "phrase", + "key": "process.executable", + "value": "/bin/dash", + "params": { + "query": "/bin/dash" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "process.executable": { + "query": "/bin/dash", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": true, + "type": "phrase", + "key": "process.name", + "value": "ReportCrash", + "params": { + "query": "ReportCrash" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index" + }, + "query": { + "match": { + "process.name": { + "query": "ReportCrash", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_web_download.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_web_download.json new file mode 100644 index 0000000000000..d9ee2ccc98f10 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_web_download.json @@ -0,0 +1,43 @@ +{ + "rule_id": "e8ec93a6-49d2-4467-8c12-81c435fcc519", + "risk_score": 50, + "description": "Linux: Web Download", + "immutable": true, + "interval": "5m", + "name": "Linux: Web Download", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: curl or wget", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "socket_opened", + "params": { + "query": "socket_opened" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "socket_opened", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_whoami_commmand.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_whoami_commmand.json new file mode 100644 index 0000000000000..47c01778786c2 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_whoami_commmand.json @@ -0,0 +1,68 @@ +{ + "rule_id": "120559c6-5e24-49f4-9e30-8ffe697df6b9", + "risk_score": 50, + "description": "Linux: Whoami Commmand", + "immutable": true, + "interval": "5m", + "name": "Linux: Whoami Commmand", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name: whoami", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "process_started", + "params": { + "query": "process_started" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "process_started", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.dataset", + "value": "process", + "params": { + "query": "process" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.dataset": { + "query": "process", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_dns_directly_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_dns_directly_to_the_internet.json new file mode 100644 index 0000000000000..3dfbb508b897f --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_dns_directly_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "6ea71ff0-9e95-475b-9506-2580d1ce6154", + "risk_score": 50, + "description": "Network - DNS Directly to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - DNS Directly to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:53 and not destination.ip: 169.254.169.254/32 and not destination.ip:127.0.0.53/32 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ftp_file_transfer_protocol_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ftp_file_transfer_protocol_activity_to_the_internet.json new file mode 100644 index 0000000000000..7462fd445d1ec --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ftp_file_transfer_protocol_activity_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "87ec6396-9ac4-4706-bcf0-2ebb22002f43", + "risk_score": 50, + "description": "Network - FTP (File Transfer Protocol) Activity to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - FTP (File Transfer Protocol) Activity to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(destination.port:20 or destination.port:21) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_irc_internet_relay_chat_protocol_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_irc_internet_relay_chat_protocol_activity_to_the_internet.json new file mode 100644 index 0000000000000..dee04ee4fea8a --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_irc_internet_relay_chat_protocol_activity_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "c6474c34-4953-447a-903e-9fcb7b6661aa", + "risk_score": 50, + "description": "Network - IRC (Internet Relay Chat) Protocol Activity to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - IRC (Internet Relay Chat) Protocol Activity to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(destination.port:6665 or destination.port:6666 or destination.port:6667 or destination.port:6668 or destination.port:6669) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_nat_traversal_port_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_nat_traversal_port_activity.json new file mode 100644 index 0000000000000..6363dd7529cd6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_nat_traversal_port_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "a9cb3641-ff4b-4cdc-a063-b4b8d02a67c7", + "risk_score": 50, + "description": "Network - NAT Traversal Port Activity\t", + "immutable": true, + "interval": "5m", + "name": "Network - NAT Traversal Port Activity\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:4500", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_26_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_26_activity.json new file mode 100644 index 0000000000000..bda9984167718 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_26_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "d7e62693-aab9-4f66-a21a-3d79ecdd603d", + "risk_score": 50, + "description": "Network - Port 26 Activity\t", + "immutable": true, + "interval": "5m", + "name": "Network - Port 26 Activity\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:26", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity.json new file mode 100644 index 0000000000000..efd92f988fd2b --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "9c5f8092-e3f7-4eda-b9d3-56eed28fb157", + "risk_score": 50, + "description": "Network - Port 8000 Activity", + "immutable": true, + "interval": "5m", + "name": "Network - Port 8000 Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:8000", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity_to_the_internet.json new file mode 100644 index 0000000000000..790773f5308bb --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "08d5d7e2-740f-44d8-aeda-e41f4263efaf", + "risk_score": 50, + "description": "Network - Port 8000 Activity to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - Port 8000 Activity to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:8000 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_pptp_point_to_point_tunneling_protocol_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_pptp_point_to_point_tunneling_protocol_activity.json new file mode 100644 index 0000000000000..f22a23648a7fa --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_pptp_point_to_point_tunneling_protocol_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "d2053495-8fe7-4168-b3df-dad844046be3", + "risk_score": 50, + "description": "Network - PPTP (Point to Point Tunneling Protocol) Activity\t", + "immutable": true, + "interval": "5m", + "name": "Network - PPTP (Point to Point Tunneling Protocol) Activity\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:1723", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_proxy_port_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_proxy_port_activity_to_the_internet.json new file mode 100644 index 0000000000000..e7cc9b2b07cfd --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_proxy_port_activity_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "ad0e5e75-dd89-4875-8d0a-dfdc1828b5f3", + "risk_score": 50, + "description": "Network - Proxy Port Activity to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - Proxy Port Activity to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(destination.port:8080 or destination.port:3128) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_from_the_internet.json new file mode 100644 index 0000000000000..c5a16bfef7248 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_from_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "8c1bdde8-4204-45c0-9e0c-c85ca3902488", + "risk_score": 50, + "description": "Network - RDP (Remote Desktop Protocol) from the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - RDP (Remote Desktop Protocol) from the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:3389 and not source.ip:10.0.0.0/8 and not source.ip:172.16.0.0/12 and not source.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_to_the_internet.json new file mode 100644 index 0000000000000..b069bd5e3ca67 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "e56993d2-759c-4120-984c-9ec9bb940fd5", + "risk_score": 50, + "description": "Network - RDP (Remote Desktop Protocol) to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - RDP (Remote Desktop Protocol) to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:3389 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_from_the_internet.json new file mode 100644 index 0000000000000..bef842ec2adc3 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_from_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "143cb236-0956-4f42-a706-814bcaa0cf5a", + "risk_score": 50, + "description": "Network - RPC (Remote Procedure Call) from the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - RPC (Remote Procedure Call) from the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:3389 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_to_the_internet.json new file mode 100644 index 0000000000000..15184aee86edb --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "32923416-763a-4531-bb35-f33b9232ecdb", + "risk_score": 50, + "description": "Network - RPC (Remote Procedure Call) to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - RPC (Remote Procedure Call) to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:135 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smb_windows_file_sharing_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smb_windows_file_sharing_activity_to_the_internet.json new file mode 100644 index 0000000000000..365490792ed37 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smb_windows_file_sharing_activity_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "c82b2bd8-d701-420c-ba43-f11a155b681a", + "risk_score": 50, + "description": "Network - SMB (Windows File Sharing) Activity to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - SMB (Windows File Sharing) Activity to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(destination.port:139 or destination.port:445) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smtp_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smtp_to_the_internet.json new file mode 100644 index 0000000000000..b16e84e8cea74 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smtp_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "67a9beba-830d-4035-bfe8-40b7e28f8ac4", + "risk_score": 50, + "description": "Network - SMTP to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - SMTP to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:25 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_sql_server_port_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_sql_server_port_activity_to_the_internet.json new file mode 100644 index 0000000000000..4e884f0de1167 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_sql_server_port_activity_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "139c7458-566a-410c-a5cd-f80238d6a5cd", + "risk_score": 50, + "description": "Network - SQL Server Port Activity to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - SQL Server Port Activity to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:1433 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_from_the_internet.json new file mode 100644 index 0000000000000..f7340b710be35 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_from_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "ea0784f0-a4d7-4fea-ae86-4baaf27a6f17", + "risk_score": 50, + "description": "Network - SSH (Secure Shell) from the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - SSH (Secure Shell) from the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:22 and not source.ip:10.0.0.0/8 and not source.ip:172.16.0.0/12 and not source.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_to_the_internet.json new file mode 100644 index 0000000000000..21877b9716aae --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "6f1500bc-62d7-4eb9-8601-7485e87da2f4", + "risk_score": 50, + "description": "Network - SSH (Secure Shell) to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - SSH (Secure Shell) to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:22 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_telnet_port_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_telnet_port_activity.json new file mode 100644 index 0000000000000..2d917277bcb85 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_telnet_port_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "34fde489-94b0-4500-a76f-b8a157cf9269", + "risk_score": 50, + "description": "Network - Telnet Port Activity\t", + "immutable": true, + "interval": "5m", + "name": "Network - Telnet Port Activity\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:23", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_tor_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_tor_activity_to_the_internet.json new file mode 100644 index 0000000000000..991cc02a2123f --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_tor_activity_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "7d2c38d7-ede7-4bdf-b140-445906e6c540", + "risk_score": 50, + "description": "Network - Tor Activity to the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - Tor Activity to the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(destination.port:9001 or destination.port:9030) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_from_the_internet.json new file mode 100644 index 0000000000000..5fbffa0149783 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_from_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "5700cb81-df44-46aa-a5d7-337798f53eb8", + "risk_score": 50, + "description": "Network - VNC (Virtual Network Computing) From the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - VNC (Virtual Network Computing) From the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:5800 and not source.ip:10.0.0.0/8 and not source.ip:172.16.0.0/12 and not source.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_to_the_internet.json new file mode 100644 index 0000000000000..9d3608cb9e05d --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "3ad49c61-7adc-42c1-b788-732eda2f5abf", + "risk_score": 50, + "description": "Network - VNC (Virtual Network Computing) To the Internet\t", + "immutable": true, + "interval": "5m", + "name": "Network - VNC (Virtual Network Computing) To the Internet\t", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "destination.port:5800 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/powershell_network_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/powershell_network_connection.json new file mode 100644 index 0000000000000..ba86dd5bdf1db --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/powershell_network_connection.json @@ -0,0 +1,68 @@ +{ + "rule_id": "8e792144-39a6-4a63-9779-2f12719dc132", + "risk_score": 50, + "description": "Powershell network connection", + "immutable": true, + "interval": "5m", + "name": "Powershell network connection", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:powershell.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Network connection detected (rule: NetworkConnect)", + "params": { + "query": "Network connection detected (rule: NetworkConnect)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Network connection detected (rule: NetworkConnect)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + }, + { + "meta": { + "negate": true, + "type": "phrase", + "key": "destination.ip", + "value": "169.254.169.254", + "params": { + "query": "169.254.169.254" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "destination.ip": { + "query": "169.254.169.254", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_acrobat_reader_possible_payload.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_acrobat_reader_possible_payload.json new file mode 100644 index 0000000000000..99968dbdcc00d --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_acrobat_reader_possible_payload.json @@ -0,0 +1,43 @@ +{ + "rule_id": "c359628d-d5af-4a20-99df-aeeea109b690", + "risk_score": 50, + "description": "Process started by Acrobat reader - possible payload", + "immutable": true, + "interval": "5m", + "name": "Process started by Acrobat reader - possible payload", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.parent.name:AcroRd32.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_ms_office_program_possible_payload.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_ms_office_program_possible_payload.json new file mode 100644 index 0000000000000..9241a2a44eb06 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_ms_office_program_possible_payload.json @@ -0,0 +1,43 @@ +{ + "rule_id": "3181b814-08e3-43f9-b77a-a2530603b131", + "risk_score": 50, + "description": "Process started by MS Office program - possible payload", + "immutable": true, + "interval": "5m", + "name": "Process started by MS Office program - possible payload", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": " process.parent.name:EXCEL.EXE or process.parent.name:MSPUB.EXE or process.parent.name:OUTLOOK.EXE or process.parent.name:POWERPNT.EXE or process.parent.name:VISIO.EXE or process.parent.name:WINWORD.EXE", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_windows_defender.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_windows_defender.json new file mode 100644 index 0000000000000..3f1dc90c99c97 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_windows_defender.json @@ -0,0 +1,17 @@ +{ + "rule_id": "b3da3321-417d-494b-854c-b40369e063f0", + "risk_score": 50, + "description": "Process started by Windows Defender", + "immutable": true, + "interval": "5m", + "name": "Process started by Windows Defender", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "parent.process.name:MsMpEng.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/psexec_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/psexec_activity.json new file mode 100644 index 0000000000000..3797b44c6d967 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/psexec_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "9511b7f4-3898-4813-8bd3-d810b03148ab", + "risk_score": 50, + "description": "PSexec activity", + "immutable": true, + "interval": "5m", + "name": "PSexec activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:PsExec.exe or process.name:PsExec64.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/search_windows_10.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/search_windows_10.json new file mode 100644 index 0000000000000..86c1c36f4b832 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/search_windows_10.json @@ -0,0 +1,66 @@ +{ + "rule_id": "5d00c579-794c-4f64-be52-1ed8cae2b11e", + "risk_score": 50, + "description": "(Search) Windows 10", + "immutable": true, + "interval": "5m", + "name": "(Search) Windows 10", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "", + "language": "kuery", + "filters": [ + { + "$state": { + "store": "appState" + }, + "meta": { + "alias": null, + "disabled": false, + "key": "agent.hostname", + "negate": false, + "params": { + "query": "LAPTOP-CQNI37L2" + }, + "type": "phrase", + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "agent.hostname": { + "query": "LAPTOP-CQNI37L2", + "type": "phrase" + } + } + } + }, + { + "meta": { + "alias": null, + "negate": false, + "disabled": false, + "type": "phrase", + "key": "event.provider", + "params": { + "query": "Microsoft-Windows-Sysmon" + }, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index" + }, + "query": { + "match": { + "event.provider": { + "query": "Microsoft-Windows-Sysmon", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_base64_encoded_invokecommand_powershell_execution.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_base64_encoded_invokecommand_powershell_execution.json new file mode 100644 index 0000000000000..332f174cad2cf --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_base64_encoded_invokecommand_powershell_execution.json @@ -0,0 +1,17 @@ +{ + "rule_id": "6ff01a30-95dd-471c-b61d-0fd9ee2d0a20", + "risk_score": 50, + "description": "Suricata Base64 Encoded Invoke-Command Powershell Execution", + "immutable": true, + "interval": "5m", + "name": "Suricata Base64 Encoded Invoke-Command Powershell Execution", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(event.module:suricata and event.kind:alert) and suricata.eve.alert.signature_id: (2610182 or 2610183 or 2610184 or 2610185 or 2610186 or 2610187)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_base64_encoded_newobject_powershell_execution.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_base64_encoded_newobject_powershell_execution.json new file mode 100644 index 0000000000000..a86f7fa07e7d9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_base64_encoded_newobject_powershell_execution.json @@ -0,0 +1,17 @@ +{ + "rule_id": "d14d5401-0f7a-4933-b816-1b8f823e3d84", + "risk_score": 50, + "description": "Suricata Base64 Encoded New-Object Powershell Execution", + "immutable": true, + "interval": "5m", + "name": "Suricata Base64 Encoded New-Object Powershell Execution", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(event.module:suricata and event.kind:alert) and suricata.eve.alert.signature_id: (2610188 or 2610189 or 2610190 or 2610191 or 2610192 or 2610193)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_base64_encoded_startprocess_powershell_execution.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_base64_encoded_startprocess_powershell_execution.json new file mode 100644 index 0000000000000..722ce65dd83e8 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_base64_encoded_startprocess_powershell_execution.json @@ -0,0 +1,17 @@ +{ + "rule_id": "372dce88-003d-4bcf-8c95-34ea8be180a1", + "risk_score": 50, + "description": "Suricata Base64 Encoded Start-Process Powershell Execution", + "immutable": true, + "interval": "5m", + "name": "Suricata Base64 Encoded Start-Process Powershell Execution", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(event.module:suricata and event.kind:alert) and suricata.eve.alert.signature_id: (2610194 or 2610195 or 2610196 or 2610197 or 2610198 or 2610199)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_cobaltstrike_artifact_in_an_dns_request.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_cobaltstrike_artifact_in_an_dns_request.json new file mode 100644 index 0000000000000..bffcd18235839 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_cobaltstrike_artifact_in_an_dns_request.json @@ -0,0 +1,17 @@ +{ + "rule_id": "481ef0f5-beda-4fa2-8bfb-039c95500deb", + "risk_score": 50, + "description": "Suricata CobaltStrike Artifact in an DNS Request", + "immutable": true, + "interval": "5m", + "name": "Suricata CobaltStrike Artifact in an DNS Request", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(event.module:suricata and event.kind:alert) and suricata.eve.alert.signature_id: (2610166 or 2610167 or 2610168)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_commonly_abused_dns_domain_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_commonly_abused_dns_domain_detected.json new file mode 100644 index 0000000000000..334a632697a81 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_commonly_abused_dns_domain_detected.json @@ -0,0 +1,17 @@ +{ + "rule_id": "1844dfe1-b05e-4ca6-b367-6b9e3a1fe227", + "risk_score": 50, + "description": "Suricata Commonly Abused DNS Domain Detected", + "immutable": true, + "interval": "5m", + "name": "Suricata Commonly Abused DNS Domain Detected", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature:(TGI* and *HUNT* and *Abused* and *TLD*) and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_directory_reversal_characters_in_an_http_request.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_directory_reversal_characters_in_an_http_request.json new file mode 100644 index 0000000000000..098b873210d6f --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_directory_reversal_characters_in_an_http_request.json @@ -0,0 +1,17 @@ +{ + "rule_id": "c0ca8090-60f8-4458-befe-c43687b648a3", + "risk_score": 50, + "description": "Suricata Directory Reversal Characters in an HTTP Request", + "immutable": true, + "interval": "5m", + "name": "Suricata Directory Reversal Characters in an HTTP Request", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(event.module:suricata and event.kind:alert) and suricata.eve.alert.signature_id: (2610161 or 2610162)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_directory_traversal_characters_in_http_response.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_directory_traversal_characters_in_http_response.json new file mode 100644 index 0000000000000..3da22fcb912a8 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_directory_traversal_characters_in_http_response.json @@ -0,0 +1,17 @@ +{ + "rule_id": "a6406974-ea70-45b5-b5d8-ca17695adbde", + "risk_score": 50, + "description": "Suricata Directory Traversal Characters in HTTP Response", + "immutable": true, + "interval": "5m", + "name": "Suricata Directory Traversal Characters in HTTP Response", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610086 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_directory_traversal_in_downloaded_zip_file.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_directory_traversal_in_downloaded_zip_file.json new file mode 100644 index 0000000000000..370f9f6ba83fc --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_directory_traversal_in_downloaded_zip_file.json @@ -0,0 +1,17 @@ +{ + "rule_id": "d5d990bc-303c-4241-8138-6ba3cf2ee93e", + "risk_score": 50, + "description": "Suricata Directory Traversal in Downloaded Zip File", + "immutable": true, + "interval": "5m", + "name": "Suricata Directory Traversal in Downloaded Zip File", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610085 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_dns_traffic_on_unusual_tcp_port.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_dns_traffic_on_unusual_tcp_port.json new file mode 100644 index 0000000000000..9389897a95b87 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_dns_traffic_on_unusual_tcp_port.json @@ -0,0 +1,17 @@ +{ + "rule_id": "deeae336-4ff7-4cf8-ae5b-18bce05da02e", + "risk_score": 50, + "description": "Suricata DNS Traffic on Unusual TCP Port", + "immutable": true, + "interval": "5m", + "name": "Suricata DNS Traffic on Unusual TCP Port", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610013 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_dns_traffic_on_unusual_udp_port.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_dns_traffic_on_unusual_udp_port.json new file mode 100644 index 0000000000000..a6bcf664bf803 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_dns_traffic_on_unusual_udp_port.json @@ -0,0 +1,17 @@ +{ + "rule_id": "2343d9a4-365b-45b2-acb0-76934d43c75b", + "risk_score": 50, + "description": "Suricata DNS Traffic on Unusual UDP Port", + "immutable": true, + "interval": "5m", + "name": "Suricata DNS Traffic on Unusual UDP Port", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610015 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_double_encoded_characters_in_a_uri.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_double_encoded_characters_in_a_uri.json new file mode 100644 index 0000000000000..005156b68ba98 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_double_encoded_characters_in_a_uri.json @@ -0,0 +1,17 @@ +{ + "rule_id": "1ed4d2d1-330c-4c7d-b32d-2d8805437946", + "risk_score": 50, + "description": "Suricata Double Encoded Characters in a URI", + "immutable": true, + "interval": "5m", + "name": "Suricata Double Encoded Characters in a URI", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(event.module:suricata and event.kind:alert) and suricata.eve.alert.signature_id: (2610092 or 2610093 or 2610094 or 2610095)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_double_encoded_characters_in_an_http_post.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_double_encoded_characters_in_an_http_post.json new file mode 100644 index 0000000000000..2ff186a4026bb --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_double_encoded_characters_in_an_http_post.json @@ -0,0 +1,17 @@ +{ + "rule_id": "a839a360-94ae-4219-b1cc-458d836333a7", + "risk_score": 50, + "description": "Suricata Double Encoded Characters in an HTTP POST", + "immutable": true, + "interval": "5m", + "name": "Suricata Double Encoded Characters in an HTTP POST", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610090 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_eval_php_function_in_an_http_request.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_eval_php_function_in_an_http_request.json new file mode 100644 index 0000000000000..16f47eb0ba663 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_eval_php_function_in_an_http_request.json @@ -0,0 +1,17 @@ +{ + "rule_id": "8c77b4ed-4e98-438b-adb0-d645d4a4ea26", + "risk_score": 50, + "description": "Suricata eval PHP Function in an HTTP Request", + "immutable": true, + "interval": "5m", + "name": "Suricata eval PHP Function in an HTTP Request", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610088 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_ftp_traffic_on_unusual_port_internet_destination.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_ftp_traffic_on_unusual_port_internet_destination.json new file mode 100644 index 0000000000000..40ada9bb87425 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_ftp_traffic_on_unusual_port_internet_destination.json @@ -0,0 +1,17 @@ +{ + "rule_id": "b1adc850-0fe3-4dac-94d3-6f240071f83a", + "risk_score": 50, + "description": "Suricata FTP Traffic on Unusual Port, Internet Destination", + "immutable": true, + "interval": "5m", + "name": "Suricata FTP Traffic on Unusual Port, Internet Destination", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610005 and (event.module:suricata and event.kind:alert) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_http_traffic_on_unusual_port_internet_destination.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_http_traffic_on_unusual_port_internet_destination.json new file mode 100644 index 0000000000000..8da00c75cedc3 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_http_traffic_on_unusual_port_internet_destination.json @@ -0,0 +1,17 @@ +{ + "rule_id": "43795909-913c-419d-8355-7f2880694bec", + "risk_score": 50, + "description": "Suricata HTTP Traffic On Unusual Port, Internet Destination", + "immutable": true, + "interval": "5m", + "name": "Suricata HTTP Traffic On Unusual Port, Internet Destination", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": " suricata.eve.alert.signature_id:2610001 and (event.module:suricata and event.kind:alert) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_imap_traffic_on_unusual_port_internet_destination.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_imap_traffic_on_unusual_port_internet_destination.json new file mode 100644 index 0000000000000..4f7bfc2baaf37 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_imap_traffic_on_unusual_port_internet_destination.json @@ -0,0 +1,17 @@ +{ + "rule_id": "738ee70b-7d0f-438f-98ac-a393df58c58f", + "risk_score": 50, + "description": "Suricata IMAP Traffic on Unusual Port, internet Destination", + "immutable": true, + "interval": "5m", + "name": "Suricata IMAP Traffic on Unusual Port, internet Destination", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610009 and (event.module:suricata and event.kind:alert) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_lazagne_artifact_in_an_http_post.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_lazagne_artifact_in_an_http_post.json new file mode 100644 index 0000000000000..ed46470838069 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_lazagne_artifact_in_an_http_post.json @@ -0,0 +1,17 @@ +{ + "rule_id": "c6e6f16f-66de-43d5-8ab7-599af536dedf", + "risk_score": 50, + "description": "Suricata LaZagne Artifact in an HTTP POST", + "immutable": true, + "interval": "5m", + "name": "Suricata LaZagne Artifact in an HTTP POST", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(event.module:suricata and event.kind:alert) and suricata.eve.alert.signature_id: (2610149 or 2610150)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_mimikatz_artifacts_in_an_http_post.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_mimikatz_artifacts_in_an_http_post.json new file mode 100644 index 0000000000000..b3a8079c16f11 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_mimikatz_artifacts_in_an_http_post.json @@ -0,0 +1,17 @@ +{ + "rule_id": "1b62e8af-c10d-4708-9a74-118cb1c9ed8a", + "risk_score": 50, + "description": "Suricata Mimikatz Artifacts in an HTTP POST", + "immutable": true, + "interval": "5m", + "name": "Suricata Mimikatz Artifacts in an HTTP POST", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610155 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_mimikatz_string_detected_in_http_response.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_mimikatz_string_detected_in_http_response.json new file mode 100644 index 0000000000000..c72f6b348e259 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_mimikatz_string_detected_in_http_response.json @@ -0,0 +1,17 @@ +{ + "rule_id": "2b365d3a-11a3-4bec-9698-b36c908f46ff", + "risk_score": 50, + "description": "Suricata Mimikatz String Detected in HTTP Response", + "immutable": true, + "interval": "5m", + "name": "Suricata Mimikatz String Detected in HTTP Response", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(event.module:suricata and event.kind:alert) and suricata.eve.alert.signature_id: (2610144 or 2610145 or 2610146 or 2610147 or 2610148)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nondns_traffic_on_tcp_port_53.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nondns_traffic_on_tcp_port_53.json new file mode 100644 index 0000000000000..66eff77cf43bc --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nondns_traffic_on_tcp_port_53.json @@ -0,0 +1,17 @@ +{ + "rule_id": "67c7d28e-8be4-49ae-9c89-5c328ea245dc", + "risk_score": 50, + "description": "Suricata non-DNS Traffic on TCP Port 53", + "immutable": true, + "interval": "5m", + "name": "Suricata non-DNS Traffic on TCP Port 53", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610014 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nondns_traffic_on_udp_port_53.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nondns_traffic_on_udp_port_53.json new file mode 100644 index 0000000000000..e09a4357ba5d4 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nondns_traffic_on_udp_port_53.json @@ -0,0 +1,17 @@ +{ + "rule_id": "ba6dea7f-ba98-4a86-b570-d05d85472e79", + "risk_score": 50, + "description": "Suricata non-DNS Traffic on UDP Port 53", + "immutable": true, + "interval": "5m", + "name": "Suricata non-DNS Traffic on UDP Port 53", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610016 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonftp_traffic_on_port_21.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonftp_traffic_on_port_21.json new file mode 100644 index 0000000000000..405be74eb8340 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonftp_traffic_on_port_21.json @@ -0,0 +1,17 @@ +{ + "rule_id": "ee2b07ec-94dd-48b2-b46b-7bef47cc43fc", + "risk_score": 50, + "description": "Suricata non-FTP Traffic on Port 21", + "immutable": true, + "interval": "5m", + "name": "Suricata non-FTP Traffic on Port 21", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610006 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonhttp_traffic_on_tcp_port_80.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonhttp_traffic_on_tcp_port_80.json new file mode 100644 index 0000000000000..cd93ceec2374f --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonhttp_traffic_on_tcp_port_80.json @@ -0,0 +1,17 @@ +{ + "rule_id": "70f9bd9f-accc-4da8-8674-38992096ddba", + "risk_score": 50, + "description": "Suricata non-HTTP Traffic on TCP Port 80", + "immutable": true, + "interval": "5m", + "name": "Suricata non-HTTP Traffic on TCP Port 80", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610002 and (event.module:suricata and event.kind:alert) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonimap_traffic_on_port_1443_imap.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonimap_traffic_on_port_1443_imap.json new file mode 100644 index 0000000000000..39e5fd188aa4a --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonimap_traffic_on_port_1443_imap.json @@ -0,0 +1,17 @@ +{ + "rule_id": "241b6a1d-4f73-4b68-bd98-22e909681930", + "risk_score": 50, + "description": "Suricata non-IMAP Traffic on Port 1443 (IMAP)", + "immutable": true, + "interval": "5m", + "name": "Suricata non-IMAP Traffic on Port 1443 (IMAP)", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610010 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonsmb_traffic_on_tcp_port_139_smb.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonsmb_traffic_on_tcp_port_139_smb.json new file mode 100644 index 0000000000000..0fd1c59a3bc62 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonsmb_traffic_on_tcp_port_139_smb.json @@ -0,0 +1,17 @@ +{ + "rule_id": "c259ab53-4b1a-42f6-b204-fe057c521515", + "risk_score": 50, + "description": "Suricata non-SMB Traffic on TCP Port 139 (SMB)", + "immutable": true, + "interval": "5m", + "name": "Suricata non-SMB Traffic on TCP Port 139 (SMB)", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610011 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonssh_traffic_on_port_22.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonssh_traffic_on_port_22.json new file mode 100644 index 0000000000000..3d1cc2e61b1a9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nonssh_traffic_on_port_22.json @@ -0,0 +1,17 @@ +{ + "rule_id": "256e9e8b-8366-4f23-8cbe-c9eb5ba25633", + "risk_score": 50, + "description": "Suricata non-SSH Traffic on Port 22", + "immutable": true, + "interval": "5m", + "name": "Suricata non-SSH Traffic on Port 22", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610008 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nontls_on_tls_port.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nontls_on_tls_port.json new file mode 100644 index 0000000000000..1fd905e6e4647 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_nontls_on_tls_port.json @@ -0,0 +1,17 @@ +{ + "rule_id": "b060c87f-af49-40eb-acee-561a1f1331aa", + "risk_score": 50, + "description": "Suricata non-TLS on TLS Port", + "immutable": true, + "interval": "5m", + "name": "Suricata non-TLS on TLS Port", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610004 and (event.module:suricata and event.kind:alert) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_possible_cobalt_strike_malleable_c2_null_response.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_possible_cobalt_strike_malleable_c2_null_response.json new file mode 100644 index 0000000000000..a6534d72a9655 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_possible_cobalt_strike_malleable_c2_null_response.json @@ -0,0 +1,17 @@ +{ + "rule_id": "6099a760-7293-4e26-8aa8-b984abb32ac6", + "risk_score": 50, + "description": "Suricata Possible Cobalt Strike Malleable C2 Null Response", + "immutable": true, + "interval": "5m", + "name": "Suricata Possible Cobalt Strike Malleable C2 Null Response", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(event.module:suricata and event.kind:alert) and suricata.eve.alert.signature_id: (2610202 or 2610203)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_possible_sql_injection_sql_commands_in_http_transactions.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_possible_sql_injection_sql_commands_in_http_transactions.json new file mode 100644 index 0000000000000..0a8b4a9861f9b --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_possible_sql_injection_sql_commands_in_http_transactions.json @@ -0,0 +1,17 @@ +{ + "rule_id": "cdfbcd5e-1d8e-47e6-b3f2-b09bce780640", + "risk_score": 50, + "description": "Suricata Possible SQL Injection - SQL Commands in HTTP Transactions", + "immutable": true, + "interval": "5m", + "name": "Suricata Possible SQL Injection - SQL Commands in HTTP Transactions", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(event.module:suricata and event.kind:alert) and suricata.eve.alert.signature_id: (2610117 or 2610118 or 2610118 or 2610119 or 2610121 or 2610122 or 2610123)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_rpc_traffic_on_http_ports.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_rpc_traffic_on_http_ports.json new file mode 100644 index 0000000000000..4431f46125ef3 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_rpc_traffic_on_http_ports.json @@ -0,0 +1,17 @@ +{ + "rule_id": "87e77fb6-b555-43be-adc5-f57c6aaf7cd0", + "risk_score": 50, + "description": "Suricata RPC Traffic on HTTP Ports", + "immutable": true, + "interval": "5m", + "name": "Suricata RPC Traffic on HTTP Ports", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610012 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_serialized_php_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_serialized_php_detected.json new file mode 100644 index 0000000000000..a176be109f8ff --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_serialized_php_detected.json @@ -0,0 +1,17 @@ +{ + "rule_id": "3baa5b65-d11e-40fb-a9b4-6b2a6a062d48", + "risk_score": 50, + "description": "Suricata Serialized PHP Detected", + "immutable": true, + "interval": "5m", + "name": "Suricata Serialized PHP Detected", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610091 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_shell_exec_php_function_in_an_http_post.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_shell_exec_php_function_in_an_http_post.json new file mode 100644 index 0000000000000..c1fdb1c083789 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_shell_exec_php_function_in_an_http_post.json @@ -0,0 +1,17 @@ +{ + "rule_id": "082fca48-4707-485a-aedb-340ee77e0687", + "risk_score": 50, + "description": "Suricata shell_exec PHP Function in an HTTP POST", + "immutable": true, + "interval": "5m", + "name": "Suricata shell_exec PHP Function in an HTTP POST", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610087 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_ssh_traffic_not_on_port_22_internet_destination.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_ssh_traffic_not_on_port_22_internet_destination.json new file mode 100644 index 0000000000000..ee0510d1e37ac --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_ssh_traffic_not_on_port_22_internet_destination.json @@ -0,0 +1,17 @@ +{ + "rule_id": "82265eef-1212-4c4f-af04-f977a3060592", + "risk_score": 50, + "description": "Suricata SSH Traffic Not on Port 22, Internet Destination", + "immutable": true, + "interval": "5m", + "name": "Suricata SSH Traffic Not on Port 22, Internet Destination", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610007 and (event.module:suricata and event.kind:alert) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_tls_traffic_on_unusual_port_internet_destination.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_tls_traffic_on_unusual_port_internet_destination.json new file mode 100644 index 0000000000000..3d0d5175168f1 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_tls_traffic_on_unusual_port_internet_destination.json @@ -0,0 +1,17 @@ +{ + "rule_id": "6c1db8ba-db4b-4513-a0e3-b3c857ba8b05", + "risk_score": 50, + "description": "Suricata TLS Traffic on Unusual Port, Internet Destination", + "immutable": true, + "interval": "5m", + "name": "Suricata TLS Traffic on Unusual Port, Internet Destination", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610003 and (event.module:suricata and event.kind:alert) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_windows_executable_served_by_jpeg_web_content.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_windows_executable_served_by_jpeg_web_content.json new file mode 100644 index 0000000000000..7ab997b11fb26 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suricata_windows_executable_served_by_jpeg_web_content.json @@ -0,0 +1,17 @@ +{ + "rule_id": "f7f038f4-b97a-4d0c-b3b6-d5fa1ad15951", + "risk_score": 50, + "description": "Suricata Windows Executable Served by JPEG Web Content", + "immutable": true, + "interval": "5m", + "name": "Suricata Windows Executable Served by JPEG Web Content", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "suricata.eve.alert.signature_id:2610084 and (event.module:suricata and event.kind:alert)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suspicious_process_started_by_a_script.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suspicious_process_started_by_a_script.json new file mode 100644 index 0000000000000..43e246cf7c26f --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suspicious_process_started_by_a_script.json @@ -0,0 +1,43 @@ +{ + "rule_id": "e49b532b-3e52-4f3d-90f6-05a86982d347", + "risk_score": 50, + "description": "Suspicious process started by a script", + "immutable": true, + "interval": "5m", + "name": "Suspicious process started by a script", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(process.parent.name:cmd.exe or process.parent.name:cscript.exe or process.parent.name:mshta.exe or process.parent.name:powershell.exe or process.parent.name:rundll32.exe or process.parent.name:wscript.exe or process.parent.name:wmiprvse.exe) and (process.name:bitsadmin.exe or process.name:certutil.exe or mshta.exe or process.name:nslookup.exe or process.name:schtasks.exe)", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_background_intelligent_transfer_service_bits_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_background_intelligent_transfer_service_bits_connecting_to_the_internet.json new file mode 100644 index 0000000000000..5842b67076edd --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_background_intelligent_transfer_service_bits_connecting_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "7edadee3-98ae-472c-b1c4-8c0a2c4877cc", + "risk_score": 50, + "description": "Windows: Background Intelligent Transfer Service (BITS) Connecting to the Internet", + "immutable": true, + "interval": "5m", + "name": "Windows: Background Intelligent Transfer Service (BITS) Connecting to the Internet", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:bitsadmin.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_burp_ce_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_burp_ce_activity.json new file mode 100644 index 0000000000000..4d87d53eb246d --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_burp_ce_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "0f09845b-2ec8-4770-8155-7df3d4e402cc", + "risk_score": 50, + "description": "Windows Burp CE activity", + "immutable": true, + "interval": "5m", + "name": "Windows Burp CE activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:BurpSuiteCommunity.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_connecting_to_the_internet.json new file mode 100644 index 0000000000000..2e0c9e2b71ae6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_connecting_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "1a2cf526-6784-4c51-a2b9-f0adcc05d85c", + "risk_score": 50, + "description": "Windows: Certutil Connecting to the Internet", + "immutable": true, + "interval": "5m", + "name": "Windows: Certutil Connecting to the Internet", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:certutil.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_prompt_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_prompt_connecting_to_the_internet.json new file mode 100644 index 0000000000000..3a0e9a2f35566 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_prompt_connecting_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "89f9a4b0-9f8f-4ee0-8823-c4751a6d6696", + "risk_score": 50, + "description": "Windows: Command Prompt Connecting to the Internet", + "immutable": true, + "interval": "5m", + "name": "Windows: Command Prompt Connecting to the Internet", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:cmd.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_commands.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_commands.json new file mode 100644 index 0000000000000..2273249c49b61 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_commands.json @@ -0,0 +1,17 @@ +{ + "rule_id": "66885745-ea38-432c-9edb-599b943948d4", + "risk_score": 50, + "description": "Windows Credential Dumping Commands", + "immutable": true, + "interval": "5m", + "name": "Windows Credential Dumping Commands", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code: 1 and process.args:*Invoke-Mimikatz-DumpCreds* or process.args:*gsecdump* or process.args:*wce* or (process.args:*procdump* and process.args:*lsass*) or (process.args:*ntdsutil* and process.args:*ntds*ifm*create*)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_imageload.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_imageload.json new file mode 100644 index 0000000000000..5c9c72efb7aa7 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_imageload.json @@ -0,0 +1,17 @@ +{ + "rule_id": "f872647c-d070-4b1c-afcc-055f081d9205", + "risk_score": 50, + "description": "Windows Credential Dumping via ImageLoad", + "immutable": true, + "interval": "5m", + "name": "Windows Credential Dumping via ImageLoad", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:7 and not process.name:Sysmon.exe and not process.name:Sysmon64.exe and not process.name:svchost.exe and not process.name:logonui.exe and (file.path:*samlib.dll* or file.path:*WinSCard.dll* or file.path:*cryptdll.dll* or file.path:*hid.dll* or file.path:*vaultcli.dll*)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_registry_save.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_registry_save.json new file mode 100644 index 0000000000000..38e23c5759162 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_registry_save.json @@ -0,0 +1,17 @@ +{ + "rule_id": "9f6fb56f-4bbd-404e-b955-49dfba7c0e68", + "risk_score": 50, + "description": "Windows Credential Dumping via Registry Save", + "immutable": true, + "interval": "5m", + "name": "Windows Credential Dumping via Registry Save", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code: 1 and process.name:reg.exe and process.args:*save* and (process.args:*sam* or process.args:*system*)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_data_compression_using_powershell.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_data_compression_using_powershell.json new file mode 100644 index 0000000000000..604c4148d3056 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_data_compression_using_powershell.json @@ -0,0 +1,17 @@ +{ + "rule_id": "bc913943-e1f9-4bf5-a593-caca7c2eb0c3", + "risk_score": 50, + "description": "Windows Data Compression Using Powershell", + "immutable": true, + "interval": "5m", + "name": "Windows Data Compression Using Powershell", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code: 1 and process.name:powershell.exe and (process.args:*Recurse* and process.args:*Compress-Archive*)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_decoding_using_certutil.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_decoding_using_certutil.json new file mode 100644 index 0000000000000..7d6e6c7d53976 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_decoding_using_certutil.json @@ -0,0 +1,17 @@ +{ + "rule_id": "d9642bf2-87d0-45c2-8781-2bd2017cdbb8", + "risk_score": 50, + "description": "Windows Defense Evasion - Decoding Using Certutil", + "immutable": true, + "interval": "5m", + "name": "Windows Defense Evasion - Decoding Using Certutil", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.name:attrib.exe and (process.args:*+h* or process.args:*+s*)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_or_persistence_via_hidden_files.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_or_persistence_via_hidden_files.json new file mode 100644 index 0000000000000..f8f2b6a3fac2a --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_or_persistence_via_hidden_files.json @@ -0,0 +1,17 @@ +{ + "rule_id": "340a0063-baba-447b-8396-26a5cc1eb684", + "risk_score": 50, + "description": "Windows Defense Evasion or Persistence via Hidden Files", + "immutable": true, + "interval": "5m", + "name": "Windows Defense Evasion or Persistence via Hidden Files", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.name:attrib.exe and (process.args:*+h* or process.args:*+s*)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_filter_manager.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_filter_manager.json new file mode 100644 index 0000000000000..362ed715a8ebf --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_filter_manager.json @@ -0,0 +1,17 @@ +{ + "rule_id": "06dceabf-adca-48af-ac79-ffdf4c3b1e9a", + "risk_score": 50, + "description": "Windows Defense evasion via Filter Manager", + "immutable": true, + "interval": "5m", + "name": "Windows Defense evasion via Filter Manager", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.name:fltmc.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_windows_event_log_tools.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_windows_event_log_tools.json new file mode 100644 index 0000000000000..e58399c8c39d2 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_windows_event_log_tools.json @@ -0,0 +1,17 @@ +{ + "rule_id": "07979a67-ab4d-460f-9ff3-bf1352de6762", + "risk_score": 50, + "description": "Windows Defense Evasion via Windows Event Log Tools", + "immutable": true, + "interval": "5m", + "name": "Windows Defense Evasion via Windows Event Log Tools", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.name:wevtutil.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_compiled_html_file.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_compiled_html_file.json new file mode 100644 index 0000000000000..dac45ae03c237 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_compiled_html_file.json @@ -0,0 +1,17 @@ +{ + "rule_id": "e3343ab9-4245-4715-b344-e11c56b0a47f", + "risk_score": 50, + "description": "Windows Execution via Compiled HTML File", + "immutable": true, + "interval": "5m", + "name": "Windows Execution via Compiled HTML File", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.name:hh.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_connection_manager.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_connection_manager.json new file mode 100644 index 0000000000000..f97b1da2d5885 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_connection_manager.json @@ -0,0 +1,17 @@ +{ + "rule_id": "f2728299-167a-489c-913c-2e0955ac3c40", + "risk_score": 50, + "description": "Windows Execution via Connection Manager", + "immutable": true, + "interval": "5m", + "name": "Windows Execution via Connection Manager", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.parent.name:pcalua.exe or (process.name:bash.exe or process.name:forfiles.exe or process.name:pcalua.exe)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_microsoft_html_application_hta.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_microsoft_html_application_hta.json new file mode 100644 index 0000000000000..3a98dcc992e3d --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_microsoft_html_application_hta.json @@ -0,0 +1,17 @@ +{ + "rule_id": "b007cc82-c522-48d1-b7a7-53f63c50c494", + "risk_score": 50, + "description": "Windows Execution via Microsoft HTML Application (HTA)", + "immutable": true, + "interval": "5m", + "name": "Windows Execution via Microsoft HTML Application (HTA)", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and (process.parent.args:*mshta* or process.args:*mshta*)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_net_com_assemblies.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_net_com_assemblies.json new file mode 100644 index 0000000000000..be40d7616290f --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_net_com_assemblies.json @@ -0,0 +1,17 @@ +{ + "rule_id": "5c12412f-602c-4120-8c4f-69d723dbba04", + "risk_score": 50, + "description": "Windows Execution via .NET COM Assemblies", + "immutable": true, + "interval": "5m", + "name": "Windows Execution via .NET COM Assemblies", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and (process.name:regasm.exe or process.name:regsvcs.exe)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_regsvr32.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_regsvr32.json new file mode 100644 index 0000000000000..c4351f70e385d --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_regsvr32.json @@ -0,0 +1,17 @@ +{ + "rule_id": "b7333d08-be4b-4cb4-b81e-924ae37b3143", + "risk_score": 50, + "description": "Windows Execution via Regsvr32", + "immutable": true, + "interval": "5m", + "name": "Windows Execution via Regsvr32", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code: 1 and scrobj.dll and (process.name:certutil.exe or process.name:regsvr32.exe or process.name:rundll32.exe)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_trusted_developer_utilities.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_trusted_developer_utilities.json new file mode 100644 index 0000000000000..cf0701685af27 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_trusted_developer_utilities.json @@ -0,0 +1,17 @@ +{ + "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae1", + "risk_score": 50, + "description": "Windows Execution via Trusted Developer Utilities", + "immutable": true, + "interval": "5m", + "name": "Windows Execution via Trusted Developer Utilities", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and (process.name:MSBuild.exe or process.name:msxsl.exe)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_html_help_executable_program_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_html_help_executable_program_connecting_to_the_internet.json new file mode 100644 index 0000000000000..6fa1d4eae7461 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_html_help_executable_program_connecting_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "b29ee2be-bf99-446c-ab1a-2dc0183394b8", + "risk_score": 50, + "description": "Windows: HTML Help executable Program Connecting to the Internet", + "immutable": true, + "interval": "5m", + "name": "Windows: HTML Help executable Program Connecting to the Internet", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:hh.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_image_load_from_a_temp_directory.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_image_load_from_a_temp_directory.json new file mode 100644 index 0000000000000..6e735cae12985 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_image_load_from_a_temp_directory.json @@ -0,0 +1,43 @@ +{ + "rule_id": "f23e4cc7-6825-4a28-b27a-e67437a9a806", + "risk_score": 50, + "description": "Windows image load from a temp directory", + "immutable": true, + "interval": "5m", + "name": "Windows image load from a temp directory", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "file.path:Temp", + "language": "kuery", + "filters": [ + { + "meta": { + "alias": null, + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Image loaded (rule: ImageLoad)", + "params": { + "query": "Image loaded (rule: ImageLoad)" + }, + "disabled": false, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Image loaded (rule: ImageLoad)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_indirect_command_execution.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_indirect_command_execution.json new file mode 100644 index 0000000000000..bfcf40d403fbe --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_indirect_command_execution.json @@ -0,0 +1,17 @@ +{ + "rule_id": "ff969842-c573-4e69-8e12-02fb303290f2", + "risk_score": 50, + "description": "Windows Indirect Command Execution", + "immutable": true, + "interval": "5m", + "name": "Windows Indirect Command Execution", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.parent.name:pcalua.exe or (process.name:bash.exe or process.name:forfiles.exe or process.name:pcalua.exe)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_iodine_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_iodine_activity.json new file mode 100644 index 0000000000000..7fb35a0176b44 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_iodine_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "fcbbf0b2-99c5-4c7f-8411-dc9ee392e43f", + "risk_score": 50, + "description": "Windows Iodine activity", + "immutable": true, + "interval": "5m", + "name": "Windows Iodine activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:iodine.exe or process.name:iodined.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_management_instrumentation_wmi_execution.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_management_instrumentation_wmi_execution.json new file mode 100644 index 0000000000000..b163dcc5c056e --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_management_instrumentation_wmi_execution.json @@ -0,0 +1,17 @@ +{ + "rule_id": "cec5eb81-6e01-40e5-a1bf-bf175cce4eb4", + "risk_score": 50, + "description": "Windows Management Instrumentation (WMI) Execution", + "immutable": true, + "interval": "5m", + "name": "Windows Management Instrumentation (WMI) Execution", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and (process.parent.args:*wmiprvse.exe* or process.name:wmic.exe or process.args:*wmic* )", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_microsoft_html_application_hta_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_microsoft_html_application_hta_connecting_to_the_internet.json new file mode 100644 index 0000000000000..647dc53a0d05f --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_microsoft_html_application_hta_connecting_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "b084514b-e8ba-4bc4-bc2b-50fe145a4215", + "risk_score": 50, + "description": "Windows: Microsoft HTML Application (HTA) Connecting to the Internet", + "immutable": true, + "interval": "5m", + "name": "Windows: Microsoft HTML Application (HTA) Connecting to the Internet", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:mshta.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_mimikatz_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_mimikatz_activity.json new file mode 100644 index 0000000000000..a6fa7f8942978 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_mimikatz_activity.json @@ -0,0 +1,43 @@ +{ + "rule_id": "5346463d-062f-419d-88ff-7a5e97875210", + "risk_score": 50, + "description": "Windows Mimikatz activity", + "immutable": true, + "interval": "5m", + "name": "Windows Mimikatz activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:mimikatz.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_misc_lolbin_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_misc_lolbin_connecting_to_the_internet.json new file mode 100644 index 0000000000000..d2bf298557401 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_misc_lolbin_connecting_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "63e65ec3-43b1-45b0-8f2d-45b34291dc44", + "risk_score": 50, + "description": "Windows: Misc LOLBin Connecting to the Internet", + "immutable": true, + "interval": "5m", + "name": "Windows: Misc LOLBin Connecting to the Internet", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(process.name:expand.exe or process.name:extrac.exe or process.name:ieexec.exe or process.name:makecab.exe) and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_activity_by_the_system_account.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_activity_by_the_system_account.json new file mode 100644 index 0000000000000..cc5e4cec1d7bd --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_activity_by_the_system_account.json @@ -0,0 +1,53 @@ +{ + "rule_id": "c3f5dc81-a8b4-4144-95a7-d0a818d7355d", + "risk_score": 50, + "description": "Windows net command activity by the SYSTEM account", + "immutable": true, + "interval": "5m", + "name": "Windows net command activity by the SYSTEM account", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "user.name:SYSTEM", + "language": "kuery", + "filters": [ + { + "meta": { + "type": "phrases", + "key": "process.name", + "value": "net.exe, net1.exe", + "params": [ + "net.exe", + "net1.exe" + ], + "alias": null, + "negate": false, + "disabled": false, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "bool": { + "should": [ + { + "match_phrase": { + "process.name": "net.exe" + } + }, + { + "match_phrase": { + "process.name": "net1.exe" + } + } + ], + "minimum_should_match": 1 + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_user_command_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_user_command_activity.json new file mode 100644 index 0000000000000..182f6a0c0928c --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_user_command_activity.json @@ -0,0 +1,43 @@ +{ + "rule_id": "b039a69d-7fba-4c84-8029-57ac12548a15", + "risk_score": 50, + "description": "Windows net user command activity", + "immutable": true, + "interval": "5m", + "name": "Windows net user command activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:net.exe and process.args:user", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_activity.json new file mode 100644 index 0000000000000..fef425b72281f --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_activity.json @@ -0,0 +1,43 @@ +{ + "rule_id": "e2437364-0c89-4e65-a34b-782cfbb7690b", + "risk_score": 50, + "description": "Windows Netcat activity", + "immutable": true, + "interval": "5m", + "name": "Windows Netcat activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:ncat.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_network_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_network_activity.json new file mode 100644 index 0000000000000..91b094785a9bb --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_network_activity.json @@ -0,0 +1,43 @@ +{ + "rule_id": "ebdc4b6f-7fdb-4c21-bbd6-59e1ed11024a", + "risk_score": 50, + "description": "Windows Netcat network activity", + "immutable": true, + "interval": "5m", + "name": "Windows Netcat network activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:ncat.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "connected-to", + "params": { + "query": "connected-to" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "connected-to", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_network_anomalous_windows_process_using_https_ports.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_network_anomalous_windows_process_using_https_ports.json new file mode 100644 index 0000000000000..c59bc4dfa4135 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_network_anomalous_windows_process_using_https_ports.json @@ -0,0 +1,17 @@ +{ + "rule_id": "b486fa9e-e6c7-44a1-b07d-7d5f07f21ce1", + "risk_score": 50, + "description": "Windows Network - Anomalous Windows Process Using HTTP/S Ports", + "immutable": true, + "interval": "5m", + "name": "Windows Network - Anomalous Windows Process Using HTTP/S Ports", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(destination.port:443 or destination.port:80) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16 and not process.name:chrome.exe and not process.name:explorer.exe and not process.name:filebeat.exe and not process.name:firefox.exe and not process.name:iexplore.exe and not process.name:jusched.exe and not process.name:MpCmdRun.exe and not process.name:MpSigStub.exe and not process.name:msfeedssync.exe and not process.name:packetbeat.exe and not process.name:powershell.exe and not process.name:procexp64.exe and not process.name:svchost.exe and not process.name:taskhostw.exe and not process.name:winlogbeat.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_activity.json new file mode 100644 index 0000000000000..31409e087f8a5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_activity.json @@ -0,0 +1,43 @@ +{ + "rule_id": "5a4b2a98-31a6-4852-b224-d63aeb9e172d", + "risk_score": 50, + "description": "Windows nmap activity", + "immutable": true, + "interval": "5m", + "name": "Windows nmap activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:nmap.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_scan_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_scan_activity.json new file mode 100644 index 0000000000000..580cbe2abcb41 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_scan_activity.json @@ -0,0 +1,43 @@ +{ + "rule_id": "54413985-a3da-4f45-b238-75afb65a1bae", + "risk_score": 50, + "description": "Windows nmap scan activity", + "immutable": true, + "interval": "5m", + "name": "Windows nmap scan activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:nmap.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "alias": null, + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Network connection detected (rule: NetworkConnect)", + "params": { + "query": "Network connection detected (rule: NetworkConnect)" + }, + "disabled": false, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Network connection detected (rule: NetworkConnect)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_payload_obfuscation_via_certutil.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_payload_obfuscation_via_certutil.json new file mode 100644 index 0000000000000..9c76c4273cafc --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_payload_obfuscation_via_certutil.json @@ -0,0 +1,17 @@ +{ + "rule_id": "ce7c270c-c69b-47dd-8c21-60a35e92f372", + "risk_score": 50, + "description": "Windows Payload Obfuscation via Certutil", + "immutable": true, + "interval": "5m", + "name": "Windows Payload Obfuscation via Certutil", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.name:certutil.exe and (process.args:*encode* or process.args:*ToBase64String*)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_or_priv_escalation_via_hooking.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_or_priv_escalation_via_hooking.json new file mode 100644 index 0000000000000..98268e9f4ad66 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_or_priv_escalation_via_hooking.json @@ -0,0 +1,17 @@ +{ + "rule_id": "015f070d-cf70-437c-99d1-472e31d36b03", + "risk_score": 50, + "description": "Windows Persistence or Priv Escalation via Hooking", + "immutable": true, + "interval": "5m", + "name": "Windows Persistence or Priv Escalation via Hooking", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.name:mavinject.exe and processs.args:*INJECTRUNNING*", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_application_shimming.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_application_shimming.json new file mode 100644 index 0000000000000..4db53da43399b --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_application_shimming.json @@ -0,0 +1,17 @@ +{ + "rule_id": "fd4a992d-6130-4802-9ff8-829b89ae801f", + "risk_score": 50, + "description": "Windows Persistence via Application Shimming", + "immutable": true, + "interval": "5m", + "name": "Windows Persistence via Application Shimming", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.name:sdbinst.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_bits_jobs.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_bits_jobs.json new file mode 100644 index 0000000000000..e2560badb7be6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_bits_jobs.json @@ -0,0 +1,17 @@ +{ + "rule_id": "7904fb20-172c-43fb-83e4-bfe27e3c702c", + "risk_score": 50, + "description": "Windows Persistence via BITS Jobs", + "immutable": true, + "interval": "5m", + "name": "Windows Persistence via BITS Jobs", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and (process.name:bitsadmin.exe or process.args:*Start-BitsTransfer*)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_modification_of_existing_service.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_modification_of_existing_service.json new file mode 100644 index 0000000000000..27300362fecf6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_modification_of_existing_service.json @@ -0,0 +1,17 @@ +{ + "rule_id": "3bb04809-84ab-4487-bd99-ccc58675bd40", + "risk_score": 50, + "description": "Windows Persistence via Modification of Existing Service", + "immutable": true, + "interval": "5m", + "name": "Windows Persistence via Modification of Existing Service", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.args:*sc*config*binpath* and (process.name:cmd.exe or process.name:powershell.exe or process.name:sc.exe)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_netshell_helper_dll.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_netshell_helper_dll.json new file mode 100644 index 0000000000000..c0bd446f968c8 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_netshell_helper_dll.json @@ -0,0 +1,17 @@ +{ + "rule_id": "d7c2561d-2758-46ad-b5a9-247efb9eea21", + "risk_score": 50, + "description": "Windows Persistence via Netshell Helper DLL", + "immutable": true, + "interval": "5m", + "name": "Windows Persistence via Netshell Helper DLL", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.name:netsh.exe and process.args:*helper*", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_powershell_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_powershell_connecting_to_the_internet.json new file mode 100644 index 0000000000000..dc3fed37a8c53 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_powershell_connecting_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "a8cfa646-e4d8-48b5-884e-6204ba77fc8d", + "risk_score": 50, + "description": "Windows: Powershell Connecting to the Internet", + "immutable": true, + "interval": "5m", + "name": "Windows: Powershell Connecting to the Internet", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:powershell.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:169.254.169.254/32 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_priv_escalation_via_accessibility_features.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_priv_escalation_via_accessibility_features.json new file mode 100644 index 0000000000000..afeb03150dfcf --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_priv_escalation_via_accessibility_features.json @@ -0,0 +1,17 @@ +{ + "rule_id": "7405ddf1-6c8e-41ce-818f-48bea6bcaed8", + "risk_score": 50, + "description": "Windows Priv Escalation via Accessibility Features", + "immutable": true, + "interval": "5m", + "name": "Windows Priv Escalation via Accessibility Features", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.parent.name:winlogon.exe and (process.name:atbroker.exe or process.name:displayswitch.exe or process.name:magnify.exe or process.name:narrator.exe or process.name:osk.exe or process.name:sethc.exe or process.name:utilman.exe)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_discovery_via_tasklist_command.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_discovery_via_tasklist_command.json new file mode 100644 index 0000000000000..488943dea2949 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_discovery_via_tasklist_command.json @@ -0,0 +1,17 @@ +{ + "rule_id": "cc16f774-59f9-462d-8b98-d27ccd4519ec", + "risk_score": 50, + "description": "Windows Process Discovery via Tasklist Command", + "immutable": true, + "interval": "5m", + "name": "Windows Process Discovery via Tasklist Command", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and process.name:tasklist.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_the_java_runtime.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_the_java_runtime.json new file mode 100644 index 0000000000000..ea246b0264370 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_the_java_runtime.json @@ -0,0 +1,43 @@ +{ + "rule_id": "159168a1-b1d0-4e5c-ad72-c1e9ae2edec2", + "risk_score": 50, + "description": "Windows process started by the Java runtime", + "immutable": true, + "interval": "5m", + "name": "Windows process started by the Java runtime", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.parent.name:javaw.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_register_server_program_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_register_server_program_connecting_to_the_internet.json new file mode 100644 index 0000000000000..cce8effd5d536 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_register_server_program_connecting_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "fb02b8d3-71ee-4af1-bacd-215d23f17efa", + "risk_score": 50, + "description": "Windows: Register Server Program Connecting to the Internet", + "immutable": true, + "interval": "5m", + "name": "Windows: Register Server Program Connecting to the Internet", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(process.name:regsvr32.exe or process.name:regsvr64.exe) and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:169.254.169.254/32 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_local.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_local.json new file mode 100644 index 0000000000000..af9935275267b --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_local.json @@ -0,0 +1,17 @@ +{ + "rule_id": "b9074c74-6d23-4b07-927e-cc18b318a088", + "risk_score": 50, + "description": "Windows Registry Query, Local", + "immutable": true, + "interval": "5m", + "name": "Windows Registry Query, Local", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code: 1 and process.name:reg.exe and process.args:*query* and process.args:*reg*", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_network.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_network.json new file mode 100644 index 0000000000000..4926aabcb8f9d --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_network.json @@ -0,0 +1,17 @@ +{ + "rule_id": "f5412e37-981e-4d37-a1b2-eddaf797445a", + "risk_score": 50, + "description": "Windows Registry Query, Network", + "immutable": true, + "interval": "5m", + "name": "Windows Registry Query, Network", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code: 1 and process.name:reg.exe and process.args:*query* and process.args:*reg*", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_remote_management_execution.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_remote_management_execution.json new file mode 100644 index 0000000000000..d0765ee531bb3 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_remote_management_execution.json @@ -0,0 +1,17 @@ +{ + "rule_id": "ced66221-3e07-40ee-8588-5f107e7d50d8", + "risk_score": 50, + "description": "Windows Remote Management Execution", + "immutable": true, + "interval": "5m", + "name": "Windows Remote Management Execution", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(process.name:wsmprovhost.exe or process.name:winrm.cmd) and (process.args:*Enable-PSRemoting -Force* or process.args:*Invoke-Command -computer_name* or process.args:*wmic*node*process call create*)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_scheduled_task_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_scheduled_task_activity.json new file mode 100644 index 0000000000000..e84d6912793bd --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_scheduled_task_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "a1abd54d-3021-4f21-b2d1-0c6bc5c4051f", + "risk_score": 50, + "description": "Windows Scheduled Task Activity", + "immutable": true, + "interval": "5m", + "name": "Windows Scheduled Task Activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and (process.name:schtasks.exe or process.name:taskeng.exe) or (event.code:1 and process.name:svchost.exe and not process.parent.executable: \"C:\\Windows\\System32\\services.exe\" )", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_script_interpreter_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_script_interpreter_connecting_to_the_internet.json new file mode 100644 index 0000000000000..373d5aa86e6a6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_script_interpreter_connecting_to_the_internet.json @@ -0,0 +1,17 @@ +{ + "rule_id": "2cc4597c-b0c9-4481-b1a6-e6c05cfc9f02", + "risk_score": 50, + "description": "Windows: Script Interpreter Connecting to the Internet", + "immutable": true, + "interval": "5m", + "name": "Windows: Script Interpreter Connecting to the Internet", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "(process.name:cscript.exe or process.name:wscript.exe) and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution.json new file mode 100644 index 0000000000000..a05d37126be3e --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution.json @@ -0,0 +1,17 @@ +{ + "rule_id": "7edb573f-1f9b-4161-8c19-c7c383bb17f2", + "risk_score": 50, + "description": "Windows Signed Binary Proxy Execution", + "immutable": true, + "interval": "5m", + "name": "Windows Signed Binary Proxy Execution", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "event.code:1 and http and (process.name:certutil.exe or process.name:msiexec.exe)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution_download.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution_download.json new file mode 100644 index 0000000000000..931a1f170e5bd --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution_download.json @@ -0,0 +1,17 @@ +{ + "rule_id": "68ecc190-cce2-4021-b976-c7c846ac0a00", + "risk_score": 50, + "description": "Windows Signed Binary Proxy Execution Download", + "immutable": true, + "interval": "5m", + "name": "Windows Signed Binary Proxy Execution Download", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": " event.code:3 and http and (process.name:certutil.exe or process.name:replace.exe)", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_whoami_command_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_whoami_command_activity.json new file mode 100644 index 0000000000000..e4acdcee249bf --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_whoami_command_activity.json @@ -0,0 +1,43 @@ +{ + "rule_id": "ef862985-3f13-4262-a686-5f357bbb9bc2", + "risk_score": 50, + "description": "Windows whoami command activity", + "immutable": true, + "interval": "5m", + "name": "Windows whoami command activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:whoami.exe", + "language": "kuery", + "filters": [ + { + "meta": { + "negate": false, + "type": "phrase", + "key": "event.action", + "value": "Process Create (rule: ProcessCreate)", + "params": { + "query": "Process Create (rule: ProcessCreate)" + }, + "disabled": false, + "alias": null, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + }, + "query": { + "match": { + "event.action": { + "query": "Process Create (rule: ProcessCreate)", + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_wireshark_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_wireshark_activity.json new file mode 100644 index 0000000000000..75dfa58e33318 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_wireshark_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "9af965ed-d501-4541-97f6-5f8d2a39737b", + "risk_score": 50, + "description": "Windows Wireshark activity", + "immutable": true, + "interval": "5m", + "name": "Windows Wireshark activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:wireshark.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windump_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windump_activity.json new file mode 100644 index 0000000000000..db42e194fcf9f --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windump_activity.json @@ -0,0 +1,17 @@ +{ + "rule_id": "61c56cf4-0c08-4ad5-83ea-d2fe6ac62fa8", + "risk_score": 50, + "description": "WinDump activity", + "immutable": true, + "interval": "5m", + "name": "WinDump activity", + "severity": "low", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "process.name:WinDump.exe", + "language": "kuery", + "filters": [], + "enabled": false, + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts new file mode 100644 index 0000000000000..3d2ca8f91281b --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ActionsClient } from '../../../../../actions'; +import { AlertsClient } from '../../../../../alerting'; +import { updateRules } from './update_rules'; +import { RuleAlertParamsRest } from '../types'; + +export const updatePrepackagedRules = async ( + alertsClient: AlertsClient, + actionsClient: ActionsClient, + rules: RuleAlertParamsRest[], + outputIndex: string +): Promise => { + await rules.forEach(async rule => { + const { + description, + false_positives: falsePositives, + from, + immutable, + query, + language, + saved_id: savedId, + meta, + filters, + rule_id: ruleId, + index, + interval, + max_signals: maxSignals, + risk_score: riskScore, + name, + severity, + tags, + to, + type, + threats, + references, + version, + } = rule; + + // Note: we do not pass down enabled as we do not want to suddenly disable + // or enable rules on the user when they were not expecting it if a rule updates + return updateRules({ + alertsClient, + actionsClient, + description, + falsePositives, + from, + immutable, + query, + language, + outputIndex, + id: undefined, // We never have an id when updating from pre-packaged rules + savedId, + meta, + filters, + ruleId, + index, + interval, + maxSignals, + riskScore, + name, + severity, + tags, + to, + type, + threats, + references, + version, + }); + }); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts index 1022fea93200f..c079c637d88b7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { calculateInterval, calculateName } from './update_rules'; +import { calculateInterval, calculateName, calculateVersion } from './update_rules'; describe('update_rules', () => { describe('#calculateInterval', () => { @@ -24,6 +24,24 @@ describe('update_rules', () => { }); }); + describe('#calculateVersion', () => { + test('given preVersion and nextVersion numbers being null it will return a 1', () => { + expect(calculateVersion(null, null)).toEqual(1); + }); + + test('given preVersion and nextVersion numbers being undefined it will return a 1', () => { + expect(calculateVersion(undefined, undefined)).toEqual(1); + }); + + test('given prevVersion as null and nextVersion being defined, nextVersion will be returned', () => { + expect(calculateVersion(undefined, 5)).toEqual(5); + }); + + test('given prevVersion as being defined but nextVersion is not, prevVersion will be incremented by 1', () => { + expect(calculateVersion(5, undefined)).toEqual(6); + }); + }); + describe('#calculateName', () => { test('should return the updated name when it and originalName is there', () => { const name = calculateName({ updatedName: 'updated', originalName: 'original' }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts index 94384155a2dcb..c9dac82b6eb8f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts @@ -8,7 +8,7 @@ import { defaults } from 'lodash/fp'; import { AlertAction, IntervalSchedule } from '../../../../../alerting/server/types'; import { readRules } from './read_rules'; import { UpdateRuleParams } from './types'; -import { updateTags } from './update_tags'; +import { addTags } from './add_tags'; export const calculateInterval = ( interval: string | undefined, @@ -23,6 +23,27 @@ export const calculateInterval = ( } }; +export const calculateVersion = ( + prevVersion: number | null | undefined, + nextVersion: number | null | undefined +) => { + if (nextVersion == null) { + if (prevVersion != null) { + return prevVersion + 1; + } else { + // really should never hit this code but to just be + // safe let us always check the prev version and if + // its null or undefined return a 1 + return 1; + } + } else { + // The user wants to custom update their version number which + // means this could be in the past. Up to the user if they want + // to do this + return nextVersion; + } +}; + export const calculateName = ({ updatedName, originalName, @@ -52,6 +73,7 @@ export const updateRules = async ({ language, outputIndex, savedId, + timelineId, meta, filters, from, @@ -69,6 +91,7 @@ export const updateRules = async ({ to, type, references, + version, }: UpdateRuleParams) => { const rule = await readRules({ alertsClient, ruleId, id }); if (rule == null) { @@ -94,6 +117,7 @@ export const updateRules = async ({ language, outputIndex, savedId, + timelineId, meta, filters, index, @@ -103,19 +127,28 @@ export const updateRules = async ({ threats, to, type, + updatedAt: new Date().toISOString(), references, + version: calculateVersion(rule.params.version, version), } ); - if (rule.enabled && !enabled) { + if (rule.enabled && enabled === false) { await alertsClient.disable({ id: rule.id }); - } else if (!rule.enabled && enabled) { + } else if (!rule.enabled && enabled === true) { await alertsClient.enable({ id: rule.id }); + } else { + // enabled is null or undefined and we do not touch the rule } + return alertsClient.update({ id: rule.id, data: { - tags: updateTags(rule.tags, tags), + tags: addTags( + tags, + rule.params.ruleId, + immutable != null ? immutable : rule.params.immutable // Add new one if it exists, otherwise re-use old one + ), name: calculateName({ updatedName: name, originalName: rule.name }), schedule: { interval: calculateInterval( diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_tags.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_tags.test.ts deleted file mode 100644 index cca937d73bd74..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_tags.test.ts +++ /dev/null @@ -1,35 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { updateTags } from './update_tags'; -import { INTERNAL_IDENTIFIER } from '../../../../common/constants'; - -describe('update_tags', () => { - test('it should copy internal structures but not any other tags when updating', () => { - const tags = updateTags(['tag 2', `${INTERNAL_IDENTIFIER}_some_value`, 'tag 3'], ['tag 1']); - expect(tags).toEqual([`${INTERNAL_IDENTIFIER}_some_value`, 'tag 1']); - }); - - test('it should copy internal structures but not any other tags if given an update of empty tags', () => { - const tags = updateTags(['tag 2', `${INTERNAL_IDENTIFIER}_some_value`, 'tag 3'], []); - expect(tags).toEqual([`${INTERNAL_IDENTIFIER}_some_value`]); - }); - - test('it should work like a normal update if there are no internal structures', () => { - const tags = updateTags(['tag 2', 'tag 3'], ['tag 1']); - expect(tags).toEqual(['tag 1']); - }); - - test('it should not perform an update if the nextTags are undefined', () => { - const tags = updateTags(['tag 2', `${INTERNAL_IDENTIFIER}_some_value`, 'tag 3'], undefined); - expect(tags).toEqual(['tag 2', `${INTERNAL_IDENTIFIER}_some_value`, 'tag 3']); - }); - - test('it should not perform an update if the nextTags are null', () => { - const tags = updateTags(['tag 2', `${INTERNAL_IDENTIFIER}_some_value`, 'tag 3'], null); - expect(tags).toEqual(['tag 2', `${INTERNAL_IDENTIFIER}_some_value`, 'tag 3']); - }); -}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_tags.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_tags.ts deleted file mode 100644 index cf1424ea31600..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_tags.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { INTERNAL_IDENTIFIER } from '../../../../common/constants'; - -export const updateTags = (prevTags: string[], nextTags: string[] | undefined | null): string[] => { - if (nextTags == null) { - return prevTags; - } else { - const allInternalStructures = prevTags.filter(tag => tag.startsWith(INTERNAL_IDENTIFIER)); - return [...allInternalStructures, ...nextTags]; - } -}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/add_prepackaged_rules.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/add_prepackaged_rules.sh new file mode 100755 index 0000000000000..be53e0cd682f0 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/add_prepackaged_rules.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./add_prepackaged_rules.sh +curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PUT ${KIBANA_URL}${SPACE_URL}/api/detection_engine/rules/prepackaged | jq . diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/convert_saved_search_to_rules.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/convert_saved_search_to_rules.sh index e4d345eec0b65..1995b8c7f6978 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/convert_saved_search_to_rules.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/convert_saved_search_to_rules.sh @@ -9,4 +9,6 @@ set -e ./check_env_variables.sh -node ../../../../scripts/convert_saved_search_to_rules.js $1 $2 +OUTPUT=${2:-../rules/prepackaged_rules} + +node ../../../../scripts/convert_saved_search_to_rules.js $1 $OUTPUT diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_rule_by_filter.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_rule_by_filter.sh index 52d49979a072b..6331e72ac7e8e 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_rule_by_filter.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_rule_by_filter.sh @@ -11,11 +11,27 @@ set -e FILTER=${1:-'alert.attributes.enabled:%20true'} -# Example: ./find_rule_by_filter.sh "alert.attributes.enabled:%20true" -# Example: ./find_rule_by_filter.sh "alert.attributes.name:%20Detect*" -# Example: ./find_rule_by_filter.sh "alert.attributes.tags:tag_1" # The %20 is just an encoded space that is typical of URL's. +# The %22 is just an encoded quote of " # Table of them for testing if needed: https://www.w3schools.com/tags/ref_urlencode.asp + +# Example get all enabled tags: +# ./find_rule_by_filter.sh "alert.attributes.enabled:%20true" + +# Example get all the names that start with Detect* +# ./find_rule_by_filter.sh "alert.attributes.name:%20Detect*" + +# Exampe get everything that has tag_1 +# ./find_rule_by_filter.sh "alert.attributes.tags:tag_1" + +# Example get all pre-packaged rules +# ./find_rule_by_filter.sh "alert.attributes.tags:%20%22__internal_immutable:true%22" + +# Example get all non pre-packaged rules +# ./find_rule_by_filter.sh "alert.attributes.tags:%20%22__internal_immutable:false%22" + +# Example get all non pre-packaged rules and a tag_1 +# ./find_rule_by_filter.sh "alert.attributes.tags:%20%22__internal_immutable:false%22%20AND%20alert.attributes.tags:tag_1" curl -s -k \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ -X GET ${KIBANA_URL}${SPACE_URL}/api/detection_engine/rules/_find?filter=$FILTER | jq . diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_timelineid.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_timelineid.json new file mode 100644 index 0000000000000..2f995029447ff --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_timelineid.json @@ -0,0 +1,11 @@ +{ + "name": "Query which is disabled", + "description": "Example query which will is disabled and will not run after being posted", + "risk_score": 1, + "severity": "high", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "user.name: root or user.name: admin", + "timeline_id": "timeline-id" +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json index adb96ba398f5e..60095a0a6a833 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json @@ -76,5 +76,7 @@ "references": [ "http://www.example.com/some-article-about-attack", "Some plain text string here explaining why this is a valid thing to look out for" - ] + ], + "timeline_id": "timeline_id", + "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json index c4aaf4aae93c2..2628b69eb064d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json @@ -77,5 +77,6 @@ "http://www.example.com/some-article-about-attack", "Some plain text string here explaining why this is a valid thing to look out for" ], - "saved_id": "test-saved-id" + "saved_id": "test-saved-id", + "timeline_id": "test-timeline-id" } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_immutable.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_immutable.json new file mode 100644 index 0000000000000..14706b6e54e7b --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_immutable.json @@ -0,0 +1,4 @@ +{ + "rule_id": "query-rule-id", + "immutable": true +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_interval.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_interval.json new file mode 100644 index 0000000000000..72a535f0ef641 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_interval.json @@ -0,0 +1,4 @@ +{ + "rule_id": "query-rule-id", + "interval": "6m" +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json index 285e9c94a76d9..4da285e5b09bf 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json @@ -76,5 +76,7 @@ "references": [ "http://www.example.com/some-article-about-attack", "Some plain text string here explaining why this is a valid thing to look out for" - ] + ], + "timeline_id": "other-timeline-id", + "version": 42 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_timelineid.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_timelineid.json new file mode 100644 index 0000000000000..8cfa3303f54a6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_timelineid.json @@ -0,0 +1,4 @@ +{ + "rule_id": "query-rule-id", + "timeline_id": "other-timeline-id" +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_version.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_version.json new file mode 100644 index 0000000000000..8df63dd22bf9a --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_version.json @@ -0,0 +1,4 @@ +{ + "rule_id": "query-rule-id", + "version": 500 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts index 47a4dafa2d17f..db5b3701a4fc2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -30,8 +30,12 @@ export const sampleRuleAlertParams = ( maxSignals: maxSignals ? maxSignals : 10000, filters: undefined, savedId: undefined, + timelineId: undefined, meta: undefined, threats: undefined, + version: 1, + updatedAt: '2019-12-17T15:04:25.343Z', + createdAt: '2019-12-17T15:04:37.105Z', }); export const sampleDocNoSortId = (someUuid: string = sampleIdGuid): SignalSourceHit => ({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts index e10158a0b879e..90860a817d270 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts @@ -69,6 +69,9 @@ describe('buildBulkBody', () => { enabled: true, created_by: 'elastic', updated_by: 'elastic', + version: 1, + created_at: fakeSignalSourceHit.signal.rule?.created_at, + updated_at: fakeSignalSourceHit.signal.rule?.updated_at, }, }, }); @@ -142,6 +145,9 @@ describe('buildBulkBody', () => { enabled: true, created_by: 'elastic', updated_by: 'elastic', + version: 1, + created_at: fakeSignalSourceHit.signal.rule?.created_at, + updated_at: fakeSignalSourceHit.signal.rule?.updated_at, }, }, }); @@ -213,6 +219,9 @@ describe('buildBulkBody', () => { enabled: true, created_by: 'elastic', updated_by: 'elastic', + version: 1, + created_at: fakeSignalSourceHit.signal.rule?.created_at, + updated_at: fakeSignalSourceHit.signal.rule?.updated_at, }, }, }); @@ -277,6 +286,9 @@ describe('buildBulkBody', () => { enabled: true, created_by: 'elastic', updated_by: 'elastic', + version: 1, + updated_at: fakeSignalSourceHit.signal.rule?.updated_at, + created_at: fakeSignalSourceHit.signal.rule?.created_at, }, }, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts index c12c6fd333f56..451e493f3ed8a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts @@ -59,6 +59,8 @@ describe('buildRule', () => { to: 'now', type: 'query', updated_by: 'elastic', + updated_at: rule.updated_at, + created_at: rule.created_at, filters: [ { query: 'host.name: Rebecca', @@ -70,6 +72,7 @@ describe('buildRule', () => { query: 'host.name: Braden', }, ], + version: 1, }; expect(rule).toEqual(expected); }); @@ -110,6 +113,9 @@ describe('buildRule', () => { to: 'now', type: 'query', updated_by: 'elastic', + version: 1, + updated_at: rule.updated_at, + created_at: rule.created_at, }; expect(rule).toEqual(expected); }); @@ -150,6 +156,9 @@ describe('buildRule', () => { to: 'now', type: 'query', updated_by: 'elastic', + version: 1, + updated_at: rule.updated_at, + created_at: rule.created_at, }; expect(rule).toEqual(expected); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts index 64ec989208b6a..0a3526d32e511 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts @@ -33,6 +33,7 @@ export const buildRule = ({ rule_id: ruleParams.ruleId, false_positives: ruleParams.falsePositives, saved_id: ruleParams.savedId, + timeline_id: ruleParams.timelineId, meta: ruleParams.meta, max_signals: ruleParams.maxSignals, risk_score: ruleParams.riskScore, @@ -55,5 +56,8 @@ export const buildRule = ({ created_by: createdBy, updated_by: updatedBy, threats: ruleParams.threats, + version: ruleParams.version, + created_at: ruleParams.createdAt, + updated_at: ruleParams.updatedAt, }); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts index 1c024d0496743..debc619fbf8b2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts @@ -51,6 +51,8 @@ describe('buildSignal', () => { tags: ['some fake tag 1', 'some fake tag 2'], to: 'now', type: 'query', + updated_at: signal.rule.updated_at, + created_at: signal.rule.created_at, }, }; expect(signal).toEqual(expected); @@ -104,6 +106,8 @@ describe('buildSignal', () => { tags: ['some fake tag 1', 'some fake tag 2'], to: 'now', type: 'query', + updated_at: signal.rule.updated_at, + created_at: signal.rule.created_at, }, }; expect(signal).toEqual(expected); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 37467e405dd8e..87d31abbc5371 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -31,6 +31,7 @@ export const signalRulesAlertType = ({ actionGroups: ['default'], validate: { params: schema.object({ + createdAt: schema.string(), description: schema.string(), falsePositives: schema.arrayOf(schema.string(), { defaultValue: [] }), from: schema.string(), @@ -40,6 +41,7 @@ export const signalRulesAlertType = ({ language: schema.nullable(schema.string()), outputIndex: schema.nullable(schema.string()), savedId: schema.nullable(schema.string()), + timelineId: schema.nullable(schema.string()), meta: schema.nullable(schema.object({}, { allowUnknowns: true })), query: schema.nullable(schema.string()), filters: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), @@ -49,7 +51,9 @@ export const signalRulesAlertType = ({ threats: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), to: schema.string(), type: schema.string(), + updatedAt: schema.string(), references: schema.arrayOf(schema.string(), { defaultValue: [] }), + version: schema.number({ defaultValue: 1 }), }), }, async executor({ alertId, services, params }) { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts index 2d562672a4a63..87739bf785012 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts @@ -28,7 +28,7 @@ describe('read_tags', () => { result2.tags = ['tag 1', 'tag 2', 'tag 3', 'tag 4']; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1, result2])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readRawTags({ @@ -49,7 +49,7 @@ describe('read_tags', () => { result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1, result2])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readRawTags({ @@ -70,7 +70,7 @@ describe('read_tags', () => { result2.tags = []; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1, result2])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readRawTags({ @@ -86,7 +86,7 @@ describe('read_tags', () => { result1.tags = ['tag 1', 'tag 1', 'tag 1', 'tag 2']; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readRawTags({ @@ -102,7 +102,7 @@ describe('read_tags', () => { result1.tags = []; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readRawTags({ @@ -125,7 +125,7 @@ describe('read_tags', () => { result2.tags = ['tag 1', 'tag 2', 'tag 3', 'tag 4']; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1, result2])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readTags({ @@ -146,7 +146,7 @@ describe('read_tags', () => { result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1, result2])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readTags({ @@ -167,7 +167,7 @@ describe('read_tags', () => { result2.tags = []; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1, result2])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readTags({ @@ -183,7 +183,7 @@ describe('read_tags', () => { result1.tags = ['tag 1', 'tag 1', 'tag 1', 'tag 2']; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readTags({ @@ -199,7 +199,7 @@ describe('read_tags', () => { result1.tags = []; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readTags({ @@ -219,7 +219,7 @@ describe('read_tags', () => { ]; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readTags({ @@ -255,7 +255,7 @@ describe('read_tags', () => { ]; const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1])); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const tags = await readTags({ @@ -277,7 +277,7 @@ describe('read_tags', () => { result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; - const findResult = getFindResultWithMultiHits([result1, result2]); + const findResult = getFindResultWithMultiHits({ data: [result1, result2] }); const set = convertTagsToSet(findResult.data); expect(Array.from(set)).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); @@ -300,7 +300,7 @@ describe('read_tags', () => { result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; - const findResult = getFindResultWithMultiHits([result1, result2]); + const findResult = getFindResultWithMultiHits({ data: [result1, result2] }); const tags = convertToTags(findResult.data); expect(tags).toEqual([ 'tag 1', @@ -331,7 +331,7 @@ describe('read_tags', () => { result3.params.ruleId = 'rule-2'; result3.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; - const findResult = getFindResultWithMultiHits([result1, result2, result3]); + const findResult = getFindResultWithMultiHits({ data: [result1, result2, result3] }); const tags = convertToTags(findResult.data); expect(tags).toEqual([ 'tag 1', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts index bb616554042f4..f4a8263da6ba4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts @@ -21,6 +21,7 @@ export interface ThreatParams { } export interface RuleAlertParams { + createdAt: string; description: string; enabled: boolean; falsePositives: string[]; @@ -42,22 +43,36 @@ export interface RuleAlertParams { severity: string; tags: string[]; to: string; + timelineId: string | undefined | null; threats: ThreatParams[] | undefined | null; type: 'query' | 'saved_query'; + version: number; + updatedAt: string; } export type RuleTypeParams = Omit; export type RuleAlertParamsRest = Omit< RuleAlertParams, - 'ruleId' | 'falsePositives' | 'maxSignals' | 'savedId' | 'riskScore' | 'outputIndex' + | 'ruleId' + | 'falsePositives' + | 'maxSignals' + | 'savedId' + | 'riskScore' + | 'timelineId' + | 'outputIndex' + | 'updatedAt' + | 'createdAt' > & { rule_id: RuleAlertParams['ruleId']; false_positives: RuleAlertParams['falsePositives']; saved_id: RuleAlertParams['savedId']; + timeline_id: RuleAlertParams['timelineId']; max_signals: RuleAlertParams['maxSignals']; risk_score: RuleAlertParams['riskScore']; output_index: RuleAlertParams['outputIndex']; + created_at: RuleAlertParams['createdAt']; + updated_at: RuleAlertParams['updatedAt']; }; export type OutputRuleAlertRest = RuleAlertParamsRest & { diff --git a/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/home.test.ts b/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/home.test.ts index effab0fcadf4d..69b5ca9e2b68f 100644 --- a/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/home.test.ts +++ b/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/home.test.ts @@ -21,6 +21,7 @@ import moment from 'moment-timezone'; const { setup } = pageHelpers.home; +jest.mock('ui/new_platform'); jest.mock('ui/i18n', () => { const I18nContext = ({ children }: any) => children; return { I18nContext }; diff --git a/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts b/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts index e14a080137c60..1222697956ffb 100644 --- a/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts +++ b/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts @@ -11,6 +11,8 @@ import { PolicyForm } from '../../public/app/components/policy_form'; import { PolicyFormTestBed } from './helpers/policy_form.helpers'; import { POLICY_EDIT } from './helpers/constant'; +jest.mock('ui/new_platform'); + const { setup } = pageHelpers.policyEdit; const { setup: setupPolicyAdd } = pageHelpers.policyAdd; diff --git a/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts b/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts index df250159c7f69..1f70755d5fd85 100644 --- a/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts +++ b/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts @@ -11,6 +11,8 @@ import { RepositoryType } from '../../common/types'; import { setupEnvironment, pageHelpers, nextTick } from './helpers'; import { RepositoryAddTestBed } from './helpers/repository_add.helpers'; +jest.mock('ui/new_platform'); + const { setup } = pageHelpers.repositoryAdd; const repositoryTypes = ['fs', 'url', 'source', 'azure', 'gcs', 's3', 'hdfs']; diff --git a/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts b/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts index ef74482fc9de7..cca81c0e74285 100644 --- a/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts +++ b/x-pack/legacy/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts @@ -12,6 +12,8 @@ import { RepositoryEditTestSubjects } from './helpers/repository_edit.helpers'; import { RepositoryAddTestSubjects } from './helpers/repository_add.helpers'; import { REPOSITORY_EDIT } from './helpers/constant'; +jest.mock('ui/new_platform'); + const { setup } = pageHelpers.repositoryEdit; const { setup: setupRepositoryAdd } = pageHelpers.repositoryAdd; diff --git a/x-pack/legacy/plugins/uptime/common/constants/capabilities.ts b/x-pack/legacy/plugins/uptime/common/constants/capabilities.ts index 1c9ae58174958..68cc114182afc 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/capabilities.ts +++ b/x-pack/legacy/plugins/uptime/common/constants/capabilities.ts @@ -5,3 +5,6 @@ */ export const INTEGRATED_SOLUTIONS = ['apm', 'infrastructure', 'logs']; + +export const DEFAULT_TIMEPICKER_QUICK_RANGES = 'timepicker:quickRanges'; +export const DEFAULT_DARK_MODE = 'theme:darkMode'; diff --git a/x-pack/legacy/plugins/uptime/common/constants/index.ts b/x-pack/legacy/plugins/uptime/common/constants/index.ts index 40ad1ff1d40b3..e3c4352f0a484 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/index.ts +++ b/x-pack/legacy/plugins/uptime/common/constants/index.ts @@ -8,6 +8,6 @@ export { CHART_FORMAT_LIMITS } from './chart_format_limits'; export { CLIENT_DEFAULTS } from './client_defaults'; export { CONTEXT_DEFAULTS } from './context_defaults'; export { INDEX_NAMES } from './index_names'; -export { INTEGRATED_SOLUTIONS } from './capabilities'; +export * from './capabilities'; export { PLUGIN } from './plugin'; export { QUERY, STATES } from './query'; diff --git a/x-pack/legacy/plugins/uptime/index.ts b/x-pack/legacy/plugins/uptime/index.ts index c8de623cb0a13..e090a2c85e136 100644 --- a/x-pack/legacy/plugins/uptime/index.ts +++ b/x-pack/legacy/plugins/uptime/index.ts @@ -36,7 +36,7 @@ export const uptime = (kibana: any) => init(server: KibanaServer) { const initializerContext = {} as PluginInitializerContext; const { savedObjects } = server; - const { elasticsearch, xpack_main } = server.plugins; + const { xpack_main } = server.plugins; const { usageCollection } = server.newPlatform.setup.plugins; plugin(initializerContext).setup( @@ -44,7 +44,6 @@ export const uptime = (kibana: any) => route: server.newPlatform.setup.core.http.createRouter(), }, { - elasticsearch, savedObjects, usageCollection, xpack: xpack_main, diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap new file mode 100644 index 0000000000000..8a49cac07692e --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap @@ -0,0 +1,287 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UptimeDatePicker component renders properly with mock data 1`] = ` +
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ + + +
+
+`; + +exports[`UptimeDatePicker component renders properly without commonlyUsedRanges prop 1`] = ` +
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ + + +
+
+`; + +exports[`UptimeDatePicker component validates props with shallow render 1`] = ` + +`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/uptime_date_picker.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/uptime_date_picker.test.tsx new file mode 100644 index 0000000000000..93fa0b505a891 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/uptime_date_picker.test.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallowWithIntl, renderWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { UptimeDatePicker, CommonlyUsedRange } from '../uptime_date_picker'; + +describe('UptimeDatePicker component', () => { + let commonlyUsedRange: CommonlyUsedRange[]; + + beforeEach(() => { + commonlyUsedRange = [ + { from: 'now/d', to: 'now/d', display: 'Today' }, + { from: 'now/w', to: 'now/w', display: 'This week' }, + { from: 'now-15m', to: 'now', display: 'Last 15 minutes' }, + { from: 'now-30m', to: 'now', display: 'Last 30 minutes' }, + { from: 'now-1h', to: 'now', display: 'Last 1 hour' }, + { from: 'now-24h', to: 'now', display: 'Last 24 hours' }, + { from: 'now-7d', to: 'now', display: 'Last 7 days' }, + { from: 'now-30d', to: 'now', display: 'Last 30 days' }, + { from: 'now-90d', to: 'now', display: 'Last 90 days' }, + { from: 'now-1y', to: 'now', display: 'Last 2 year' }, + ]; + }); + + it('validates props with shallow render', () => { + const component = shallowWithIntl( + + ); + expect(component).toMatchSnapshot(); + }); + + it('renders properly with mock data', () => { + const component = renderWithIntl( + + ); + expect(component).toMatchSnapshot(); + }); + + it('renders properly without commonlyUsedRanges prop', () => { + const component = renderWithIntl(); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap index 376f20ffcf18a..1f197294ebc15 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap @@ -5,6 +5,7 @@ exports[`MonitorBarSeries component renders a series when there are down items 1 style={ Object { "height": 50, + "maxWidth": "1200px", "width": "100%", } } diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/monitor_bar_series.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/monitor_bar_series.tsx index 1011ddd8e4101..52b41416bd17b 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/monitor_bar_series.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/monitor_bar_series.tsx @@ -17,6 +17,8 @@ import { } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiText, EuiToolTip } from '@elastic/eui'; import { SummaryHistogramPoint } from '../../../../common/graphql/types'; import { getColorsMap } from './get_colors_map'; import { getChartDateLabel, seriesHasDownValues } from '../../../lib/helper'; @@ -54,7 +56,7 @@ export const MonitorBarSeries = ({ const id = getSpecId('downSeries'); return seriesHasDownValues(histogramSeries) ? ( -
+
- ) : null; + ) : ( + down }} + /> + } + > + -- + + ); }; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap index 0c6acb8d9f46e..dc98878f6fe08 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap @@ -26,6 +26,7 @@ exports[`MonitorList component renders a no items message when no data is provid "field": "state.monitor.status", "name": "Status", "render": [Function], + "width": "20%", }, Object { "align": "left", @@ -33,15 +34,10 @@ exports[`MonitorList component renders a no items message when no data is provid "name": "Name", "render": [Function], "sortable": true, + "width": "30%", }, Object { - "align": "left", - "field": "state.url.full", - "name": "URL", - "render": [Function], - "sortable": true, - }, - Object { + "align": "center", "field": "histogram.points", "mobileOptions": Object { "show": false, @@ -51,23 +47,11 @@ exports[`MonitorList component renders a no items message when no data is provid }, Object { "align": "right", - "field": "state", - "hasActions": true, - "id": "actions", - "mobileOptions": Object { - "header": false, - }, - "name": "Integrations", - "render": [Function], - }, - Object { - "align": "left", "field": "monitor_id", "isExpander": true, "name": "", "render": [Function], "sortable": true, - "width": "24px", }, ] } @@ -133,6 +117,7 @@ exports[`MonitorList component renders the monitor list 1`] = ` "field": "state.monitor.status", "name": "Status", "render": [Function], + "width": "20%", }, Object { "align": "left", @@ -140,15 +125,10 @@ exports[`MonitorList component renders the monitor list 1`] = ` "name": "Name", "render": [Function], "sortable": true, + "width": "30%", }, Object { - "align": "left", - "field": "state.url.full", - "name": "URL", - "render": [Function], - "sortable": true, - }, - Object { + "align": "center", "field": "histogram.points", "mobileOptions": Object { "show": false, @@ -158,23 +138,11 @@ exports[`MonitorList component renders the monitor list 1`] = ` }, Object { "align": "right", - "field": "state", - "hasActions": true, - "id": "actions", - "mobileOptions": Object { - "header": false, - }, - "name": "Integrations", - "render": [Function], - }, - Object { - "align": "left", "field": "monitor_id", "isExpander": true, "name": "", "render": [Function], "sortable": true, - "width": "24px", }, ] } diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_pagination.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_pagination.test.tsx.snap index b7c8ddbc51b27..c251e3d8de0ea 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_pagination.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_pagination.test.tsx.snap @@ -26,6 +26,7 @@ exports[`MonitorList component renders a no items message when no data is provid "field": "state.monitor.status", "name": "Status", "render": [Function], + "width": "20%", }, Object { "align": "left", @@ -33,15 +34,10 @@ exports[`MonitorList component renders a no items message when no data is provid "name": "Name", "render": [Function], "sortable": true, + "width": "30%", }, Object { - "align": "left", - "field": "state.url.full", - "name": "URL", - "render": [Function], - "sortable": true, - }, - Object { + "align": "center", "field": "histogram.points", "mobileOptions": Object { "show": false, @@ -51,23 +47,11 @@ exports[`MonitorList component renders a no items message when no data is provid }, Object { "align": "right", - "field": "state", - "hasActions": true, - "id": "actions", - "mobileOptions": Object { - "header": false, - }, - "name": "Integrations", - "render": [Function], - }, - Object { - "align": "left", "field": "monitor_id", "isExpander": true, "name": "", "render": [Function], "sortable": true, - "width": "24px", }, ] } @@ -133,6 +117,7 @@ exports[`MonitorList component renders the monitor list 1`] = ` "field": "state.monitor.status", "name": "Status", "render": [Function], + "width": "20%", }, Object { "align": "left", @@ -140,15 +125,10 @@ exports[`MonitorList component renders the monitor list 1`] = ` "name": "Name", "render": [Function], "sortable": true, + "width": "30%", }, Object { - "align": "left", - "field": "state.url.full", - "name": "URL", - "render": [Function], - "sortable": true, - }, - Object { + "align": "center", "field": "histogram.points", "mobileOptions": Object { "show": false, @@ -158,23 +138,11 @@ exports[`MonitorList component renders the monitor list 1`] = ` }, Object { "align": "right", - "field": "state", - "hasActions": true, - "id": "actions", - "mobileOptions": Object { - "header": false, - }, - "name": "Integrations", - "render": [Function], - }, - Object { - "align": "left", "field": "monitor_id", "isExpander": true, "name": "", "render": [Function], "sortable": true, - "width": "24px", }, ] } diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx index 40a51f7c978e6..f059418324f71 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx @@ -10,13 +10,10 @@ import { EuiPanel, EuiTitle, EuiButtonIcon, - EuiIcon, - EuiLink, EuiFlexItem, EuiSpacer, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; import React, { useState, Fragment } from 'react'; import { withUptimeGraphQL, UptimeGraphQLQueryProps } from '../../higher_order'; @@ -32,8 +29,8 @@ import { ExpandedRowMap } from './types'; import { MonitorListDrawer } from './monitor_list_drawer'; import { MonitorBarSeries } from '../charts'; import { MonitorPageLink } from './monitor_page_link'; -import { MonitorListActionsPopover } from './monitor_list_actions_popover'; import { OverviewPageLink } from './overview_page_link'; +import * as labels from './translations'; interface MonitorListQueryResult { monitorStates?: MonitorSummaryResult; @@ -80,6 +77,68 @@ export const MonitorListComponent = (props: Props) => { }, {}); }; + const columns = [ + { + align: 'left', + width: '20%', + field: 'state.monitor.status', + name: labels.STATUS_COLUMN_LABEL, + render: (status: string, { state: { timestamp } }: MonitorSummary) => { + return ; + }, + }, + { + align: 'left', + width: '30%', + field: 'state.monitor.name', + name: labels.NAME_COLUMN_LABEL, + render: (name: string, summary: MonitorSummary) => ( + + {name ? name : `Unnamed - ${summary.monitor_id}`} + + ), + sortable: true, + }, + { + align: 'center', + field: 'histogram.points', + name: labels.HISTORY_COLUMN_LABEL, + mobileOptions: { + show: false, + }, + render: (histogramSeries: SummaryHistogramPoint[] | null) => ( + + ), + }, + { + align: 'right', + field: 'monitor_id', + name: '', + sortable: true, + isExpander: true, + render: (id: string) => { + return ( + { + if (drawerIds.includes(id)) { + updateDrawerIds(drawerIds.filter(p => p !== id)); + } else { + updateDrawerIds([...drawerIds, id]); + } + }} + /> + ); + }, + }, + ]; + return ( @@ -93,11 +152,7 @@ export const MonitorListComponent = (props: Props) => { { // TODO: not needed without sorting and pagination // onChange={onChange} noItemsMessage={ - hasActiveFilters - ? i18n.translate('xpack.uptime.monitorList.noItemForSelectedFiltersMessage', { - defaultMessage: 'No monitors found for selected filter criteria', - description: - 'This message is show if there are no monitors in the table and some filter or search criteria exists', - }) - : i18n.translate('xpack.uptime.monitorList.noItemMessage', { - defaultMessage: 'No uptime monitors found', - description: - 'This message is shown if the monitors table is rendered but has no items.', - }) + hasActiveFilters ? labels.NO_MONITOR_ITEM_SELECTED : labels.NO_DATA_MESSAGE } // TODO: reintegrate pagination in future release // pagination={pagination} // TODO: reintegrate sorting in future release // sorting={sorting} - columns={[ - { - align: 'left', - field: 'state.monitor.status', - name: i18n.translate('xpack.uptime.monitorList.statusColumnLabel', { - defaultMessage: 'Status', - }), - render: (status: string, { state: { timestamp } }: MonitorSummary) => { - return ; - }, - }, - { - align: 'left', - field: 'state.monitor.name', - name: i18n.translate('xpack.uptime.monitorList.nameColumnLabel', { - defaultMessage: 'Name', - }), - render: (name: string, summary: MonitorSummary) => ( - - {name ? name : `Unnamed - ${summary.monitor_id}`} - - ), - sortable: true, - }, - { - align: 'left', - field: 'state.url.full', - name: i18n.translate('xpack.uptime.monitorList.urlColumnLabel', { - defaultMessage: 'URL', - }), - render: (url: string, summary: MonitorSummary) => ( - - - {url} - - - ), - sortable: true, - }, - { - field: 'histogram.points', - name: i18n.translate('xpack.uptime.monitorList.monitorHistoryColumnLabel', { - defaultMessage: 'Downtime history', - }), - mobileOptions: { - show: false, - }, - render: (histogramSeries: SummaryHistogramPoint[] | null) => ( - - ), - }, - { - id: 'actions', - align: 'right', - field: 'state', - hasActions: true, - mobileOptions: { - header: false, - }, - name: i18n.translate( - 'xpack.uptime.monitorList.observabilityIntegrationsColumnLabel', - { - defaultMessage: 'Integrations', - description: - 'The heading column of some action buttons that will take users to other Observability apps', - } - ), - render: (state: any, summary: MonitorSummary) => ( - - ), - }, - { - align: 'left', - field: 'monitor_id', - name: '', - sortable: true, - width: '24px', - isExpander: true, - render: (id: string) => { - return ( - { - if (drawerIds.includes(id)) { - updateDrawerIds(drawerIds.filter(p => p !== id)); - } else { - updateDrawerIds([...drawerIds, id]); - } - }} - /> - ); - }, - }, - ]} + columns={columns} /> diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_actions_popover.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_actions_popover.tsx index 04f195d42531b..af06761f50c83 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_actions_popover.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_actions_popover.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButtonIcon, EuiPopover } from '@elastic/eui'; +import { EuiPopover, EuiButton } from '@elastic/eui'; import React, { useContext } from 'react'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; @@ -43,7 +43,7 @@ const MonitorListActionsPopoverComponent = ({ return ( togglePopoverIsVisible({ id: popoverId, open: true })} - /> + iconType="arrowDown" + iconSide="right" + > + Integrations + } closePopover={() => togglePopoverIsVisible({ id: popoverId, open: false })} id={popoverId} diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap index 80e064e25e1a5..b6402ae852215 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap @@ -6,21 +6,68 @@ exports[`MonitorListDrawer component renders a MonitorListDrawer when there are - - https://expired.badssl.com - - + + + https://expired.badssl.com + + + + + + - - https://expired.badssl.com - - + + + https://expired.badssl.com + + + + + + - - - , - } - } - /> - - `; @@ -39,7 +22,7 @@ exports[`MonitorStatusList component renders null in place of child status with - - - , - } - } - /> - - `; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap index e2caf6f718728..8e4aa984c1e89 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap @@ -16,7 +16,7 @@ exports[`MonitorStatusRow component renders status row when status is down 1`] = /> `; @@ -37,7 +37,7 @@ exports[`MonitorStatusRow component renders status row when status is up 1`] = ` /> `; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap index 5c1fe27d234a3..389afa27fd21f 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap @@ -2,6 +2,9 @@ exports[`MostRecentError component renders properly with mock data 1`] = ` Array [ +
,
@@ -9,6 +12,9 @@ Array [ Most recent error (5 days ago)
, +
, +
+
+
+ `); + + cleanup(); + + expect(target).toMatchInlineSnapshot(`
`); + }); + + it('should register the nav control once the license supports it', () => { + const license$ = new BehaviorSubject({} as ILicense); + + const navControlService = new SecurityNavControlService(); + navControlService.setup({ + securityLicense: new SecurityLicenseService().setup({ license$ }).license, + }); + + const coreStart = coreMock.createStart(); + navControlService.start({ core: coreStart }); + + expect(coreStart.chrome.navControls.registerRight).not.toHaveBeenCalled(); + + license$.next(validLicense); + + expect(coreStart.chrome.navControls.registerRight).toHaveBeenCalled(); + }); + + it('should not register the nav control for anonymous paths', () => { + const license$ = new BehaviorSubject(validLicense); + + const navControlService = new SecurityNavControlService(); + navControlService.setup({ + securityLicense: new SecurityLicenseService().setup({ license$ }).license, + }); + + const coreStart = coreMock.createStart(); + coreStart.http.anonymousPaths.isAnonymous.mockReturnValue(true); + navControlService.start({ core: coreStart }); + + expect(coreStart.chrome.navControls.registerRight).not.toHaveBeenCalled(); + }); + + it('should only register the nav control once', () => { + const license$ = new BehaviorSubject(validLicense); + + const navControlService = new SecurityNavControlService(); + navControlService.setup({ + securityLicense: new SecurityLicenseService().setup({ license$ }).license, + }); + + const coreStart = coreMock.createStart(); + navControlService.start({ core: coreStart }); + + expect(coreStart.chrome.navControls.registerRight).toHaveBeenCalledTimes(1); + + // trigger license change + license$.next({} as ILicense); + license$.next(validLicense); + + expect(coreStart.chrome.navControls.registerRight).toHaveBeenCalledTimes(1); + }); + + it('should allow for re-registration if the service is restarted', () => { + const license$ = new BehaviorSubject(validLicense); + + const navControlService = new SecurityNavControlService(); + navControlService.setup({ + securityLicense: new SecurityLicenseService().setup({ license$ }).license, + }); + + const coreStart = coreMock.createStart(); + navControlService.start({ core: coreStart }); + + expect(coreStart.chrome.navControls.registerRight).toHaveBeenCalledTimes(1); + + navControlService.stop(); + + navControlService.start({ core: coreStart }); + expect(coreStart.chrome.navControls.registerRight).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx new file mode 100644 index 0000000000000..aeeb84219c937 --- /dev/null +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Subscription } from 'rxjs'; +import { CoreStart } from 'src/core/public'; +import ReactDOM from 'react-dom'; +import React from 'react'; +import { SecurityLicense } from '../../common/licensing'; +import { AuthenticatedUser } from '../../common/model'; +import { SecurityNavControl } from './nav_control_component'; + +interface SetupDeps { + securityLicense: SecurityLicense; +} + +interface StartDeps { + core: CoreStart; +} + +export class SecurityNavControlService { + private securityLicense!: SecurityLicense; + + private navControlRegistered!: boolean; + + private securityFeaturesSubscription?: Subscription; + + public setup({ securityLicense }: SetupDeps) { + this.securityLicense = securityLicense; + } + + public start({ core }: StartDeps) { + this.securityFeaturesSubscription = this.securityLicense.features$.subscribe( + ({ showLinks }) => { + const isAnonymousPath = core.http.anonymousPaths.isAnonymous(window.location.pathname); + + const shouldRegisterNavControl = + !isAnonymousPath && showLinks && !this.navControlRegistered; + + if (shouldRegisterNavControl) { + const user = core.http.get('/internal/security/me', { + headers: { + 'kbn-system-api': true, + }, + }) as Promise; + this.registerSecurityNavControl(core, user); + } + } + ); + } + + public stop() { + if (this.securityFeaturesSubscription) { + this.securityFeaturesSubscription.unsubscribe(); + this.securityFeaturesSubscription = undefined; + } + this.navControlRegistered = false; + } + + private registerSecurityNavControl( + core: Pick, + user: Promise + ) { + core.chrome.navControls.registerRight({ + order: 2000, + mount: (el: HTMLElement) => { + const I18nContext = core.i18n.Context; + + const props = { + user, + editProfileUrl: core.http.basePath.prepend('/app/kibana#/account'), + logoutUrl: core.http.basePath.prepend(`/logout`), + }; + ReactDOM.render( + + + , + el + ); + + return () => ReactDOM.unmountComponentAtNode(el); + }, + }); + + this.navControlRegistered = true; + } +} diff --git a/x-pack/plugins/security/public/plugin.ts b/x-pack/plugins/security/public/plugin.ts index 7b1a554e1d3f1..0f10f9d89f25a 100644 --- a/x-pack/plugins/security/public/plugin.ts +++ b/x-pack/plugins/security/public/plugin.ts @@ -4,18 +4,29 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Plugin, CoreSetup } from 'src/core/public'; +import { Plugin, CoreSetup, CoreStart } from 'src/core/public'; +import { LicensingPluginSetup } from '../../licensing/public'; import { SessionExpired, SessionTimeout, SessionTimeoutHttpInterceptor, UnauthorizedResponseHttpInterceptor, } from './session'; +import { SecurityLicenseService } from '../common/licensing'; +import { SecurityNavControlService } from './nav_control'; + +export interface PluginSetupDependencies { + licensing: LicensingPluginSetup; +} export class SecurityPlugin implements Plugin { private sessionTimeout!: SessionTimeout; - public setup(core: CoreSetup) { + private navControlService!: SecurityNavControlService; + + private securityLicenseService!: SecurityLicenseService; + + public setup(core: CoreSetup, { licensing }: PluginSetupDependencies) { const { http, notifications, injectedMetadata } = core; const { basePath, anonymousPaths } = http; anonymousPaths.register('/login'); @@ -28,18 +39,29 @@ export class SecurityPlugin implements Plugin { let apiKeys: APIKeys; diff --git a/x-pack/plugins/security/server/authentication/api_keys.ts b/x-pack/plugins/security/server/authentication/api_keys.ts index b207e227c56af..2b1a93d907471 100644 --- a/x-pack/plugins/security/server/authentication/api_keys.ts +++ b/x-pack/plugins/security/server/authentication/api_keys.ts @@ -5,7 +5,7 @@ */ import { IClusterClient, KibanaRequest, Logger } from '../../../../../src/core/server'; -import { SecurityLicense } from '../licensing'; +import { SecurityLicense } from '../../common/licensing'; /** * Represents the options to create an APIKey class instance that will be diff --git a/x-pack/plugins/security/server/authentication/index.test.ts b/x-pack/plugins/security/server/authentication/index.test.ts index 6a0057e97dcf0..0c1095b56e6e2 100644 --- a/x-pack/plugins/security/server/authentication/index.test.ts +++ b/x-pack/plugins/security/server/authentication/index.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { licenseMock } from '../licensing/index.mock'; +import { licenseMock } from '../../common/licensing/index.mock'; jest.mock('./api_keys'); jest.mock('./authenticator'); @@ -43,7 +43,7 @@ import { InvalidateAPIKeyResult, InvalidateAPIKeyParams, } from './api_keys'; -import { SecurityLicense } from '../licensing'; +import { SecurityLicense } from '../../common/licensing'; describe('setupAuthentication()', () => { let mockSetupAuthenticationParams: { diff --git a/x-pack/plugins/security/server/authentication/index.ts b/x-pack/plugins/security/server/authentication/index.ts index de2fb54ab8c2a..1002ad7709b80 100644 --- a/x-pack/plugins/security/server/authentication/index.ts +++ b/x-pack/plugins/security/server/authentication/index.ts @@ -16,7 +16,7 @@ import { getErrorStatusCode } from '../errors'; import { Authenticator, ProviderSession } from './authenticator'; import { LegacyAPI } from '../plugin'; import { APIKeys, CreateAPIKeyParams, InvalidateAPIKeyParams } from './api_keys'; -import { SecurityLicense } from '../licensing'; +import { SecurityLicense } from '../../common/licensing'; export { canRedirectRequest } from './can_redirect_request'; export { Authenticator, ProviderLoginAttempt } from './authenticator'; diff --git a/x-pack/plugins/security/server/authorization/index.test.ts b/x-pack/plugins/security/server/authorization/index.test.ts index 34b9efea77165..9e99cae620633 100644 --- a/x-pack/plugins/security/server/authorization/index.test.ts +++ b/x-pack/plugins/security/server/authorization/index.test.ts @@ -24,7 +24,7 @@ import { elasticsearchServiceMock, loggingServiceMock, } from '../../../../../src/core/server/mocks'; -import { licenseMock } from '../licensing/index.mock'; +import { licenseMock } from '../../common/licensing/index.mock'; test(`returns exposed services`, () => { const kibanaIndexName = '.a-kibana-index'; diff --git a/x-pack/plugins/security/server/authorization/index.ts b/x-pack/plugins/security/server/authorization/index.ts index 41e6d12eb8f36..00a50dd5b8821 100644 --- a/x-pack/plugins/security/server/authorization/index.ts +++ b/x-pack/plugins/security/server/authorization/index.ts @@ -31,7 +31,7 @@ import { disableUICapabilitiesFactory } from './disable_ui_capabilities'; import { validateFeaturePrivileges } from './validate_feature_privileges'; import { registerPrivilegesWithCluster } from './register_privileges_with_cluster'; import { APPLICATION_PREFIX } from '../../common/constants'; -import { SecurityLicense } from '../licensing'; +import { SecurityLicense } from '../../common/licensing'; export { Actions } from './actions'; export { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges'; diff --git a/x-pack/plugins/security/server/authorization/mode.test.ts b/x-pack/plugins/security/server/authorization/mode.test.ts index 3f6aa1f68ff0d..7d7d2108c049b 100644 --- a/x-pack/plugins/security/server/authorization/mode.test.ts +++ b/x-pack/plugins/security/server/authorization/mode.test.ts @@ -7,9 +7,9 @@ import { authorizationModeFactory } from './mode'; import { httpServerMock } from '../../../../../src/core/server/mocks'; -import { licenseMock } from '../licensing/index.mock'; -import { SecurityLicenseFeatures } from '../licensing/license_features'; -import { SecurityLicense } from '../licensing'; +import { licenseMock } from '../../common/licensing/index.mock'; +import { SecurityLicenseFeatures } from '../../common/licensing/license_features'; +import { SecurityLicense } from '../../common/licensing'; describe(`#useRbacForRequest`, () => { let mockLicense: jest.Mocked; diff --git a/x-pack/plugins/security/server/authorization/mode.ts b/x-pack/plugins/security/server/authorization/mode.ts index 43ac8f43436fd..6a56c6cfc260a 100644 --- a/x-pack/plugins/security/server/authorization/mode.ts +++ b/x-pack/plugins/security/server/authorization/mode.ts @@ -5,7 +5,7 @@ */ import { KibanaRequest } from '../../../../../src/core/server'; -import { SecurityLicense } from '../licensing'; +import { SecurityLicense } from '../../common/licensing'; export interface AuthorizationMode { useRbacForRequest(request: KibanaRequest): boolean; diff --git a/x-pack/plugins/security/server/licensing/license_service.ts b/x-pack/plugins/security/server/licensing/license_service.ts deleted file mode 100644 index e9e2791efbd17..0000000000000 --- a/x-pack/plugins/security/server/licensing/license_service.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { deepFreeze } from '../../../../../src/core/utils'; -import { ILicense } from '../../../licensing/server'; -import { SecurityLicenseFeatures } from './license_features'; - -export interface SecurityLicense { - isEnabled(): boolean; - getFeatures(): SecurityLicenseFeatures; -} - -export class SecurityLicenseService { - public setup() { - let rawLicense: Readonly | undefined; - - return { - update(newRawLicense: Readonly) { - rawLicense = newRawLicense; - }, - - license: deepFreeze({ - isEnabled() { - if (!rawLicense) { - return false; - } - - const securityFeature = rawLicense.getFeature('security'); - return ( - securityFeature !== undefined && - securityFeature.isAvailable && - securityFeature.isEnabled - ); - }, - - /** - * Returns up-do-date Security related features based on the last known license. - */ - getFeatures(): SecurityLicenseFeatures { - // If, for some reason, we cannot get license information from Elasticsearch, - // assume worst-case and lock user at login screen. - if (rawLicense === undefined || !rawLicense.isAvailable) { - return { - showLogin: true, - allowLogin: false, - showLinks: false, - allowRoleDocumentLevelSecurity: false, - allowRoleFieldLevelSecurity: false, - allowRbac: false, - layout: - rawLicense !== undefined && !rawLicense.isAvailable - ? 'error-xpack-unavailable' - : 'error-es-unavailable', - }; - } - - if (!this.isEnabled()) { - return { - showLogin: false, - allowLogin: false, - showLinks: false, - allowRoleDocumentLevelSecurity: false, - allowRoleFieldLevelSecurity: false, - allowRbac: false, - linksMessage: 'Access is denied because Security is disabled in Elasticsearch.', - }; - } - - const isLicensePlatinumOrBetter = rawLicense.isOneOf(['platinum', 'enterprise', 'trial']); - return { - showLogin: true, - allowLogin: true, - showLinks: true, - // Only platinum and trial licenses are compliant with field- and document-level security. - allowRoleDocumentLevelSecurity: isLicensePlatinumOrBetter, - allowRoleFieldLevelSecurity: isLicensePlatinumOrBetter, - allowRbac: true, - }; - }, - }), - }; - } -} diff --git a/x-pack/plugins/security/server/plugin.test.ts b/x-pack/plugins/security/server/plugin.test.ts index cce928976accc..05d67b112bad8 100644 --- a/x-pack/plugins/security/server/plugin.test.ts +++ b/x-pack/plugins/security/server/plugin.test.ts @@ -54,6 +54,17 @@ describe('Security Plugin', () => { "secureCookies": true, }, "license": Object { + "features$": Observable { + "_isScalar": false, + "operator": MapOperator { + "project": [Function], + "thisArg": undefined, + }, + "source": Observable { + "_isScalar": false, + "_subscribe": [Function], + }, + }, "getFeatures": [Function], "isEnabled": [Function], }, diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index df5be97e7c393..14dd1e6ac00d3 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Subscription, combineLatest } from 'rxjs'; +import { combineLatest } from 'rxjs'; import { first } from 'rxjs/operators'; import { IClusterClient, @@ -25,7 +25,7 @@ import { Authentication, setupAuthentication } from './authentication'; import { Authorization, setupAuthorization } from './authorization'; import { createConfig$ } from './config'; import { defineRoutes } from './routes'; -import { SecurityLicenseService, SecurityLicense } from './licensing'; +import { SecurityLicenseService, SecurityLicense } from '../common/licensing'; import { setupSavedObjects } from './saved_objects'; import { SecurityAuditLogger } from './audit'; import { elasticsearchClientPlugin } from './elasticsearch_client_plugin'; @@ -90,7 +90,7 @@ export class Plugin { private readonly logger: Logger; private clusterClient?: IClusterClient; private spacesService?: SpacesService | symbol = Symbol('not accessed'); - private licenseSubscription?: Subscription; + private securityLicenseService?: SecurityLicenseService; private legacyAPI?: LegacyAPI; private readonly getLegacyAPI = () => { @@ -128,10 +128,10 @@ export class Plugin { plugins: [elasticsearchClientPlugin], }); - const { license, update: updateLicense } = new SecurityLicenseService().setup(); - this.licenseSubscription = licensing.license$.subscribe(rawLicense => - updateLicense(rawLicense) - ); + this.securityLicenseService = new SecurityLicenseService(); + const { license } = this.securityLicenseService.setup({ + license$: licensing.license$, + }); const authc = await setupAuthentication({ http: core.http, @@ -223,9 +223,9 @@ export class Plugin { this.clusterClient = undefined; } - if (this.licenseSubscription) { - this.licenseSubscription.unsubscribe(); - this.licenseSubscription = undefined; + if (this.securityLicenseService) { + this.securityLicenseService.stop(); + this.securityLicenseService = undefined; } } diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts index f01a28847bbdf..c9ac114f3808a 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts @@ -176,6 +176,7 @@ describe('copySavedObjectsToSpaces', () => { "_maxListeners": undefined, "_read": [Function], "_readableState": ReadableState { + "autoDestroy": false, "awaitDrain": 0, "buffer": BufferList { "head": null, @@ -228,6 +229,7 @@ describe('copySavedObjectsToSpaces', () => { "_maxListeners": undefined, "_read": [Function], "_readableState": ReadableState { + "autoDestroy": false, "awaitDrain": 0, "buffer": BufferList { "head": null, diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts index 97b7480ea4af8..a1db5032ba73b 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts @@ -195,6 +195,7 @@ describe('resolveCopySavedObjectsToSpacesConflicts', () => { "_maxListeners": undefined, "_read": [Function], "_readableState": ReadableState { + "autoDestroy": false, "awaitDrain": 0, "buffer": BufferList { "head": null, @@ -254,6 +255,7 @@ describe('resolveCopySavedObjectsToSpacesConflicts', () => { "_maxListeners": undefined, "_read": [Function], "_readableState": ReadableState { + "autoDestroy": false, "awaitDrain": 0, "buffer": BufferList { "head": null, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b7036399f3f71..d2373261fb1f6 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -461,8 +461,8 @@ "common.ui.legacyBrowserMessage": "この Kibana インストレーションは、現在ご使用のブラウザが満たしていない厳格なセキュリティ要件が有効になっています。", "common.ui.legacyBrowserTitle": "ブラウザをアップグレードしてください", "common.ui.management.breadcrumb": "管理", - "common.ui.management.connectDataDisplayName": "データに接続", - "common.ui.management.displayName": "管理", + "management.connectDataDisplayName": "データに接続", + "management.displayName": "管理", "common.ui.management.nav.menu": "管理メニュー", "common.ui.modals.cancelButtonLabel": "キャンセル", "common.ui.notify.fatalError.errorStatusMessage": "エラー {errStatus} {errStatusText}: {errMessage}", @@ -5838,7 +5838,6 @@ "xpack.infra.homePage.noMetricsIndicesInstructionsActionLabel": "セットアップの手順を表示", "xpack.infra.homePage.noMetricsIndicesTitle": "メトリックインデックスがないようです。", "xpack.infra.homePage.toolbar.kqlSearchFieldPlaceholder": "インフラストラクチャーデータを検索… (例: host.name:host-1)", - "xpack.infra.homePage.toolbar.showingLastOneMinuteDataText": "指定期間の最後の 1 分間のデータを表示中", "xpack.infra.infrastructureDescription": "インフラストラクチャーを閲覧します", "xpack.infra.infrastructureMetricsExplorerPage.documentTitle": "{previousTitle} | メトリックエクスプローラー", "xpack.infra.infrastructureSnapshotPage.documentTitle": "{previousTitle} | インベントリ", @@ -6078,13 +6077,9 @@ "xpack.infra.logs.analysis.logRateSectionNoDataTitle": "表示するデータがありません。", "xpack.infra.logs.analysis.logRateSectionTitle": "ログレート", "xpack.infra.logs.analysisPage.loadingMessage": "分析ジョブのステータスを確認中…", - "xpack.infra.logs.analysisPage.unavailable.mlAppButton": "機械学習を開く", "xpack.infra.logs.analysisPage.unavailable.mlAppLink": "機械学習アプリ", - "xpack.infra.logs.analysisPage.unavailable.mlDisabledBody": "詳細は {machineLearningAppLink} をご覧ください。", - "xpack.infra.logs.analysisPage.unavailable.mLDisabledTitle": "分析機能には機械学習が必要です", "xpack.infra.logs.highlights.goToNextHighlightButtonLabel": "次のハイライトにスキップ", "xpack.infra.logs.highlights.goToPreviousHighlightButtonLabel": "前のハイライトにスキップ", - "xpack.infra.logs.index.analysisBetaBadgeTitle": "分析", "xpack.infra.logs.index.settingsTabTitle": "設定", "xpack.infra.logs.index.streamTabTitle": "ストリーム", "xpack.infra.logs.logsAnalysisResults.onboardingSuccessContent": "機械学習ロボットがデータの収集を開始するまでしばらくお待ちください。", @@ -6175,8 +6170,6 @@ "xpack.infra.logs.analysis.overallAnomalyChartMaxScoresLabel": "最高異常スコア", "xpack.infra.logs.analysis.partitionMaxAnomalyScoreAnnotationLabel": "最高異常スコア: {maxAnomalyScore}", "xpack.infra.logs.analysis.recreateJobButtonLabel": "ML ジョブを再作成", - "xpack.infra.logs.analysisPage.setupStatusUnknown.title": "ML ジョブのステータスを特定できませんでした。", - "xpack.infra.logs.analysisPage.setupStatusUnknown.tryAgainButton": "再試行", "xpack.infra.logs.jumpToTailText": "最も新しいエントリーに移動", "xpack.infra.metricsExplorer.chartOptions.barLabel": "バー", "xpack.infra.nodeDetails.labels.availabilityZone": "アベイラビリティゾーン", @@ -11792,11 +11785,9 @@ "xpack.uptime.monitorList.monitoringStatusTitle": "監視ステータス", "xpack.uptime.monitorList.observabilityIntegrationsColumn.apmIntegrationLink.tooltip": "ここをクリックしてAPM でドメイン「{domain}」を確認します。", "xpack.uptime.monitorList.observabilityIntegrationsColumn.popoverIconButton.ariaLabel": "URL {monitorUrl}で監査のための移行ポップオーバーを開く", - "xpack.uptime.monitorList.observabilityIntegrationsColumnLabel": "統合", "xpack.uptime.monitorList.statusColumn.downLabel": "ダウン", "xpack.uptime.monitorList.statusColumn.upLabel": "アップ", "xpack.uptime.monitorList.statusColumnLabel": "ステータス", - "xpack.uptime.monitorList.urlColumnLabel": "URL", "xpack.uptime.monitorStatusBar.durationTextAriaLabel": "ミリ秒単位の監視時間", "xpack.uptime.monitorStatusBar.healthStatus.durationInMillisecondsMessage": "{duration}ms", "xpack.uptime.monitorStatusBar.healthStatusMessage.downLabel": "ダウン", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index af4aa1f0c7bbe..d59d50a8de1b5 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -461,8 +461,8 @@ "common.ui.legacyBrowserMessage": "此 Kibana 安装启用了当前浏览器未满足的严格安全要求。", "common.ui.legacyBrowserTitle": "请升级您的浏览器", "common.ui.management.breadcrumb": "管理", - "common.ui.management.connectDataDisplayName": "连接数据", - "common.ui.management.displayName": "管理", + "management.connectDataDisplayName": "连接数据", + "management.displayName": "管理", "common.ui.management.nav.menu": "管理菜单", "common.ui.modals.cancelButtonLabel": "取消", "common.ui.notify.fatalError.errorStatusMessage": "错误 {errStatus} {errStatusText}:{errMessage}", @@ -5840,7 +5840,6 @@ "xpack.infra.homePage.noMetricsIndicesInstructionsActionLabel": "查看设置说明", "xpack.infra.homePage.noMetricsIndicesTitle": "似乎您没有任何指标索引。", "xpack.infra.homePage.toolbar.kqlSearchFieldPlaceholder": "搜索基础设施数据……(例如 host.name:host-1)", - "xpack.infra.homePage.toolbar.showingLastOneMinuteDataText": "在选定时间显示过去 1 分钟的数据", "xpack.infra.infrastructureDescription": "浏览您的基础设施", "xpack.infra.infrastructureMetricsExplorerPage.documentTitle": "{previousTitle} | 指标浏览器", "xpack.infra.infrastructureSnapshotPage.documentTitle": "{previousTitle} | 库存", @@ -6080,13 +6079,9 @@ "xpack.infra.logs.analysis.logRateSectionNoDataTitle": "没有可显示的数据。", "xpack.infra.logs.analysis.logRateSectionTitle": "日志速率", "xpack.infra.logs.analysisPage.loadingMessage": "正在检查分析作业的状态......", - "xpack.infra.logs.analysisPage.unavailable.mlAppButton": "打开 Machine Learning", "xpack.infra.logs.analysisPage.unavailable.mlAppLink": "Machine Learning 应用", - "xpack.infra.logs.analysisPage.unavailable.mlDisabledBody": "查看 {machineLearningAppLink}以了解更多信息。", - "xpack.infra.logs.analysisPage.unavailable.mLDisabledTitle": "分析功能需要 Machine Learning", "xpack.infra.logs.highlights.goToNextHighlightButtonLabel": "跳转到下一高亮条目", "xpack.infra.logs.highlights.goToPreviousHighlightButtonLabel": "跳转到上一高亮条目", - "xpack.infra.logs.index.analysisBetaBadgeTitle": "分析", "xpack.infra.logs.index.settingsTabTitle": "设置", "xpack.infra.logs.index.streamTabTitle": "流式传输", "xpack.infra.logs.logsAnalysisResults.onboardingSuccessContent": "请注意,我们的 Machine Learning 机器人若干分钟后才会开始收集数据。", @@ -6177,8 +6172,6 @@ "xpack.infra.logs.analysis.overallAnomalyChartMaxScoresLabel": "最大异常分数:", "xpack.infra.logs.analysis.partitionMaxAnomalyScoreAnnotationLabel": "最大异常分数:{maxAnomalyScore}", "xpack.infra.logs.analysis.recreateJobButtonLabel": "重新创建 ML 作业", - "xpack.infra.logs.analysisPage.setupStatusUnknown.title": "我们无法确定您的 ML 作业的状态。", - "xpack.infra.logs.analysisPage.setupStatusUnknown.tryAgainButton": "请重试", "xpack.infra.logs.jumpToTailText": "跳到最近的条目", "xpack.infra.metricsExplorer.chartOptions.barLabel": "条形图", "xpack.infra.nodeDetails.labels.availabilityZone": "可用区", @@ -11881,11 +11874,9 @@ "xpack.uptime.monitorList.monitoringStatusTitle": "检测状态", "xpack.uptime.monitorList.observabilityIntegrationsColumn.apmIntegrationLink.tooltip": "点击此处以在 APM 中查找“{domain}”。", "xpack.uptime.monitorList.observabilityIntegrationsColumn.popoverIconButton.ariaLabel": "打开 url {monitorUrl} 的监测的集成弹出式窗口", - "xpack.uptime.monitorList.observabilityIntegrationsColumnLabel": "集成", "xpack.uptime.monitorList.statusColumn.downLabel": "关闭", "xpack.uptime.monitorList.statusColumn.upLabel": "运行", "xpack.uptime.monitorList.statusColumnLabel": "状态", - "xpack.uptime.monitorList.urlColumnLabel": "URL", "xpack.uptime.monitorStatusBar.durationTextAriaLabel": "监测持续时间(毫秒)", "xpack.uptime.monitorStatusBar.healthStatus.durationInMillisecondsMessage": "{duration}ms", "xpack.uptime.monitorStatusBar.healthStatusMessage.downLabel": "关闭", @@ -12810,4 +12801,4 @@ "xpack.licensing.welcomeBanner.licenseIsExpiredDescription.updateYourLicenseLinkText": "更新您的许可", "xpack.licensing.welcomeBanner.licenseIsExpiredTitle": "您的{licenseType}许可已过期" } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/infra/log_summary.ts b/x-pack/test/api_integration/apis/infra/log_summary.ts index da8c89f674202..017b1c6f7ab45 100644 --- a/x-pack/test/api_integration/apis/infra/log_summary.ts +++ b/x-pack/test/api_integration/apis/infra/log_summary.ts @@ -5,76 +5,72 @@ */ import expect from '@kbn/expect'; + import { pairs } from 'd3-array'; -import gql from 'graphql-tag'; + +import { pipe } from 'fp-ts/lib/pipeable'; +import { identity } from 'fp-ts/lib/function'; +import { fold } from 'fp-ts/lib/Either'; + +import { + createPlainError, + throwErrors, +} from '../../../../legacy/plugins/infra/common/runtime_types'; + +import { + LOG_ENTRIES_SUMMARY_PATH, + logEntriesSummaryRequestRT, + logEntriesSummaryResponseRT, +} from '../../../../legacy/plugins/infra/common/http_api/log_entries'; import { FtrProviderContext } from '../../ftr_provider_context'; const EARLIEST_TIME_WITH_DATA = new Date('2018-10-17T19:42:22.000Z').valueOf(); const LATEST_TIME_WITH_DATA = new Date('2018-10-17T19:57:21.611Z').valueOf(); -const logSummaryBetweenQuery = gql` - query LogSummary( - $sourceId: ID = "default" - $start: Float! - $end: Float! - $bucketSize: Float! - $filterQuery: String - ) { - source(id: $sourceId) { - id - logSummaryBetween( - start: $start - end: $end - bucketSize: $bucketSize - filterQuery: $filterQuery - ) { - start - end - buckets { - start - end - entriesCount - } - } - } - } -`; +const COMMON_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', +}; export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('infraOpsGraphQLClient'); + const supertest = getService('supertest'); describe('logSummaryBetween', () => { before(() => esArchiver.load('infra/metrics_and_logs')); after(() => esArchiver.unload('infra/metrics_and_logs')); it('should return empty and non-empty consecutive buckets', async () => { - const start = EARLIEST_TIME_WITH_DATA; - const end = LATEST_TIME_WITH_DATA + (LATEST_TIME_WITH_DATA - EARLIEST_TIME_WITH_DATA); - const bucketSize = Math.ceil((end - start) / 10); + const startDate = EARLIEST_TIME_WITH_DATA; + const endDate = LATEST_TIME_WITH_DATA + (LATEST_TIME_WITH_DATA - EARLIEST_TIME_WITH_DATA); + const bucketSize = Math.ceil((endDate - startDate) / 10); + + const { body } = await supertest + .post(LOG_ENTRIES_SUMMARY_PATH) + .set(COMMON_HEADERS) + .send( + logEntriesSummaryRequestRT.encode({ + sourceId: 'default', + startDate, + endDate, + bucketSize, + query: null, + }) + ) + .expect(200); - const { - data: { - source: { logSummaryBetween }, - }, - } = await client.query({ - query: logSummaryBetweenQuery, - variables: { - start, - end, - bucketSize, - }, - }); + const logSummaryResponse = pipe( + logEntriesSummaryResponseRT.decode(body), + fold(throwErrors(createPlainError), identity) + ); - expect(logSummaryBetween).to.have.property('buckets'); - expect(logSummaryBetween.buckets).to.have.length(10); + expect(logSummaryResponse.data.buckets).to.have.length(10); expect( - logSummaryBetween.buckets.filter((bucket: any) => bucket.entriesCount > 0) + logSummaryResponse.data.buckets.filter((bucket: any) => bucket.entriesCount > 0) ).to.have.length(5); expect( pairs( - logSummaryBetween.buckets, + logSummaryResponse.data.buckets, (first: any, second: any) => first.end === second.start ).every(pair => pair) ).to.equal(true); diff --git a/x-pack/test/api_integration/apis/infra/logs_without_millis.ts b/x-pack/test/api_integration/apis/infra/logs_without_millis.ts index 9cddca4c08a14..ce2319259985b 100644 --- a/x-pack/test/api_integration/apis/infra/logs_without_millis.ts +++ b/x-pack/test/api_integration/apis/infra/logs_without_millis.ts @@ -8,10 +8,27 @@ import expect from '@kbn/expect'; import { ascending, pairs } from 'd3-array'; import gql from 'graphql-tag'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { identity } from 'fp-ts/lib/function'; +import { fold } from 'fp-ts/lib/Either'; + +import { + createPlainError, + throwErrors, +} from '../../../../legacy/plugins/infra/common/runtime_types'; + import { FtrProviderContext } from '../../ftr_provider_context'; import { sharedFragments } from '../../../../legacy/plugins/infra/common/graphql/shared'; import { InfraTimeKey } from '../../../../legacy/plugins/infra/public/graphql/types'; - +import { + LOG_ENTRIES_SUMMARY_PATH, + logEntriesSummaryRequestRT, + logEntriesSummaryResponseRT, +} from '../../../../legacy/plugins/infra/common/http_api/log_entries'; + +const COMMON_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', +}; const KEY_WITHIN_DATA_RANGE = { time: new Date('2019-01-06T00:00:00.000Z').valueOf(), tiebreaker: 0, @@ -28,6 +45,7 @@ const LATEST_KEY_WITH_DATA = { export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const client = getService('infraOpsGraphQLClient'); + const supertest = getService('supertest'); describe('logs without epoch_millis format', () => { before(() => esArchiver.load('infra/logs_without_epoch_millis')); @@ -74,26 +92,31 @@ export default function({ getService }: FtrProviderContext) { }); it('logSummaryBetween should return non-empty buckets', async () => { - const start = EARLIEST_KEY_WITH_DATA.time; - const end = LATEST_KEY_WITH_DATA.time + 1; // the interval end is exclusive - const bucketSize = Math.ceil((end - start) / 10); - - const { - data: { - source: { logSummaryBetween }, - }, - } = await client.query({ - query: logSummaryBetweenQuery, - variables: { - start, - end, - bucketSize, - }, - }); + const startDate = EARLIEST_KEY_WITH_DATA.time; + const endDate = LATEST_KEY_WITH_DATA.time + 1; // the interval end is exclusive + const bucketSize = Math.ceil((endDate - startDate) / 10); + + const { body } = await supertest + .post(LOG_ENTRIES_SUMMARY_PATH) + .set(COMMON_HEADERS) + .send( + logEntriesSummaryRequestRT.encode({ + sourceId: 'default', + startDate, + endDate, + bucketSize, + query: null, + }) + ) + .expect(200); + + const logSummaryResponse = pipe( + logEntriesSummaryResponseRT.decode(body), + fold(throwErrors(createPlainError), identity) + ); - expect(logSummaryBetween).to.have.property('buckets'); expect( - logSummaryBetween.buckets.filter((bucket: any) => bucket.entriesCount > 0) + logSummaryResponse.data.buckets.filter((bucket: any) => bucket.entriesCount > 0) ).to.have.length(2); }); }); @@ -161,34 +184,6 @@ const logEntriesBetweenQuery = gql` ${sharedFragments.InfraLogEntryFields} `; -const logSummaryBetweenQuery = gql` - query LogSummary( - $sourceId: ID = "default" - $start: Float! - $end: Float! - $bucketSize: Float! - $filterQuery: String - ) { - source(id: $sourceId) { - id - logSummaryBetween( - start: $start - end: $end - bucketSize: $bucketSize - filterQuery: $filterQuery - ) { - start - end - buckets { - start - end - entriesCount - } - } - } - } -`; - const isSorted = (comparator: (first: Value, second: Value) => number) => ( values: Value[] ) => pairs(values, comparator).every(order => order <= 0); diff --git a/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/overview.json b/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/overview.json index be87c3458453c..49e80b244f760 100644 --- a/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/overview.json +++ b/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/overview.json @@ -69,7 +69,8 @@ "nodeExists": null, "indexExists": null, "typeExists": false, - "typeExistsAtAnyTime": false + "typeExistsAtAnyTime": false, + "usingStructuredLogs": false }, "types": [] } diff --git a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail.json b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail.json index 7aee56e697d9e..04d56d5949d2c 100644 --- a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail.json +++ b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/index_detail.json @@ -17,6 +17,7 @@ "indexPatternExists": false, "indexPatternInTimeRangeExists": false, "typeExistsAtAnyTime": false, + "usingStructuredLogs": false, "nodeExists": null, "indexExists": false, "typeExists": false diff --git a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail.json b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail.json index c2ee3a818fe14..0b8d26558e7fc 100644 --- a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail.json +++ b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/node_detail.json @@ -18,6 +18,7 @@ "indexPatternExists": false, "indexPatternInTimeRangeExists": false, "typeExistsAtAnyTime": false, + "usingStructuredLogs": false, "nodeExists": false, "indexExists": null, "typeExists": false diff --git a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_green_platinum.json b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_green_platinum.json index 8f9b427d4466b..9dd55cdd27ede 100644 --- a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_green_platinum.json +++ b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_green_platinum.json @@ -5757,6 +5757,7 @@ "indexPatternExists": false, "indexPatternInTimeRangeExists": false, "typeExistsAtAnyTime": false, + "usingStructuredLogs": false, "nodeExists": null, "indexExists": null, "typeExists": false diff --git a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_red_platinum.json b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_red_platinum.json index 8a9405abda817..45f2a7e6581f9 100644 --- a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_red_platinum.json +++ b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_red_platinum.json @@ -22,6 +22,7 @@ "indexPatternExists": false, "indexPatternInTimeRangeExists": false, "typeExistsAtAnyTime": false, + "usingStructuredLogs": false, "nodeExists": null, "indexExists": null, "typeExists": false diff --git a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_shards_relocating.json b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_shards_relocating.json index 9c340ffb4cd50..415ef5c589c1f 100644 --- a/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_shards_relocating.json +++ b/x-pack/test/api_integration/apis/monitoring/elasticsearch/fixtures/overview_shards_relocating.json @@ -22,6 +22,7 @@ "indexPatternExists": false, "indexPatternInTimeRangeExists": false, "typeExistsAtAnyTime": false, + "usingStructuredLogs": false, "nodeExists": null, "indexExists": null, "typeExists": false diff --git a/x-pack/test/api_integration/apis/monitoring/logs/fixtures/multiple_clusters.json b/x-pack/test/api_integration/apis/monitoring/logs/fixtures/multiple_clusters.json index 37afc6acec076..442f9cb38ae33 100644 --- a/x-pack/test/api_integration/apis/monitoring/logs/fixtures/multiple_clusters.json +++ b/x-pack/test/api_integration/apis/monitoring/logs/fixtures/multiple_clusters.json @@ -5,6 +5,7 @@ "indexPatternExists": true, "indexPatternInTimeRangeExists": true, "typeExistsAtAnyTime": true, + "usingStructuredLogs": true, "typeExists": true, "clusterExists": false, "nodeExists": null, diff --git a/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/cluster.json b/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/cluster.json index ae61d289fe338..802bd0c7fcd74 100644 --- a/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/cluster.json +++ b/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/cluster.json @@ -16,6 +16,7 @@ "indexPatternExists": false, "indexPatternInTimeRangeExists": false, "typeExistsAtAnyTime": false, + "usingStructuredLogs": false, "nodeExists": null, "indexExists": null, "typeExists": false diff --git a/yarn.lock b/yarn.lock index 2025ceb0142cc..ef35e06a52f7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,21 +2,22 @@ # yarn lockfile v1 -"@babel/cli@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.7.5.tgz#25702cc65418efc06989af3727897b9f4c8690b6" - integrity sha512-y2YrMGXM3NUyu1Myg0pxg+Lx6g8XhEyvLHYNRwTBV6fDek3H7Io6b7N/LXscLs4HWn4HxMdy7f2rM1rTMp2mFg== +"@babel/cli@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.5.5.tgz#bdb6d9169e93e241a08f5f7b0265195bf38ef5ec" + integrity sha512-UHI+7pHv/tk9g6WXQKYz+kmXTI77YtuY3vqC59KIqcoWEjsJJSG6rAxKaLsgj3LDyadsPrCB929gVOKM6Hui0w== dependencies: - commander "^4.0.1" + commander "^2.8.1" convert-source-map "^1.1.0" fs-readdir-recursive "^1.1.0" glob "^7.0.0" lodash "^4.17.13" - make-dir "^2.1.0" + mkdirp "^0.5.1" + output-file-sync "^2.0.0" slash "^2.0.0" source-map "^0.5.0" optionalDependencies: - chokidar "^2.1.8" + chokidar "^2.0.4" "@babel/code-frame@7.5.5", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": version "7.5.5" @@ -25,7 +26,7 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/core@7.5.5", "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.4.3": +"@babel/core@7.5.5", "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.4.3", "@babel/core@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30" integrity sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg== @@ -65,26 +66,6 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.7.5.tgz#ae1323cd035b5160293307f50647e83f8ba62f7e" - integrity sha512-M42+ScN4+1S9iB6f+TL7QBpoQETxbclx+KNoKJABghnKYE+fMzSGqst0BZJc8CpI625bwPwYgUyRvxZ+0mZzpw== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.7.4" - "@babel/helpers" "^7.7.4" - "@babel/parser" "^7.7.5" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" - convert-source-map "^1.7.0" - debug "^4.1.0" - json5 "^2.1.0" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - "@babel/generator@^7.0.0", "@babel/generator@^7.4.0", "@babel/generator@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf" @@ -133,13 +114,6 @@ dependencies: "@babel/types" "^7.0.0" -"@babel/helper-annotate-as-pure@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.4.tgz#bb3faf1e74b74bd547e867e48f551fa6b098b6ce" - integrity sha512-2BQmQgECKzYKFPpiycoF9tlb5HA4lrVyAmLLVK177EcQAqjVLciUb2/R+n1boQ9y5ENV3uz2ZqiNw7QMBBw1Og== - dependencies: - "@babel/types" "^7.7.4" - "@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f" @@ -148,14 +122,6 @@ "@babel/helper-explode-assignable-expression" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.4.tgz#5f73f2b28580e224b5b9bd03146a4015d6217f5f" - integrity sha512-Biq/d/WtvfftWZ9Uf39hbPBYDUo986m5Bb4zhkeYDGUllF43D+nUe5M6Vuo6/8JDK/0YX/uBdeoQpyaNhNugZQ== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.7.4" - "@babel/types" "^7.7.4" - "@babel/helper-builder-react-jsx@^7.3.0": version "7.3.0" resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.3.0.tgz#a1ac95a5d2b3e88ae5e54846bf462eeb81b318a4" @@ -181,15 +147,6 @@ "@babel/traverse" "^7.4.4" "@babel/types" "^7.4.4" -"@babel/helper-call-delegate@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.7.4.tgz#621b83e596722b50c0066f9dc37d3232e461b801" - integrity sha512-8JH9/B7J7tCYJ2PpWVpw9JhPuEVHztagNVuQAFBVFYluRMlpG7F1CgKEgGeL6KFqcsIa92ZYVj6DSc0XwmN1ZA== - dependencies: - "@babel/helper-hoist-variables" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" - "@babel/helper-create-class-features-plugin@^7.4.4", "@babel/helper-create-class-features-plugin@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.5.tgz#401f302c8ddbc0edd36f7c6b2887d8fa1122e5a4" @@ -214,14 +171,6 @@ "@babel/helper-replace-supers" "^7.7.4" "@babel/helper-split-export-declaration" "^7.7.4" -"@babel/helper-create-regexp-features-plugin@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.4.tgz#6d5762359fd34f4da1500e4cff9955b5299aaf59" - integrity sha512-Mt+jBKaxL0zfOIWrfQpnfYCN7/rS6GKx6CCCfuoqVVd+17R8zNDlzVYmIi9qyb2wOk002NsmSTDymkIygDUH7A== - dependencies: - "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.6.0" - "@babel/helper-define-map@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz#3dec32c2046f37e09b28c93eb0b103fd2a25d369" @@ -231,15 +180,6 @@ "@babel/types" "^7.5.5" lodash "^4.17.13" -"@babel/helper-define-map@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.7.4.tgz#2841bf92eb8bd9c906851546fe6b9d45e162f176" - integrity sha512-v5LorqOa0nVQUvAUTUF3KPastvUt/HzByXNamKQ6RdJRTV7j8rLL+WB5C/MzzWAwOomxDhYFb1wLLxHqox86lg== - dependencies: - "@babel/helper-function-name" "^7.7.4" - "@babel/types" "^7.7.4" - lodash "^4.17.13" - "@babel/helper-explode-assignable-expression@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz#537fa13f6f1674df745b0c00ec8fe4e99681c8f6" @@ -248,14 +188,6 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-explode-assignable-expression@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.4.tgz#fa700878e008d85dc51ba43e9fb835cddfe05c84" - integrity sha512-2/SicuFrNSXsZNBxe5UGdLr+HZg+raWBLE9vC98bdYOKX/U6PY0mdGlYUJdtTDPSU0Lw0PNbKKDpwYHJLn2jLg== - dependencies: - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" - "@babel/helper-function-name@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" @@ -295,13 +227,6 @@ dependencies: "@babel/types" "^7.4.4" -"@babel/helper-hoist-variables@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.4.tgz#612384e3d823fdfaaf9fce31550fe5d4db0f3d12" - integrity sha512-wQC4xyvc1Jo/FnLirL6CEgPgPCa8M74tOdjWpRhQYapz5JC7u3NYU1zCVoVAGCE3EaIP9T1A3iW0WLJ+reZlpQ== - dependencies: - "@babel/types" "^7.7.4" - "@babel/helper-member-expression-to-functions@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz#1fb5b8ec4453a93c439ee9fe3aeea4a84b76b590" @@ -323,13 +248,6 @@ dependencies: "@babel/types" "^7.0.0" -"@babel/helper-module-imports@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz#e5a92529f8888bf319a6376abfbd1cebc491ad91" - integrity sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ== - dependencies: - "@babel/types" "^7.7.4" - "@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.4.4": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz#f84ff8a09038dcbca1fd4355661a500937165b4a" @@ -342,18 +260,6 @@ "@babel/types" "^7.5.5" lodash "^4.17.13" -"@babel/helper-module-transforms@^7.7.4", "@babel/helper-module-transforms@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz#d044da7ffd91ec967db25cd6748f704b6b244835" - integrity sha512-A7pSxyJf1gN5qXVcidwLWydjftUN878VkalhXX5iQDuGyiGK3sOrrKKHF4/A4fwHtnsotv/NipwAeLzY4KQPvw== - dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@babel/helper-simple-access" "^7.7.4" - "@babel/helper-split-export-declaration" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/types" "^7.7.4" - lodash "^4.17.13" - "@babel/helper-optimise-call-expression@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz#a2920c5702b073c15de51106200aa8cad20497d5" @@ -391,17 +297,6 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-remap-async-to-generator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.4.tgz#c68c2407350d9af0e061ed6726afb4fff16d0234" - integrity sha512-Sk4xmtVdM9sA/jCI80f+KS+Md+ZHIpjuqmYPk1M7F/upHou5e4ReYmExAiu6PVe65BhJPZA2CY9x9k4BqE5klw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.7.4" - "@babel/helper-wrap-function" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" - "@babel/helper-replace-supers@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz#f84ce43df031222d2bad068d2626cb5799c34bc2" @@ -430,14 +325,6 @@ "@babel/template" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-simple-access@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz#a169a0adb1b5f418cfc19f22586b2ebf58a9a294" - integrity sha512-zK7THeEXfan7UlWsG2A6CI/L9jVnI5+xxKZOdej39Y0YtDYKx9raHk5F2EtK9K8DHRTihYwg20ADt9S36GR78A== - dependencies: - "@babel/template" "^7.7.4" - "@babel/types" "^7.7.4" - "@babel/helper-split-export-declaration@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" @@ -462,16 +349,6 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.2.0" -"@babel/helper-wrap-function@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz#37ab7fed5150e22d9d7266e830072c0cdd8baace" - integrity sha512-VsfzZt6wmsocOaVU0OokwrIytHND55yvyT4BPB9AIIgwr8+x7617hetdJTsuGwygN5RC6mxA9EJztTjuwm2ofg== - dependencies: - "@babel/helper-function-name" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" - "@babel/helpers@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.5.tgz#63908d2a73942229d1e6685bc2a0e730dde3b75e" @@ -490,15 +367,6 @@ "@babel/traverse" "^7.6.0" "@babel/types" "^7.6.0" -"@babel/helpers@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.7.4.tgz#62c215b9e6c712dadc15a9a0dcab76c92a940302" - integrity sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg== - dependencies: - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" - "@babel/highlight@^7.0.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" @@ -528,7 +396,7 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.4.tgz#cb9b36a7482110282d5cb6dd424ec9262b473d81" integrity sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A== -"@babel/parser@^7.7.4", "@babel/parser@^7.7.5": +"@babel/parser@^7.7.4": version "7.7.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.5.tgz#cbf45321619ac12d83363fcf9c94bb67fa646d71" integrity sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig== @@ -542,16 +410,7 @@ "@babel/helper-remap-async-to-generator" "^7.1.0" "@babel/plugin-syntax-async-generators" "^7.2.0" -"@babel/plugin-proposal-async-generator-functions@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.4.tgz#0351c5ac0a9e927845fffd5b82af476947b7ce6d" - integrity sha512-1ypyZvGRXriY/QP668+s8sFr2mqinhkRDMPSQLNghCQE+GAkFtp+wkHVvg2+Hdki8gwP+NFzJBJ/N1BfzCCDEw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-remap-async-to-generator" "^7.7.4" - "@babel/plugin-syntax-async-generators" "^7.7.4" - -"@babel/plugin-proposal-class-properties@7.5.5": +"@babel/plugin-proposal-class-properties@7.5.5", "@babel/plugin-proposal-class-properties@^7.3.3", "@babel/plugin-proposal-class-properties@^7.5.1", "@babel/plugin-proposal-class-properties@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz#a974cfae1e37c3110e71f3c6a2e48b8e71958cd4" integrity sha512-AF79FsnWFxjlaosgdi421vmYG6/jg79bVD0dpD44QdgobzHKuLZ6S3vl8la9qIeSwGi8i1fS0O1mfuDAAdo1/A== @@ -559,14 +418,6 @@ "@babel/helper-create-class-features-plugin" "^7.5.5" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-proposal-class-properties@^7.7.0", "@babel/plugin-proposal-class-properties@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.7.4.tgz#2f964f0cb18b948450362742e33e15211e77c2ba" - integrity sha512-EcuXeV4Hv1X3+Q1TsuOmyyxeTRiSqurGJ26+I/FW1WbymmRRapVORm6x1Zl3iDIHyRxEs+VXWp6qnlcfcJSbbw== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-decorators@7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.4.tgz#de9b2a1a8ab0196f378e2a82f10b6e2a36f21cc0" @@ -584,14 +435,6 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-dynamic-import" "^7.2.0" -"@babel/plugin-proposal-dynamic-import@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.7.4.tgz#dde64a7f127691758cbfed6cf70de0fa5879d52d" - integrity sha512-StH+nGAdO6qDB1l8sZ5UBV8AC3F2VW2I8Vfld73TMKyptMU9DY5YsJAS8U81+vEtxcH3Y/La0wG0btDrhpnhjQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-dynamic-import" "^7.7.4" - "@babel/plugin-proposal-json-strings@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317" @@ -600,23 +443,15 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-json-strings" "^7.2.0" -"@babel/plugin-proposal-json-strings@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.7.4.tgz#7700a6bfda771d8dc81973249eac416c6b4c697d" - integrity sha512-wQvt3akcBTfLU/wYoqm/ws7YOAQKu8EVJEvHip/mzkNtjaclQoCCIqKXFP5/eyfnfbQCDV3OLRIK3mIVyXuZlw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-json-strings" "^7.7.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.7.4.tgz#7db302c83bc30caa89e38fee935635ef6bd11c28" - integrity sha512-TbYHmr1Gl1UC7Vo2HVuj/Naci5BEGNZ0AJhzqD2Vpr6QPFWpUmBRLrIDjedzx7/CShq0bRDS2gI4FIs77VHLVQ== +"@babel/plugin-proposal-nullish-coalescing-operator@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.4.4.tgz#41c360d59481d88e0ce3a3f837df10121a769b39" + integrity sha512-Amph7Epui1Dh/xxUxS2+K22/MUi6+6JVTvy3P58tja3B6yKTSjwwx0/d83rF7551D6PVSSoplQb8GCwqec7HRw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.7.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.2.0" -"@babel/plugin-proposal-object-rest-spread@7.5.5", "@babel/plugin-proposal-object-rest-spread@^7.5.5": +"@babel/plugin-proposal-object-rest-spread@7.5.5", "@babel/plugin-proposal-object-rest-spread@^7.3.2", "@babel/plugin-proposal-object-rest-spread@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58" integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw== @@ -624,14 +459,6 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-object-rest-spread" "^7.2.0" -"@babel/plugin-proposal-object-rest-spread@^7.6.2", "@babel/plugin-proposal-object-rest-spread@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.4.tgz#cc57849894a5c774214178c8ab64f6334ec8af71" - integrity sha512-rnpnZR3/iWKmiQyJ3LKJpSwLDcX/nSXhdLk4Aq/tXOApIvyu7qoabrige0ylsAJffaUC51WiBu209Q0U+86OWQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.7.4" - "@babel/plugin-proposal-optional-catch-binding@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5" @@ -640,21 +467,13 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" -"@babel/plugin-proposal-optional-catch-binding@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.7.4.tgz#ec21e8aeb09ec6711bc0a39ca49520abee1de379" - integrity sha512-DyM7U2bnsQerCQ+sejcTNZh8KQEUuC3ufzdnVnSiUv/qoGJp2Z3hanKL18KDhsBT5Wj6a7CMT5mdyCNJsEaA9w== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.7.4" - -"@babel/plugin-proposal-optional-chaining@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.7.5.tgz#f0835f044cef85b31071a924010a2a390add11d4" - integrity sha512-sOwFqT8JSchtJeDD+CjmWCaiFoLxY4Ps7NjvwHC/U7l4e9i5pTRNt8nDMIFSOUL+ncFbYSwruHM8WknYItWdXw== +"@babel/plugin-proposal-optional-chaining@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.6.0.tgz#e9bf1f9b9ba10c77c033082da75f068389041af8" + integrity sha512-kj4gkZ6qUggkprRq3Uh5KP8XnE1MdIO0J7MhdDX8+rAbB6dJ2UrensGIS+0NPZAaaJ1Vr0PN6oLUgXMU1uMcSg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-chaining" "^7.7.4" + "@babel/plugin-syntax-optional-chaining" "^7.2.0" "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.4.4" @@ -665,14 +484,6 @@ "@babel/helper-regex" "^7.4.4" regexpu-core "^4.5.4" -"@babel/plugin-proposal-unicode-property-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.4.tgz#7c239ccaf09470dbe1d453d50057460e84517ebb" - integrity sha512-cHgqHgYvffluZk85dJ02vloErm3Y6xtH+2noOBOJ2kXOJH3aVCDnj5eR/lVNlTnYu4hndAPJD3rTFjW3qee0PA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-async-generators@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f" @@ -680,13 +491,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-async-generators@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.7.4.tgz#331aaf310a10c80c44a66b238b6e49132bd3c889" - integrity sha512-Li4+EjSpBgxcsmeEF8IFcfV/+yJGxHXDirDkEoyFjumuwbmfCVHUt0HuowD/iGM7OhIRyXJH9YXxqiH6N815+g== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-decorators@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz#c50b1b957dcc69e4b1127b65e1c33eef61570c1b" @@ -701,13 +505,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-dynamic-import@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz#29ca3b4415abfe4a5ec381e903862ad1a54c3aec" - integrity sha512-jHQW0vbRGvwQNgyVxwDh4yuXu4bH1f5/EICJLAhl1SblLs2CDhrsmCk+v5XLdE9wxtAFRyxx+P//Iw+a5L/tTg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-flow@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.2.0.tgz#a765f061f803bc48f240c26f8747faf97c26bf7c" @@ -722,13 +519,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-json-strings@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.7.4.tgz#86e63f7d2e22f9e27129ac4e83ea989a382e86cc" - integrity sha512-QpGupahTQW1mHRXddMG5srgpHWqRLwJnJZKXTigB9RPFCCGbDGCgBeM/iC82ICXp414WeYx/tD54w7M2qRqTMg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-jsx@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz#0b85a3b4bc7cdf4cc4b8bf236335b907ca22e7c7" @@ -743,10 +533,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-nullish-coalescing-operator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.7.4.tgz#e53b751d0c3061b1ba3089242524b65a7a9da12b" - integrity sha512-XKh/yIRPiQTOeBg0QJjEus5qiSKucKAiApNtO1psqG7D17xmE+X2i5ZqBEuSvo0HRuyPaKaSN/Gy+Ha9KFQolw== +"@babel/plugin-syntax-nullish-coalescing-operator@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.2.0.tgz#f75083dfd5ade73e783db729bbd87e7b9efb7624" + integrity sha512-lRCEaKE+LTxDQtgbYajI04ddt6WW0WJq57xqkAZ+s11h4YgfRHhVA/Y2VhfPzzFD4qeLHWg32DMp9HooY4Kqlg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -757,13 +547,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-object-rest-spread@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz#47cf220d19d6d0d7b154304701f468fc1cc6ff46" - integrity sha512-mObR+r+KZq0XhRVS2BrBKBpr5jqrqzlPvS9C9vuOf5ilSwzloAl7RPWLrgKdWS6IreaVrjHxTjtyqFiOisaCwg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c" @@ -771,24 +554,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-optional-catch-binding@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.7.4.tgz#a3e38f59f4b6233867b4a92dcb0ee05b2c334aa6" - integrity sha512-4ZSuzWgFxqHRE31Glu+fEr/MirNZOMYmD/0BhBWyLyOOQz/gTAl7QmWm2hX1QxEIXsr2vkdlwxIzTyiYRC4xcQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-optional-chaining@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.7.4.tgz#c91fdde6de85d2eb8906daea7b21944c3610c901" - integrity sha512-2MqYD5WjZSbJdUagnJvIdSfkb/ucOC9/1fRJxm7GAxY6YQLWlUvkfxoNbUPcPLHJyetKUDQ4+yyuUyAoc0HriA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-top-level-await@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.7.4.tgz#bd7d8fa7b9fee793a36e4027fd6dd1aa32f946da" - integrity sha512-wdsOw0MvkL1UIgiQ/IFr3ETcfv1xb8RMM0H9wbiDyLaJFyiDg5oZvDLCXosIXmFeIlweML5iOBXAkqddkYNizg== +"@babel/plugin-syntax-optional-chaining@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.2.0.tgz#a59d6ae8c167e7608eaa443fda9fa8fa6bf21dff" + integrity sha512-HtGCtvp5Uq/jH/WNUPkK6b7rufnCPLLlDAFN7cmACoIjaOOiXxUt3SswU5loHqrhtqTsa/WoLQ1OQ1AGuZqaWA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -813,13 +582,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-arrow-functions@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.7.4.tgz#76309bd578addd8aee3b379d809c802305a98a12" - integrity sha512-zUXy3e8jBNPiffmqkHRNDdZM2r8DWhCB7HhcoyZjiK1TxYEluLHAvQuYnTT+ARqRpabWqy/NHkO6e3MsYB5YfA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-async-to-generator@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e" @@ -829,15 +591,6 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-remap-async-to-generator" "^7.1.0" -"@babel/plugin-transform-async-to-generator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.4.tgz#694cbeae6d613a34ef0292713fa42fb45c4470ba" - integrity sha512-zpUTZphp5nHokuy8yLlyafxCJ0rSlFoSHypTUWgpdwoDXWQcseaect7cJ8Ppk6nunOM6+5rPMkod4OYKPR5MUg== - dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-remap-async-to-generator" "^7.7.4" - "@babel/plugin-transform-block-scoped-functions@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190" @@ -845,13 +598,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-block-scoped-functions@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.7.4.tgz#d0d9d5c269c78eaea76227ace214b8d01e4d837b" - integrity sha512-kqtQzwtKcpPclHYjLK//3lH8OFsCDuDJBaFhVwf8kqdnF6MN4l618UDlcA7TfRs3FayrHj+svYnSX8MC9zmUyQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-block-scoping@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz#a35f395e5402822f10d2119f6f8e045e3639a2ce" @@ -868,14 +614,6 @@ "@babel/helper-plugin-utils" "^7.0.0" lodash "^4.17.13" -"@babel/plugin-transform-block-scoping@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.7.4.tgz#200aad0dcd6bb80372f94d9e628ea062c58bf224" - integrity sha512-2VBe9u0G+fDt9B5OV5DQH4KBf5DoiNkwFKOz0TCvBWvdAN2rOykCTkrL+jTLxfCAm76l9Qo5OqL7HBOx2dWggg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - lodash "^4.17.13" - "@babel/plugin-transform-classes@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz#d094299d9bd680a14a2a0edae38305ad60fb4de9" @@ -890,20 +628,6 @@ "@babel/helper-split-export-declaration" "^7.4.4" globals "^11.1.0" -"@babel/plugin-transform-classes@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.4.tgz#c92c14be0a1399e15df72667067a8f510c9400ec" - integrity sha512-sK1mjWat7K+buWRuImEzjNf68qrKcrddtpQo3swi9j7dUcG6y6R6+Di039QN2bD1dykeswlagupEmpOatFHHUg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.7.4" - "@babel/helper-define-map" "^7.7.4" - "@babel/helper-function-name" "^7.7.4" - "@babel/helper-optimise-call-expression" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.7.4" - "@babel/helper-split-export-declaration" "^7.7.4" - globals "^11.1.0" - "@babel/plugin-transform-computed-properties@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da" @@ -911,13 +635,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-computed-properties@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.7.4.tgz#e856c1628d3238ffe12d668eb42559f79a81910d" - integrity sha512-bSNsOsZnlpLLyQew35rl4Fma3yKWqK3ImWMSC/Nc+6nGjC9s5NFWAer1YQ899/6s9HxO2zQC1WoFNfkOqRkqRQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-destructuring@7.5.0", "@babel/plugin-transform-destructuring@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz#f6c09fdfe3f94516ff074fe877db7bc9ef05855a" @@ -932,13 +649,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-destructuring@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.7.4.tgz#2b713729e5054a1135097b6a67da1b6fe8789267" - integrity sha512-4jFMXI1Cu2aXbcXXl8Lr6YubCn6Oc7k9lLsu8v61TZh+1jny2BWmdtvY9zSUlLdGUvcy9DMAWyZEOqjsbeg/wA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3" @@ -948,14 +658,6 @@ "@babel/helper-regex" "^7.4.4" regexpu-core "^4.5.4" -"@babel/plugin-transform-dotall-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.4.tgz#f7ccda61118c5b7a2599a72d5e3210884a021e96" - integrity sha512-mk0cH1zyMa/XHeb6LOTXTbG7uIJ8Rrjlzu91pUx/KS3JpcgaTDwMS8kM+ar8SLOvlL2Lofi4CGBAjCo3a2x+lw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-duplicate-keys@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz#c5dbf5106bf84cdf691222c0974c12b1df931853" @@ -963,13 +665,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-duplicate-keys@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.7.4.tgz#3d21731a42e3f598a73835299dd0169c3b90ac91" - integrity sha512-g1y4/G6xGWMD85Tlft5XedGaZBCIVN+/P0bs6eabmcPP9egFleMAo65OOjlhcz1njpwagyY3t0nsQC9oTFegJA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-exponentiation-operator@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008" @@ -978,14 +673,6 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-exponentiation-operator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.7.4.tgz#dd30c0191e3a1ba19bcc7e389bdfddc0729d5db9" - integrity sha512-MCqiLfCKm6KEA1dglf6Uqq1ElDIZwFuzz1WH5mTf8k2uQSxEJMbOIEh7IZv7uichr7PMfi5YVSrr1vz+ipp7AQ== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-flow-strip-types@7.4.4", "@babel/plugin-transform-flow-strip-types@^7.0.0": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.4.4.tgz#d267a081f49a8705fc9146de0768c6b58dccd8f7" @@ -1001,13 +688,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-for-of@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.7.4.tgz#248800e3a5e507b1f103d8b4ca998e77c63932bc" - integrity sha512-zZ1fD1B8keYtEcKF+M1TROfeHTKnijcVQm0yO/Yu1f7qoDoxEIc/+GX6Go430Bg84eM/xwPFp0+h4EbZg7epAA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-function-name@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad" @@ -1016,14 +696,6 @@ "@babel/helper-function-name" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-function-name@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.4.tgz#75a6d3303d50db638ff8b5385d12451c865025b1" - integrity sha512-E/x09TvjHNhsULs2IusN+aJNRV5zKwxu1cpirZyRPw+FyyIKEHPXTsadj48bVpc1R5Qq1B5ZkzumuFLytnbT6g== - dependencies: - "@babel/helper-function-name" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-literals@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1" @@ -1031,13 +703,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.7.4.tgz#27fe87d2b5017a2a5a34d1c41a6b9f6a6262643e" - integrity sha512-X2MSV7LfJFm4aZfxd0yLVFrEXAgPqYoDG53Br/tCKiKYfX0MjVjQeWPIhPHHsCqzwQANq+FLN786fF5rgLS+gw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-member-expression-literals@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz#fa10aa5c58a2cb6afcf2c9ffa8cb4d8b3d489a2d" @@ -1045,13 +710,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-member-expression-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.7.4.tgz#aee127f2f3339fc34ce5e3055d7ffbf7aa26f19a" - integrity sha512-9VMwMO7i69LHTesL0RdGy93JU6a+qOPuvB4F4d0kR0zyVjJRVJRaoaGjhtki6SzQUu8yen/vxPKN6CWnCUw6bA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-modules-amd@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz#ef00435d46da0a5961aa728a1d2ecff063e4fb91" @@ -1061,15 +719,6 @@ "@babel/helper-plugin-utils" "^7.0.0" babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-amd@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.5.tgz#39e0fb717224b59475b306402bb8eedab01e729c" - integrity sha512-CT57FG4A2ZUNU1v+HdvDSDrjNWBrtCmSH6YbbgN3Lrf0Di/q/lWRxZrE72p3+HCCz9UjfZOEBdphgC0nzOS6DQ== - dependencies: - "@babel/helper-module-transforms" "^7.7.5" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.0" - "@babel/plugin-transform-modules-commonjs@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74" @@ -1090,16 +739,6 @@ "@babel/helper-simple-access" "^7.1.0" babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-commonjs@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.5.tgz#1d27f5eb0bcf7543e774950e5b2fa782e637b345" - integrity sha512-9Cq4zTFExwFhQI6MT1aFxgqhIsMWQWDVwOgLzl7PTWJHsNaqFvklAU+Oz6AQLAS0dJKTwZSOCo20INwktxpi3Q== - dependencies: - "@babel/helper-module-transforms" "^7.7.5" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-simple-access" "^7.7.4" - babel-plugin-dynamic-import-node "^2.3.0" - "@babel/plugin-transform-modules-systemjs@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz#e75266a13ef94202db2a0620977756f51d52d249" @@ -1109,15 +748,6 @@ "@babel/helper-plugin-utils" "^7.0.0" babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-systemjs@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.4.tgz#cd98152339d3e763dfe838b7d4273edaf520bb30" - integrity sha512-y2c96hmcsUi6LrMqvmNDPBBiGCiQu0aYqpHatVVu6kD4mFEXKjyNxd/drc18XXAf9dv7UXjrZwBVmTTGaGP8iw== - dependencies: - "@babel/helper-hoist-variables" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.0" - "@babel/plugin-transform-modules-umd@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz#7678ce75169f0877b8eb2235538c074268dd01ae" @@ -1126,14 +756,6 @@ "@babel/helper-module-transforms" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-modules-umd@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.4.tgz#1027c355a118de0aae9fee00ad7813c584d9061f" - integrity sha512-u2B8TIi0qZI4j8q4C51ktfO7E3cQ0qnaXFI1/OXITordD40tt17g/sXqgNNCcMTcBFKrUPcGDx+TBJuZxLx7tw== - dependencies: - "@babel/helper-module-transforms" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-named-capturing-groups-regex@^7.4.5": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz#9d269fd28a370258199b4294736813a60bbdd106" @@ -1148,13 +770,6 @@ dependencies: regexp-tree "^0.1.13" -"@babel/plugin-transform-named-capturing-groups-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.4.tgz#fb3bcc4ee4198e7385805007373d6b6f42c98220" - integrity sha512-jBUkiqLKvUWpv9GLSuHUFYdmHg0ujC1JEYoZUfeOOfNydZXp1sXObgyPatpcwjWgsdBGsagWW0cdJpX/DO2jMw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" - "@babel/plugin-transform-new-target@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5" @@ -1162,13 +777,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-new-target@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.7.4.tgz#4a0753d2d60639437be07b592a9e58ee00720167" - integrity sha512-CnPRiNtOG1vRodnsyGX37bHQleHE14B9dnnlgSeEs3ek3fHN1A1SScglTCg1sfbe7sRQ2BUcpgpTpWSfMKz3gg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-object-super@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz#c70021df834073c65eb613b8679cc4a381d1a9f9" @@ -1177,14 +785,6 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-replace-supers" "^7.5.5" -"@babel/plugin-transform-object-super@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.7.4.tgz#48488937a2d586c0148451bf51af9d7dda567262" - integrity sha512-ho+dAEhC2aRnff2JCA0SAK7V2R62zJd/7dmtoe7MHcso4C2mS+vZjn1Pb1pCVZvJs1mgsvv5+7sT+m3Bysb6eg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.7.4" - "@babel/plugin-transform-parameters@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16" @@ -1194,15 +794,6 @@ "@babel/helper-get-function-arity" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-parameters@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.7.4.tgz#da4555c97f39b51ac089d31c7380f03bca4075ce" - integrity sha512-VJwhVePWPa0DqE9vcfptaJSzNDKrWU/4FbYCjZERtmqEs05g3UMXnYMZoXja7JAJ7Y7sPZipwm/pGApZt7wHlw== - dependencies: - "@babel/helper-call-delegate" "^7.7.4" - "@babel/helper-get-function-arity" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-property-literals@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz#03e33f653f5b25c4eb572c98b9485055b389e905" @@ -1210,14 +801,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-property-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.7.4.tgz#2388d6505ef89b266103f450f9167e6bd73f98c2" - integrity sha512-MatJhlC4iHsIskWYyawl53KuHrt+kALSADLQQ/HkhTjX954fkxIEh4q5slL4oRAnsm/eDoZ4q0CIZpcqBuxhJQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-react-constant-elements@^7.0.0": +"@babel/plugin-transform-react-constant-elements@^7.0.0", "@babel/plugin-transform-react-constant-elements@^7.2.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.5.0.tgz#4d6ae4033bc38f8a65dfca2b6235c44522a422fc" integrity sha512-c5Ba8cpybZFp1Izkf2sWGuNjOxoQ32tFgBvvYvwGhi4+9f6vGiSK9Gex4uVuO/Va6YJFu41aAh1MzMjUWkp0IQ== @@ -1225,14 +809,6 @@ "@babel/helper-annotate-as-pure" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-react-constant-elements@^7.6.3": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.7.4.tgz#499cf732a21ffd62cc4b0016e27c3906097f8982" - integrity sha512-U6XkHZ8RnmeEb8jBUOpeo6oFka5RhLgxAVvK4/fBbwoYlsHQYLb8I37ymTPDVsrWjqb94+hueuWQA/1OAA4rAQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-react-display-name@7.2.0", "@babel/plugin-transform-react-display-name@^7.0.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz#ebfaed87834ce8dc4279609a4f0c324c156e3eb0" @@ -1304,13 +880,6 @@ dependencies: regenerator-transform "^0.14.0" -"@babel/plugin-transform-regenerator@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.5.tgz#3a8757ee1a2780f390e89f246065ecf59c26fce9" - integrity sha512-/8I8tPvX2FkuEyWbjRCt4qTAgZK0DVy8QRguhA524UH48RfGJy94On2ri+dCuwOpcerPRl9O4ebQkRcVzIaGBw== - dependencies: - regenerator-transform "^0.14.0" - "@babel/plugin-transform-reserved-words@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz#4792af87c998a49367597d07fedf02636d2e1634" @@ -1318,14 +887,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-reserved-words@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.7.4.tgz#6a7cf123ad175bb5c69aec8f6f0770387ed3f1eb" - integrity sha512-OrPiUB5s5XvkCO1lS7D8ZtHcswIC57j62acAnJZKqGGnHP+TIc/ljQSrgdX/QyOTdEK5COAhuc820Hi1q2UgLQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-runtime@7.5.5": +"@babel/plugin-transform-runtime@7.5.5", "@babel/plugin-transform-runtime@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.5.tgz#a6331afbfc59189d2135b2e09474457a8e3d28bc" integrity sha512-6Xmeidsun5rkwnGfMOp6/z9nSzWpHFNVr2Jx7kwoq4mVatQfQx5S56drBgEHF+XQbKOdIaOiMIINvp/kAwMN+w== @@ -1335,16 +897,6 @@ resolve "^1.8.1" semver "^5.5.1" -"@babel/plugin-transform-runtime@^7.7.6": - version "7.7.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.7.6.tgz#4f2b548c88922fb98ec1c242afd4733ee3e12f61" - integrity sha512-tajQY+YmXR7JjTwRvwL4HePqoL3DYxpYXIHKVvrOIvJmeHe2y1w4tz5qz9ObUDC9m76rCzIMPyn4eERuwA4a4A== - dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - resolve "^1.8.1" - semver "^5.5.1" - "@babel/plugin-transform-shorthand-properties@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0" @@ -1352,13 +904,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-shorthand-properties@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.7.4.tgz#74a0a9b2f6d67a684c6fbfd5f0458eb7ba99891e" - integrity sha512-q+suddWRfIcnyG5YiDP58sT65AJDZSUhXQDZE3r04AuqD6d/XLaQPPXSBzP2zGerkgBivqtQm9XKGLuHqBID6Q== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-spread@^7.2.0": version "7.2.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406" @@ -1366,13 +911,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-spread@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.7.4.tgz#aa673b356fe6b7e70d69b6e33a17fef641008578" - integrity sha512-8OSs0FLe5/80cndziPlg4R0K6HcWSM0zyNhHhLsmw/Nc5MaA49cAsnoJ/t/YZf8qkG7fD+UjTRaApVDB526d7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-sticky-regex@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1" @@ -1381,14 +919,6 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.0.0" -"@babel/plugin-transform-sticky-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.7.4.tgz#ffb68c05090c30732076b1285dc1401b404a123c" - integrity sha512-Ls2NASyL6qtVe1H1hXts9yuEeONV2TJZmplLONkMPUG158CtmnrzW5Q5teibM5UVOFjG0D3IC5mzXR6pPpUY7A== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.0.0" - "@babel/plugin-transform-template-literals@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0" @@ -1397,14 +927,6 @@ "@babel/helper-annotate-as-pure" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-template-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.7.4.tgz#1eb6411736dd3fe87dbd20cc6668e5121c17d604" - integrity sha512-sA+KxLwF3QwGj5abMHkHgshp9+rRz+oY9uoRil4CyLtgEuE/88dpkeWgNk5qKVsJE9iSfly3nvHapdRiIS2wnQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-typeof-symbol@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2" @@ -1412,13 +934,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-typeof-symbol@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.7.4.tgz#3174626214f2d6de322882e498a38e8371b2140e" - integrity sha512-KQPUQ/7mqe2m0B8VecdyaW5XcQYaePyl9R7IsKd+irzj6jvbhoGnRE+M0aNkyAzI07VfUQ9266L5xMARitV3wg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-typescript@^7.3.2": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.5.5.tgz#6d862766f09b2da1cb1f7d505fe2aedab6b7d4b8" @@ -1446,15 +961,7 @@ "@babel/helper-regex" "^7.4.4" regexpu-core "^4.5.4" -"@babel/plugin-transform-unicode-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.4.tgz#a3c0f65b117c4c81c5b6484f2a5e7b95346b83ae" - integrity sha512-N77UUIV+WCvE+5yHw+oks3m18/umd7y392Zv7mYTpFqHtkpcc+QUz+gLJNTWVlWROIWeLqY0f3OjZxV5TcXnRw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/preset-env@7.5.5", "@babel/preset-env@^7.4.3": +"@babel/preset-env@7.5.5", "@babel/preset-env@^7.4.3", "@babel/preset-env@^7.4.5", "@babel/preset-env@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.5.tgz#bc470b53acaa48df4b8db24a570d6da1fef53c9a" integrity sha512-GMZQka/+INwsMz1A5UEql8tG015h5j/qjptpKY2gJ7giy8ohzU710YciJB5rcKsWGWHiW3RUnHib0E5/m3Tp3A== @@ -1566,63 +1073,6 @@ js-levenshtein "^1.1.3" semver "^5.5.0" -"@babel/preset-env@^7.7.1", "@babel/preset-env@^7.7.6": - version "7.7.6" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.7.6.tgz#39ac600427bbb94eec6b27953f1dfa1d64d457b2" - integrity sha512-k5hO17iF/Q7tR9Jv8PdNBZWYW6RofxhnxKjBMc0nG4JTaWvOTiPoO/RLFwAKcA4FpmuBFm6jkoqaRJLGi0zdaQ== - dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-async-generator-functions" "^7.7.4" - "@babel/plugin-proposal-dynamic-import" "^7.7.4" - "@babel/plugin-proposal-json-strings" "^7.7.4" - "@babel/plugin-proposal-object-rest-spread" "^7.7.4" - "@babel/plugin-proposal-optional-catch-binding" "^7.7.4" - "@babel/plugin-proposal-unicode-property-regex" "^7.7.4" - "@babel/plugin-syntax-async-generators" "^7.7.4" - "@babel/plugin-syntax-dynamic-import" "^7.7.4" - "@babel/plugin-syntax-json-strings" "^7.7.4" - "@babel/plugin-syntax-object-rest-spread" "^7.7.4" - "@babel/plugin-syntax-optional-catch-binding" "^7.7.4" - "@babel/plugin-syntax-top-level-await" "^7.7.4" - "@babel/plugin-transform-arrow-functions" "^7.7.4" - "@babel/plugin-transform-async-to-generator" "^7.7.4" - "@babel/plugin-transform-block-scoped-functions" "^7.7.4" - "@babel/plugin-transform-block-scoping" "^7.7.4" - "@babel/plugin-transform-classes" "^7.7.4" - "@babel/plugin-transform-computed-properties" "^7.7.4" - "@babel/plugin-transform-destructuring" "^7.7.4" - "@babel/plugin-transform-dotall-regex" "^7.7.4" - "@babel/plugin-transform-duplicate-keys" "^7.7.4" - "@babel/plugin-transform-exponentiation-operator" "^7.7.4" - "@babel/plugin-transform-for-of" "^7.7.4" - "@babel/plugin-transform-function-name" "^7.7.4" - "@babel/plugin-transform-literals" "^7.7.4" - "@babel/plugin-transform-member-expression-literals" "^7.7.4" - "@babel/plugin-transform-modules-amd" "^7.7.5" - "@babel/plugin-transform-modules-commonjs" "^7.7.5" - "@babel/plugin-transform-modules-systemjs" "^7.7.4" - "@babel/plugin-transform-modules-umd" "^7.7.4" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.7.4" - "@babel/plugin-transform-new-target" "^7.7.4" - "@babel/plugin-transform-object-super" "^7.7.4" - "@babel/plugin-transform-parameters" "^7.7.4" - "@babel/plugin-transform-property-literals" "^7.7.4" - "@babel/plugin-transform-regenerator" "^7.7.5" - "@babel/plugin-transform-reserved-words" "^7.7.4" - "@babel/plugin-transform-shorthand-properties" "^7.7.4" - "@babel/plugin-transform-spread" "^7.7.4" - "@babel/plugin-transform-sticky-regex" "^7.7.4" - "@babel/plugin-transform-template-literals" "^7.7.4" - "@babel/plugin-transform-typeof-symbol" "^7.7.4" - "@babel/plugin-transform-unicode-regex" "^7.7.4" - "@babel/types" "^7.7.4" - browserslist "^4.6.0" - core-js-compat "^3.4.7" - invariant "^2.2.2" - js-levenshtein "^1.1.3" - semver "^5.5.0" - "@babel/preset-flow@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.0.0.tgz#afd764835d9535ec63d8c7d4caf1c06457263da2" @@ -1642,7 +1092,7 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" -"@babel/preset-react@^7.7.0", "@babel/preset-react@^7.7.4": +"@babel/preset-react@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.7.4.tgz#3fe2ea698d8fb536d8e7881a592c3c1ee8bf5707" integrity sha512-j+vZtg0/8pQr1H8wKoaJyGL2IEk3rG/GIvua7Sec7meXVIvGycihlGMx5xcU00kqCJbwzHs18xTu3YfREOqQ+g== @@ -1669,10 +1119,10 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-transform-typescript" "^7.7.4" -"@babel/register@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.7.4.tgz#45a4956471a9df3b012b747f5781cc084ee8f128" - integrity sha512-/fmONZqL6ZMl9KJUYajetCrID6m0xmL4odX7v+Xvoxcv0DdbP/oO0TWIeLUCHqczQ6L6njDMqmqHFy2cp3FFsA== +"@babel/register@^7.7.0": + version "7.7.0" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.7.0.tgz#4e23ecf840296ef79c605baaa5c89e1a2426314b" + integrity sha512-HV3GJzTvSoyOMWGYn2TAh6uL6g+gqKTgEZ99Q3+X9UURT1VPT/WcU46R61XftIc5rXytcOHZ4Z0doDlsjPomIg== dependencies: find-cache-dir "^2.0.0" lodash "^4.17.13" @@ -1718,7 +1168,7 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2": version "7.7.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.6.tgz#d18c511121aff1b4f2cd1d452f1bac9601dd830f" integrity sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw== @@ -1839,7 +1289,7 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.7.2", "@babel/types@^7.7.4": +"@babel/types@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193" integrity sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA== @@ -1866,13 +1316,13 @@ date-fns "^1.27.2" figures "^1.7.0" -"@cypress/webpack-preprocessor@^4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@cypress/webpack-preprocessor/-/webpack-preprocessor-4.1.1.tgz#3c0b5b8de6eaac605dac3b1f1c3f5916c1c6eaea" - integrity sha512-SfzDqOvWBSlfGRm8ak/XHUXAnndwHU2qJIRr1LIC7j2UqWcZoJ+286CuNloJbkwfyEAO6tQggLd4E/WHUAcKZQ== +"@cypress/webpack-preprocessor@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@cypress/webpack-preprocessor/-/webpack-preprocessor-4.1.0.tgz#8c4debc0b1abf045b62524d1996dd9aeaf7e86a8" + integrity sha512-LbxsdYVpHGoC2fMOdW0aQvuvVRD7aZx8p8DrP53HISpl7bD1PqLGWKzhHn7cGG24UHycBJrbaEeKEosW29W1dg== dependencies: - bluebird "3.7.1" - debug "4.1.1" + bluebird "3.5.0" + debug "3.1.0" optionalDependencies: "@babel/core" "^7.0.1" "@babel/preset-env" "^7.0.0" @@ -1886,6 +1336,31 @@ debug "^3.1.0" lodash.once "^4.1.1" +"@elastic/apm-rum-core@^4.7.0": + version "4.7.0" + resolved "https://registry.yarnpkg.com/@elastic/apm-rum-core/-/apm-rum-core-4.7.0.tgz#b00b58bf7380f2e36652e5333e3ca97608986e40" + integrity sha512-/lTZWfA3ces3qoKCx72Sc+w43lZkyktaQlbYoYO86h3tNX7tScc/7YBBHI9oxKMcXweqkKOcpnwNZFy71bb86w== + dependencies: + error-stack-parser "^1.3.5" + es6-promise "^4.2.8" + opentracing "^0.14.3" + uuid "^3.1.0" + +"@elastic/apm-rum-react@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@elastic/apm-rum-react/-/apm-rum-react-0.3.2.tgz#134634643e15ebcf97b6f17b2c74a50afdbe1c64" + integrity sha512-hU1srW9noygppyrLmipulu30c+LWEie8V/dQjEqLYMx2mRZRwNIue3midYgWa6qrWqgYZhwpAtWrWcXc+AWk2Q== + dependencies: + "@elastic/apm-rum" "^4.6.0" + hoist-non-react-statics "^3.3.0" + +"@elastic/apm-rum@^4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@elastic/apm-rum/-/apm-rum-4.6.0.tgz#e2ac560dd4a4761c0e9b08c301418b1d4063bdd2" + integrity sha512-hsqvyTm5rT6lKgV06wvm8ID9aMsuJyw8wIOPjRwKmvzlTjayabxKTcr50lJJV8jY9OWfDkqymIqpHyCEChQAHQ== + dependencies: + "@elastic/apm-rum-core" "^4.7.0" + "@elastic/charts@^14.0.0": version "14.0.0" resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-14.0.0.tgz#410c87e9ae53df5848aae09a210fa7d08510b376" @@ -2395,26 +1870,24 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" -"@jimp/bmp@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.9.3.tgz#98eafc81674ce750f428ac9380007f1a4e90255e" - integrity sha512-wXZYccgGQAsIK8DZX0wZE3gbSd2mL2+eheSJMts6I5hQjxhVRZd1Gwu425nUQGzfKCOgKYTW0nLv7/8OoOTTkw== +"@jimp/bmp@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.8.4.tgz#3246e0c6b073b3e2d9b61075ac0146d9124c9277" + integrity sha512-Cf/V+SUyEVxCCP8q1emkarCHJ8NkLFcLp41VMqBihoR4ke0TIPfCSdgW/JXbM/28vvZ5a2bvMe6uOll6cFggvA== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" + "@jimp/utils" "^0.8.4" bmp-js "^0.1.0" - core-js "^3.4.1" + core-js "^2.5.7" -"@jimp/core@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/core/-/core-0.9.3.tgz#bffbf955c046569bf4b682b575228e31bb41e445" - integrity sha512-kB9lvst1QhgYOC963SAuPgv+DdVfxTProphrSffAAoo5eLeQab/Ca3ZUeX1E/SnLSr+NGVnNCd8c9gyuKDiENg== +"@jimp/core@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/core/-/core-0.8.4.tgz#fbdb3cb0823301381736e988674f14c282dc5c63" + integrity sha512-3fK5UEOEQsfSDhsrAgBT6W8Up51qkeCj9RVjusxUaEGmix34PO/KTVfzURlu6NOpOUvtfNXsCq9xS7cxBTWSCA== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" + "@jimp/utils" "^0.8.4" any-base "^1.1.0" buffer "^5.2.0" - core-js "^3.4.1" + core-js "^2.5.7" exif-parser "^0.1.12" file-type "^9.0.0" load-bmfont "^1.3.1" @@ -2423,256 +1896,231 @@ pixelmatch "^4.0.2" tinycolor2 "^1.4.1" -"@jimp/custom@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/custom/-/custom-0.9.3.tgz#b49dfe1d6b24e62fd4101a7db77104024c8d97e8" - integrity sha512-2E7yabQMeqjcK8+ZFu3Ja5cWyrB0zv/pmzNSDg/BBPJ59HE0fj/qcERAz6VklcjHUYRUfmE5uODsb+4DE0o/YQ== +"@jimp/custom@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/custom/-/custom-0.8.4.tgz#abd61281ce12194ae23046ee71d60b754b515bc8" + integrity sha512-iS/RB3QQKpm4QS8lxxtQzvYDMph9YvOn3d68gMM4pDKn95n3nt5/ySHFv6fQq/yzfox1OPdeYaXbOLvC3+ofqw== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/core" "^0.9.3" - core-js "^3.4.1" + "@jimp/core" "^0.8.4" + core-js "^2.5.7" -"@jimp/gif@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/gif/-/gif-0.9.3.tgz#b2b1a519092f94a913a955f252996f9a968930db" - integrity sha512-DshKgMQ8lXorI/xTRyeRkZqZ3JqgnL2aGYAhx0SkAunyHgXji27chmrOGj/6KVDBucrDf/6mSexnSoUDnlWrfA== +"@jimp/gif@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/gif/-/gif-0.8.4.tgz#1429a71ed3b055f73d63c9b195fa7f0a46e947b5" + integrity sha512-YpHZ7aWzmrviY7YigXRolHs6oBhGJItRry8fh3zebAgKth06GMv58ce84yXXOKX4yQ+QGd6GgOWzePx+KMP9TA== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" omggif "^1.0.9" -"@jimp/jpeg@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/jpeg/-/jpeg-0.9.3.tgz#a759cb3bccf3cb163166873b9bdc0c949c5991b5" - integrity sha512-AJzcTJXfN9BHtpzAbICwR3+GoH0pSr6OYXbAS6yuKwz+xVn9UHrEjQb74CIzIRqrT/VWcIKg29cMQxgokzWY7w== +"@jimp/jpeg@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/jpeg/-/jpeg-0.8.4.tgz#effde867116f88f59ac20b44b1a526b11caca026" + integrity sha512-7exKk3LNPKJgsFzUPL+mOJtIEHcLp6yU9sVbULffVDjVUun6/Are2tCX8rCXZq28yiUhofzr61k5UqjkKFJXrA== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" jpeg-js "^0.3.4" -"@jimp/plugin-blit@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-blit/-/plugin-blit-0.9.3.tgz#740346ac62ec0f7ae4458f5fd59c7582e630a8e8" - integrity sha512-+UxCsJ3XkRSdpigpTBJ9WkdwUc3OtBlhVZdU6OL6M9ldume5Gj3rTyWvMCqytOK1tZ/+7HmxoWe4IWX31hz9qA== +"@jimp/plugin-blit@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-blit/-/plugin-blit-0.8.4.tgz#991b4199cc5506f0faae22b821b14ec93fbce1bb" + integrity sha512-H9bpetmOUgEHpkDSRzbXLMXQhr34i8YicYV3EDeuHU8mKlAjtMbVpbp5ZN4mcadTz+EYdTdVNfQNsRCcIb5Oeg== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-blur@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-blur/-/plugin-blur-0.9.3.tgz#9df505aaa63de138060264cf83ed4a98304bf105" - integrity sha512-RADcYjZ5vbk5ZrUiK7qv0G4xOpHtu19HWVVX9JTDbm4VByWTxPboVKlgiYLA6l+IxIXNtEqDclsADIM0s9FQhA== +"@jimp/plugin-blur@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-blur/-/plugin-blur-0.8.4.tgz#460f79c45eda7f24adf624a691134d6192d3dbb4" + integrity sha512-gvEDWW7+MI9Hk1KKzuFliRdDPaofkxB4pRJ/n1hipDoOGcNYFqxx5FGNQ4wsGSDpQ+RiHZF+JGKKb+EIwHg+0Q== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-color@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-color/-/plugin-color-0.9.3.tgz#4a5ad28f68901355878f5330186c260f4f87f944" - integrity sha512-gHDA5GVx4/R4fitEACKmWH7hNy0aU48MZWYRxmATvuqY39KidJ0fjwp+brQ3Ivgb35AgFVc2jQYc3U/JXv4RxQ== +"@jimp/plugin-color@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-color/-/plugin-color-0.8.4.tgz#a9aa525421ea50bf2c1baec7618f73b7e4fc3464" + integrity sha512-DHCGMxInCI1coXMIfdZJ5G/4hpt5yZLNB5+oUIxT4aClzyhUjqD4xOcnO7hlPY6LuX8+FX7cYMHhdMfhTXB3Dg== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" tinycolor2 "^1.4.1" -"@jimp/plugin-contain@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-contain/-/plugin-contain-0.9.3.tgz#d0da9892edea25549611c88e125bfcc59045c426" - integrity sha512-vdYAtp65LNDT/hMctow5o0a/SbD41/y7Z9AO7MGsfUIK92Woq90SNTWx7JplDl4HSZGrqaBONnfiEhRiYlDrdg== +"@jimp/plugin-contain@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-contain/-/plugin-contain-0.8.4.tgz#2db8c12de910490cd74f339e9414a968b8c9328e" + integrity sha512-3wwLXig5LkOMg5FrNZrX/r99ehaA+0s3dkro3CiRg0Ez6Y0fz067so+HdsmqmoG78WY/dCdgdps/xLOW2VV4DQ== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-cover@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-cover/-/plugin-cover-0.9.3.tgz#2fca63620fcf8145bdecf315cf461588b09d9488" - integrity sha512-yOwsvakgyS2/C4iZF1a1wg63QKfYvqb2d6k+rgY/0vaAe44JtEx+Gbg+7iOt4EaMm5BDlxRwmcA2Q8Pef8TvAQ== +"@jimp/plugin-cover@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-cover/-/plugin-cover-0.8.4.tgz#a09bfbbe88129754ca35e281707bc5ed3f3f0c63" + integrity sha512-U0xmSfGLmw0Ieiw00CM8DQ+XoQVBxbjsLE5To8EejnyLx5X+oNZ8r7E5EsQaushUlzij95IqMCloo+nCGhdYMw== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-crop@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-crop/-/plugin-crop-0.9.3.tgz#9b19c11293714a99c03d4b517ab597a5f88823e8" - integrity sha512-kqMXSyY8hrfo0idr6qY2USOWPrNqpDWs+D6Vwa+kV6SGJhj3rMTIcptQDaamIETSxbjkE8rwUu3K4Q5UD69D7w== +"@jimp/plugin-crop@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-crop/-/plugin-crop-0.8.4.tgz#ca5bd359c4e4b2374777bae6130e8b94552932fa" + integrity sha512-Neqs0K4cr7SU9nSte2qvGVh/8+K9ArH8mH1fWhZw4Zq8qD9NicX+g5hqmpmeSjOKD73t/jOmwvBevfJDu2KKSA== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-displace@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-displace/-/plugin-displace-0.9.3.tgz#07645687b29ebc8a8491244410172795d511ba21" - integrity sha512-0AdwxYRWDmJ2wIRIj2RR3sRmNjMhcy5Kwt9Jbi/RRnzxkRScZAiyzkNZhBul23EM7ClfjrUrZufuUvRMHxZRDw== +"@jimp/plugin-displace@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-displace/-/plugin-displace-0.8.4.tgz#c6d5cff889e52cb64194979967e6bd7fff4d5d1b" + integrity sha512-qKCwAP2lAO3R8ofYaEF/Gh+sfcjzZLtEiYHzjx/mYvPpXS6Yvkvl28aUH8pwdJYT+QYGelHmOne0RJvjsac1NQ== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-dither@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-dither/-/plugin-dither-0.9.3.tgz#292b3ee617a5dcfe065d13b643055e910f8b6934" - integrity sha512-8OE+Xak9xepiCwSV+oAsb/gupTnttG3aDKxtpSZjwHebnr+k1VG8NgICbMSFATTVJqqZ18oj6LC+5726qHUJ9w== +"@jimp/plugin-dither@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-dither/-/plugin-dither-0.8.4.tgz#a2320d6a8c467cf7697109e0c5ed4ee3d3898b73" + integrity sha512-19+y5VAO6d0keRne9eJCdOeB9X0LFuRdRSjgwl/57JtREeoPj+iKBg6REBl4atiSGd7/UCFg3wRtFOw24XFKgw== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-flip@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-flip/-/plugin-flip-0.9.3.tgz#a755ffa1d860106067215987cbac213501d22b41" - integrity sha512-w+lzE1ZF/UOjB8qJdeIm+dLQtOK1obZwGYdCIbgxZxw4SfkkjAftJdY8o8RNOXhHDZqGu+cYQZbMKP1zcoNkyQ== +"@jimp/plugin-flip@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-flip/-/plugin-flip-0.8.4.tgz#08bf46470c3c0b4890691f554c28ccf17813746f" + integrity sha512-1BtKtc8cANuGgiWyOmltQZaR3Y5Og/GS/db8wBpFNLJ33Ir5UAGN2raDtx4EYEd5okuRVFj3OP+wAZl69m72LQ== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-gaussian@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-gaussian/-/plugin-gaussian-0.9.3.tgz#b10b5a5b4c37cb4edc3ed22a9b25294e68daf2f8" - integrity sha512-RPrWwzlZsbWC2opSgeyWt30JU9Uwg1+GwBnoNpEMLKeqm0Dv6snASASa4zVtviGWAIq//p3Jrap7g57hKqL0Cg== +"@jimp/plugin-gaussian@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-gaussian/-/plugin-gaussian-0.8.4.tgz#f3be12c5f16c5670959ab711e69b2963f66f7b4f" + integrity sha512-qYcVmiJn8l8uDZqk4FlB/qTV8fJgiJAh/xc/WKNEp2E8qFEgxoIPeimPHO8cJorEHqlh8I8l24OZkTkkEKaFfw== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-invert@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-invert/-/plugin-invert-0.9.3.tgz#723a873133a1d62f9b93e023991f262c85917c78" - integrity sha512-0lRsh7IPkzyYqExrZDT50h38xdlB/+KrdiDcuxWwWyIlKauLMR0kInjwf8sPeb3elPLeETmze7uwPAxrIAtsGQ== +"@jimp/plugin-invert@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-invert/-/plugin-invert-0.8.4.tgz#fd4577beba2973f663164f5ee454b2172ca56b34" + integrity sha512-OQ/dFDbBUmEd935Gitl5Pmgz+nLVyszwS0RqL6+G1U9EHYBeiHDrmY2sj7NgDjDEJYlRLxGlBRsTIPHzF3tdNw== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-mask@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-mask/-/plugin-mask-0.9.3.tgz#6329ec861269244ab10ab9b3f54b1624c4ce0bab" - integrity sha512-nZ0J62Hly9JtMZctlSDVgnTd8Fg2XGikzAYilSTCjzIRtbXL5Be/qSAZrMfLD3CZ8exTxdlEGRkEJI3RZKXYCw== +"@jimp/plugin-mask@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-mask/-/plugin-mask-0.8.4.tgz#0dfe02a14530c3bddfc258e83bd3c979e53d15ef" + integrity sha512-uqLdRGShHwCd9RHv8bMntTfDNDI2pcEeE7+F868P6PngWLKrzQCpuAyTnK6WK0ZN95fSsgy7TzCoesYk+FchkQ== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-normalize@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-normalize/-/plugin-normalize-0.9.3.tgz#564155032d1b9dc567dbb7427a85606a25427c30" - integrity sha512-0IvgTt4R15QJnoCHvvqlK56zOtCsQV7Mkx757kdNah8uyPGjadTcFBuqCaOMK943X36IIv+o7Ix7yvNUJZt4aw== +"@jimp/plugin-normalize@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-normalize/-/plugin-normalize-0.8.4.tgz#aa2c3131082b6ceef2fb6222323db9f7d837447c" + integrity sha512-+ihgQeVD8syWxw12F5ngUUdtlIcGDqH7hEoHcwVVGOFfaJqR4YBQR4FM3QLFFFdi2X/uK2nGJt9cMh0UaINEgw== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-print@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-print/-/plugin-print-0.9.3.tgz#b4470137312232de9b35eaf412cd753f999c58d8" - integrity sha512-pV6oX5Bhe9O/dbgrotz46Bv6u1M+/n9G0kRUunDjwzXrvON5raBFEJHQDPcTXiqPT25Gc9Ba4/Akfo/Zl6+wgQ== +"@jimp/plugin-print@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-print/-/plugin-print-0.8.4.tgz#c110d6e7632e3c9cf47ce395e36b0f3c1461a9ca" + integrity sha512-Wg5tZI3hW5DG9Caz4wg4ZolS3Lvv4MFAxORPAeWeahDpHs38XZ7ydJ0KR39p2oWJPP0yIFv1fETYpU7BiJPRRw== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" load-bmfont "^1.4.0" -"@jimp/plugin-resize@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-resize/-/plugin-resize-0.9.3.tgz#916abd57c4f9b426984354c77555ade1efda7a82" - integrity sha512-YzqVE8QoDIZpVuI52v+WejwEjEEiJfNFviQfprfm5af7uSSseZgDw1sJ0koqAu+liMSY+Ewp79v2SDrKoJKqtg== +"@jimp/plugin-resize@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-resize/-/plugin-resize-0.8.4.tgz#6690f50c98cfd89ac3682b58ba9623e7c46e0be6" + integrity sha512-z9tumvsQja/YFTSeGvofYLvVws8LZYLYVW8l17hBETzfZQdVEvPOdWKkXqsAsK5uY9m8M5rH7kR8NZbCDVbyzA== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-rotate@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-rotate/-/plugin-rotate-0.9.3.tgz#aa0d674c08726c0ae3ebc7f2adbfca0a927b1d9f" - integrity sha512-kADY2pI3/yMyHbuyvKB4nqPoKf8DPQBU1b4zz2K7SxcwKh1krFf4Fa9mmhhDLoFwuNSy0SPb1JCMUO4BtFCFLA== +"@jimp/plugin-rotate@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-rotate/-/plugin-rotate-0.8.4.tgz#bf3ea70d10123f1372507b74d06bfefb40b3e526" + integrity sha512-PVxpt3DjqaUnHP6Nd3tzZjl4SYe/FYXszGTshtx51AMuvZLnpvekrrclYyc7Dc1Ry3kx3ma6UuLCvmf85hrdmw== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugin-scale@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugin-scale/-/plugin-scale-0.9.3.tgz#427fed7642883c27601aae33c25413980b6a2c50" - integrity sha512-vZaiL5Qc+WrgGEfUe4Y0vG+qbT6pe2TW68/mu124E1tKVcZjHKZUeFN0Wr/hP2myN6nqTYj0/sord2OS/04JpA== +"@jimp/plugin-scale@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugin-scale/-/plugin-scale-0.8.4.tgz#2de9cc80d49f6a36e4177b22e0ab1d4305331c2e" + integrity sha512-PrBTOMJ5n4gbIvRNxWfc1MdgHw4vd5r1UOHRVuc6ZQ9Z/FueBuvIidnz7GBRHbsRm3IjckvsLfEL1nIK0Kqh3A== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" -"@jimp/plugins@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/plugins/-/plugins-0.9.3.tgz#bdff9d49484469c4d74ef47c2708e75773ca22b9" - integrity sha512-KYCSgFGoZBNC0224X5yUnMHCZnCdUVrsu2Yo67o3XZfUgDjO81J+vdzZ0twpPQ6qLLVAP+nQ8hkRV/QzEUstMw== - dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/plugin-blit" "^0.9.3" - "@jimp/plugin-blur" "^0.9.3" - "@jimp/plugin-color" "^0.9.3" - "@jimp/plugin-contain" "^0.9.3" - "@jimp/plugin-cover" "^0.9.3" - "@jimp/plugin-crop" "^0.9.3" - "@jimp/plugin-displace" "^0.9.3" - "@jimp/plugin-dither" "^0.9.3" - "@jimp/plugin-flip" "^0.9.3" - "@jimp/plugin-gaussian" "^0.9.3" - "@jimp/plugin-invert" "^0.9.3" - "@jimp/plugin-mask" "^0.9.3" - "@jimp/plugin-normalize" "^0.9.3" - "@jimp/plugin-print" "^0.9.3" - "@jimp/plugin-resize" "^0.9.3" - "@jimp/plugin-rotate" "^0.9.3" - "@jimp/plugin-scale" "^0.9.3" - core-js "^3.4.1" +"@jimp/plugins@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/plugins/-/plugins-0.8.4.tgz#af24c0686aec327f3abcc50ba5bbae1df2113fb0" + integrity sha512-Vd0oCe0bj7c+crHL6ee178q2c1o50UnbCmc0imHYg7M+pY8S1kl4ubZWwkAg2W96FCarGrm9eqPvCUyAdFOi9w== + dependencies: + "@jimp/plugin-blit" "^0.8.4" + "@jimp/plugin-blur" "^0.8.4" + "@jimp/plugin-color" "^0.8.4" + "@jimp/plugin-contain" "^0.8.4" + "@jimp/plugin-cover" "^0.8.4" + "@jimp/plugin-crop" "^0.8.4" + "@jimp/plugin-displace" "^0.8.4" + "@jimp/plugin-dither" "^0.8.4" + "@jimp/plugin-flip" "^0.8.4" + "@jimp/plugin-gaussian" "^0.8.4" + "@jimp/plugin-invert" "^0.8.4" + "@jimp/plugin-mask" "^0.8.4" + "@jimp/plugin-normalize" "^0.8.4" + "@jimp/plugin-print" "^0.8.4" + "@jimp/plugin-resize" "^0.8.4" + "@jimp/plugin-rotate" "^0.8.4" + "@jimp/plugin-scale" "^0.8.4" + core-js "^2.5.7" timm "^1.6.1" -"@jimp/png@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/png/-/png-0.9.3.tgz#5c1bbb89b32e2332891a13efdb423e87287a8321" - integrity sha512-LJXUemDTSbTGAGEp9hNQH0uTRSB8gYeE6FsfT3M00oZincu6/WzDzl0P8E95rMjNxZqAihdTyOP3+kcrbbqX+w== +"@jimp/png@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/png/-/png-0.8.4.tgz#d150ddaaebafcda83d820390f62a4d3c409acc03" + integrity sha512-DLj260SwQr9ZNhSto1BacXGNRhIQiLNOESPoq5DGjbqiPCmYNxE7CPlXB1BVh0T3AmZBjnZkZORU0Y9wTi3gJw== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.9.3" - core-js "^3.4.1" + "@jimp/utils" "^0.8.4" + core-js "^2.5.7" pngjs "^3.3.3" -"@jimp/tiff@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/tiff/-/tiff-0.9.3.tgz#a4498c0616fb24034f5512b159b75b0aea389e9c" - integrity sha512-w9H6dT+GDHN//Srsv27JhRn7R2byzUahOGfFw7KpIn95jg0ogcxjKTo/RAGQC56sr4U092e4Npl7E85Lt934WQ== +"@jimp/tiff@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/tiff/-/tiff-0.8.4.tgz#bc18c32cef996ad986a92bb7d926d6abd1db9d10" + integrity sha512-SQmf1B/TbCtbwzJReLw/lzGqbeu8MOfT+wkaia0XWS72H6bEW66PTQKhB4/3uzC/Xnmsep1WNQITlwcWdgc36Q== dependencies: - "@babel/runtime" "^7.7.2" - core-js "^3.4.1" + core-js "^2.5.7" utif "^2.0.1" -"@jimp/types@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/types/-/types-0.9.3.tgz#75337245a1a8c7c84a414beca3cfeded338c0ef1" - integrity sha512-hUJKoT2IhnbO/trxNWzN19n8g+p7aKbM1R+71n4wMZnD41PzrVtz+sBBCdB+JCjBJs/i7fJt4d9z0i3Xe8m7Zw== - dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/bmp" "^0.9.3" - "@jimp/gif" "^0.9.3" - "@jimp/jpeg" "^0.9.3" - "@jimp/png" "^0.9.3" - "@jimp/tiff" "^0.9.3" - core-js "^3.4.1" +"@jimp/types@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/types/-/types-0.8.4.tgz#01df00a5adb955cb4ba79df1288408faa3bb40ed" + integrity sha512-BCehQ5hrTOGDGdeROwXOYqgFGAzJPkuXmVJXgMgBoW1YjoGWhXJ5iShaJ/l7DRErrdezoWUdAhTFlV5bJf51dg== + dependencies: + "@jimp/bmp" "^0.8.4" + "@jimp/gif" "^0.8.4" + "@jimp/jpeg" "^0.8.4" + "@jimp/png" "^0.8.4" + "@jimp/tiff" "^0.8.4" + core-js "^2.5.7" timm "^1.6.1" -"@jimp/utils@^0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@jimp/utils/-/utils-0.9.3.tgz#fd7af0d1138febbeacc841be4b802218444ce088" - integrity sha512-9D2Of6BcjYONtl77YfmU2y5aRMLe0/O2e2aQvfCxdNwD33jRdwNdN4i3m73dpiClNquApIjL4nYGhTixA4UstA== +"@jimp/utils@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@jimp/utils/-/utils-0.8.4.tgz#a6bbdc13dba99b95d4cabf0bde87b1bcd2230d25" + integrity sha512-6Cwplao7IgwhFRijMvvyjdV7Sa7Fw71vS1aDsUDCVpi3XHsiLUM+nPTno6OKjzg2z2EufuolWPEvuq/GSte4lA== dependencies: - "@babel/runtime" "^7.7.2" - core-js "^3.4.1" + core-js "^2.5.7" "@mapbox/extent@0.4.0": version "0.4.0" @@ -3149,17 +2597,17 @@ "@types/node" ">=8.9.0" axios "^0.18.0" -"@storybook/addon-actions@^5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-5.2.8.tgz#f63c6e1afb59e94ca1ebc776b7cad9b815e7419e" - integrity sha512-hadk+UaU6upOW0g447RfLRrnXRgE2rjRVk5sT8mVxBMj032NnwUd7ie/BZwy1yg5B8oFtpkgQYwqhPtoO2xBaQ== - dependencies: - "@storybook/addons" "5.2.8" - "@storybook/api" "5.2.8" - "@storybook/client-api" "5.2.8" - "@storybook/components" "5.2.8" - "@storybook/core-events" "5.2.8" - "@storybook/theming" "5.2.8" +"@storybook/addon-actions@^5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-5.2.6.tgz#4fe411fc3bdb1d44058f23fbc8eb8d1bac29d521" + integrity sha512-CwTJPqe3NcEU7oqS5KoiCX9FXYmI2Dyp1Sh6r90JmXZ8B49ZXm6BDLX0gS3TooD6/AcdU8xdBcSvN0CkxQ5QGA== + dependencies: + "@storybook/addons" "5.2.6" + "@storybook/api" "5.2.6" + "@storybook/client-api" "5.2.6" + "@storybook/components" "5.2.6" + "@storybook/core-events" "5.2.6" + "@storybook/theming" "5.2.6" core-js "^3.0.1" fast-deep-equal "^2.0.1" global "^4.3.2" @@ -3176,15 +2624,15 @@ dependencies: global "^4.3.2" -"@storybook/addon-info@^5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-info/-/addon-info-5.2.8.tgz#bf29741c21c16c85f4a7007606e8afa3eb77965c" - integrity sha512-iY8malDF6yayLfpiffMwDXOWeeoXdRbxse1f+kNHK4aVEUXLyh+uiogPhO8dzVDy8dQw1LP9C7xKZe2Dls59kw== +"@storybook/addon-info@^5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/addon-info/-/addon-info-5.2.6.tgz#25c8405ded9e20b1bf3b607d235601b087a73a5f" + integrity sha512-2ct91Zf14Be5HZ77szPpJK4Fvy4D/haQtziCfv9UKZXaBm7Rz9vKPuM3uCcfMywh1eMunhhluhJ/3bxmttHXAw== dependencies: - "@storybook/addons" "5.2.8" - "@storybook/client-logger" "5.2.8" - "@storybook/components" "5.2.8" - "@storybook/theming" "5.2.8" + "@storybook/addons" "5.2.6" + "@storybook/client-logger" "5.2.6" + "@storybook/components" "5.2.6" + "@storybook/theming" "5.2.6" core-js "^3.0.1" global "^4.3.2" jsx-to-string "^1.4.0" @@ -3198,17 +2646,17 @@ react-lifecycles-compat "^3.0.4" util-deprecate "^1.0.2" -"@storybook/addon-knobs@^5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-5.2.8.tgz#e0d03823969921a0da57a329376d03066dd749ee" - integrity sha512-5SAMJj+0pbhCiyNkKjkUxEbM9L/wrOE4HTvM7gvm902fULuKZklb3wV8iiUNRfIPCs6VhmmIhPzXICGjhW5xIg== - dependencies: - "@storybook/addons" "5.2.8" - "@storybook/api" "5.2.8" - "@storybook/client-api" "5.2.8" - "@storybook/components" "5.2.8" - "@storybook/core-events" "5.2.8" - "@storybook/theming" "5.2.8" +"@storybook/addon-knobs@^5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-5.2.6.tgz#28b2c74ea26519fef204915142a03bd7476f247c" + integrity sha512-whEZl6PpUPtOWBhmWlZ11EloxN6ad3WrJk5FyYlg3BcXG/HtlMVogBKdch83SYTT9jhHwbfwKnAng9J3UjgPbQ== + dependencies: + "@storybook/addons" "5.2.6" + "@storybook/api" "5.2.6" + "@storybook/client-api" "5.2.6" + "@storybook/components" "5.2.6" + "@storybook/core-events" "5.2.6" + "@storybook/theming" "5.2.6" "@types/react-color" "^3.0.1" copy-to-clipboard "^3.0.8" core-js "^3.0.1" @@ -3222,13 +2670,13 @@ react-lifecycles-compat "^3.0.4" react-select "^3.0.0" -"@storybook/addon-storyshots@^5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-storyshots/-/addon-storyshots-5.2.8.tgz#1878d0cc7490941cc4006bd3bd96bfd06005e1e3" - integrity sha512-izUQTwzt1I0TdtYn3jv6yFIaW7H48gPW+nehisVXOA+0b0f+IySnC63+q3YcCQcC9cmZ5xxzZNJ1XycrsyVm0A== +"@storybook/addon-storyshots@^5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/addon-storyshots/-/addon-storyshots-5.2.6.tgz#cc94f256cb28e2769a3dd472af420f8e0fcc306f" + integrity sha512-04UX6VXFOrv1o41L8P3mevFYN2H9RU6JCNXfqCJLtD4ZxP5iDoXjF8/0VWLdqCPANe9Ng58r5BnZgNwWPjcGbA== dependencies: "@jest/transform" "^24.9.0" - "@storybook/addons" "5.2.8" + "@storybook/addons" "5.2.6" core-js "^3.0.1" glob "^7.1.3" global "^4.3.2" @@ -3237,29 +2685,29 @@ regenerator-runtime "^0.12.1" ts-dedent "^1.1.0" -"@storybook/addons@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.2.8.tgz#f8bf8bd555b7a69fb1e9a52ab8cdb96384d931ff" - integrity sha512-yAo1N5z/45bNIQP8SD+HVTr7X898bYAtz1EZBrQ6zD8bGamzA2Br06rOLL9xXw29eQhsaVnPlqgDwCS1sTC7aQ== +"@storybook/addons@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.2.6.tgz#c1278137acb3502e068b0b0d07a8371c607e9c02" + integrity sha512-5MF64lsAhIEMxTbVpYROz5Wez595iwSw45yXyP8gWt12d+EmFO5tdy7cYJCxcMuVhDfaCI78tFqS9orr1atVyA== dependencies: - "@storybook/api" "5.2.8" - "@storybook/channels" "5.2.8" - "@storybook/client-logger" "5.2.8" - "@storybook/core-events" "5.2.8" + "@storybook/api" "5.2.6" + "@storybook/channels" "5.2.6" + "@storybook/client-logger" "5.2.6" + "@storybook/core-events" "5.2.6" core-js "^3.0.1" global "^4.3.2" util-deprecate "^1.0.2" -"@storybook/api@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/api/-/api-5.2.8.tgz#21f03df8041114eb929bd10b570a17f266568b7f" - integrity sha512-rFrPtTFDIPQoicLwq1AVsOvZNTUKnjD1w/NX1kKcyuWLL9BcOkU3YNLBlliGBg2JX/yS+fJKMyKk4NMzNBCZCg== +"@storybook/api@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-5.2.6.tgz#43d3c20b90e585e6c94b36e29845d39704ae2135" + integrity sha512-X/di44/SAL68mD6RHTX2qdWwhjRW6BgcfPtu0dMd38ErB3AfsfP4BITXs6kFOeSM8kWiaQoyuw0pOBzA8vlYug== dependencies: - "@storybook/channels" "5.2.8" - "@storybook/client-logger" "5.2.8" - "@storybook/core-events" "5.2.8" - "@storybook/router" "5.2.8" - "@storybook/theming" "5.2.8" + "@storybook/channels" "5.2.6" + "@storybook/client-logger" "5.2.6" + "@storybook/core-events" "5.2.6" + "@storybook/router" "5.2.6" + "@storybook/theming" "5.2.6" core-js "^3.0.1" fast-deep-equal "^2.0.1" global "^4.3.2" @@ -3273,35 +2721,35 @@ telejson "^3.0.2" util-deprecate "^1.0.2" -"@storybook/channel-postmessage@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-5.2.8.tgz#7a84869ce0fc270c3b5dcd7fa4ed798b6055816f" - integrity sha512-RS3iDW1kpfODN+kBq3youn+KtLqHslZ4m7mTlOL80BUHKb4YkrA1lVkzpy1kVMWBU523pyDVQUVXr+M8y3iVug== +"@storybook/channel-postmessage@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-5.2.6.tgz#60aaef0e80300c9812a571ca3ce0f28e2c404f04" + integrity sha512-y+63wWiEc/Q4s4MZ3KJ//5A8j5VLufxuLvPxwv9FuS4z8lmN0fqeGJn857qIlFGbZhzsQaoRdmfsCQpBBgUneg== dependencies: - "@storybook/channels" "5.2.8" - "@storybook/client-logger" "5.2.8" + "@storybook/channels" "5.2.6" + "@storybook/client-logger" "5.2.6" core-js "^3.0.1" global "^4.3.2" telejson "^3.0.2" -"@storybook/channels@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-5.2.8.tgz#79a99ad85dcacb688073c22340c5b7d16b801202" - integrity sha512-mFwQec27QSrqcl+IH0xA+4jfoEqC4m1G99LBHt/aTDjLZXclX1A470WqeZCp7Gx4OALpaPEVTaaaKPbiKz4C6w== +"@storybook/channels@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-5.2.6.tgz#e2837508864dc4d5b5e03f078886f0ce113762ea" + integrity sha512-/UsktYsXuvb1efjVPCEivhh5ywRhm7hl73pQnpJLJHRqyLMM2I5nGPFELTTNuU9yWy7sP9QL5gRqBBPe1sqjZQ== dependencies: core-js "^3.0.1" -"@storybook/client-api@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-5.2.8.tgz#1de791f7888442287f848e5f544eb883c5edc0da" - integrity sha512-OCKhZ+2sS3ot0ZV48nD79BWVzvvdMjUFYl0073ps5q+1+TLic1AlNmH0Sb5/9NrYXNV86v3VrM2jUbGsKe1qyw== - dependencies: - "@storybook/addons" "5.2.8" - "@storybook/channel-postmessage" "5.2.8" - "@storybook/channels" "5.2.8" - "@storybook/client-logger" "5.2.8" - "@storybook/core-events" "5.2.8" - "@storybook/router" "5.2.8" +"@storybook/client-api@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-5.2.6.tgz#5760cb4302d82ce9210a63f3f55b1e05f04759c1" + integrity sha512-upynf4ER2fkThNnE+mBlfRFFJxTiOh60fho1ODFcBun9BbvRD2wOHLvw7+WigIhb99HM20vk8f2dhv3I5Udzlg== + dependencies: + "@storybook/addons" "5.2.6" + "@storybook/channel-postmessage" "5.2.6" + "@storybook/channels" "5.2.6" + "@storybook/client-logger" "5.2.6" + "@storybook/core-events" "5.2.6" + "@storybook/router" "5.2.6" common-tags "^1.8.0" core-js "^3.0.1" eventemitter3 "^4.0.0" @@ -3310,23 +2758,22 @@ lodash "^4.17.15" memoizerific "^1.11.3" qs "^6.6.0" - stable "^0.1.8" util-deprecate "^1.0.2" -"@storybook/client-logger@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-5.2.8.tgz#5affe2f9dbbee374721fd2e8729116f5ac39c779" - integrity sha512-+oVSEJdeh7TQ1Bhanb3mCr7fc3Bug3+K79abZ28J45Ub5x4L/ZVClj1xMgUsJs30BZ5FB8vhdgH6TQb0NSxR4A== +"@storybook/client-logger@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-5.2.6.tgz#cfc4536e9b724b086f7509c2bb34c221016713c9" + integrity sha512-hJvPD267cCwLIRMOISjDH8h9wbwOcXIJip29UlJbU9iMtZtgE+YelmlpmZJvqcDfUiXWWrOh7tP76mj8EAfwIQ== dependencies: core-js "^3.0.1" -"@storybook/components@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-5.2.8.tgz#f5d4a06ba4ba8c700b2d962deae182105b72fb99" - integrity sha512-h9l/LAMaj+emUCOyY/+ETy/S3P0npwQU280J88uL4O9XJALJ72EKfyttBCvMLvpM50E+fAPeDzuYn0t5qzGGxg== +"@storybook/components@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-5.2.6.tgz#cddb60227720aea7cae34fe782d0370bcdbd4005" + integrity sha512-C7OS90bZ1ZvxlWUZ3B2MPFFggqAtUo7X8DqqS3IwsuDUiK9dD/KS0MwPgOuFDnOTW1R5XqmQd/ylt53w3s/U5g== dependencies: - "@storybook/client-logger" "5.2.8" - "@storybook/theming" "5.2.8" + "@storybook/client-logger" "5.2.6" + "@storybook/theming" "5.2.6" "@types/react-syntax-highlighter" "10.1.0" "@types/react-textarea-autosize" "^4.3.3" core-js "^3.0.1" @@ -3345,32 +2792,32 @@ react-textarea-autosize "^7.1.0" simplebar-react "^1.0.0-alpha.6" -"@storybook/core-events@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-5.2.8.tgz#93fc458ea0820ff1409d268b0fe51abba200f5a4" - integrity sha512-NkQKC5doO/YL9gsO61bqaxgveKktkiJWZ3XyyhL1ZebgnO9wTlrU+i9b5aX73Myk1oxbicQw9KcwDGYk0qFuNQ== +"@storybook/core-events@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-5.2.6.tgz#34c9aae256e7e5f4a565b81f1e77dda8bccc6752" + integrity sha512-W8kLJ7tc0aAxs11CPUxUOCReocKL4MYGyjTg8qwk0USLzPUb/FUQWmhcm2ilFz6Nz8dXLcKrXdRVYTmiMsgAeg== dependencies: core-js "^3.0.1" -"@storybook/core@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-5.2.8.tgz#3f6ddbacc705c1893deb15582c3a0a1ecd882cd1" - integrity sha512-P1Xx4setLBESPgS5KgL7Jskf5Q6fRa3ApwPt+ocjDoSDGCvsV7cUEpAp09U65u+89e5K4nQxvaZouhknFQBc1A== +"@storybook/core@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-5.2.6.tgz#60c092607158d7d28db59f7e67da4f7e12703fb2" + integrity sha512-q7Ful7TCm9nmjgLsJFqIwVv395NlaOXgGajyaQCQlCKB2V+jgs7GDmdCNNdWAOue4eAsFU6wQSP9lWtq0yzK4w== dependencies: - "@babel/plugin-proposal-class-properties" "^7.7.0" - "@babel/plugin-proposal-object-rest-spread" "^7.6.2" + "@babel/plugin-proposal-class-properties" "^7.3.3" + "@babel/plugin-proposal-object-rest-spread" "^7.3.2" "@babel/plugin-syntax-dynamic-import" "^7.2.0" - "@babel/plugin-transform-react-constant-elements" "^7.6.3" - "@babel/preset-env" "^7.7.1" - "@storybook/addons" "5.2.8" - "@storybook/channel-postmessage" "5.2.8" - "@storybook/client-api" "5.2.8" - "@storybook/client-logger" "5.2.8" - "@storybook/core-events" "5.2.8" - "@storybook/node-logger" "5.2.8" - "@storybook/router" "5.2.8" - "@storybook/theming" "5.2.8" - "@storybook/ui" "5.2.8" + "@babel/plugin-transform-react-constant-elements" "^7.2.0" + "@babel/preset-env" "^7.4.5" + "@storybook/addons" "5.2.6" + "@storybook/channel-postmessage" "5.2.6" + "@storybook/client-api" "5.2.6" + "@storybook/client-logger" "5.2.6" + "@storybook/core-events" "5.2.6" + "@storybook/node-logger" "5.2.6" + "@storybook/router" "5.2.6" + "@storybook/theming" "5.2.6" + "@storybook/ui" "5.2.6" airbnb-js-shims "^1 || ^2" ansi-to-html "^0.6.11" autoprefixer "^9.4.9" @@ -3426,10 +2873,10 @@ webpack-dev-middleware "^3.7.0" webpack-hot-middleware "^2.25.0" -"@storybook/node-logger@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-5.2.8.tgz#4a3df21d731014d54b9ca53d5b9a72dd350bb075" - integrity sha512-3TK5mx6VWbfJO+WUrqwPhTbTQ4qESTnwJY/02xPzOhvuC6tIG1QOxzi+Rq6rFlwxTpUuWh6iyDYnGIqFFQywkA== +"@storybook/node-logger@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-5.2.6.tgz#e353aff14375bef9e922c217a0afb50f93e2ceb1" + integrity sha512-Z3mn9CUSiG7kR2OBoz4lNeoeBS094h5d9wufZSp5S+M47L6KEXmTgNcuePKj+t8Z8KT/Ph8B63bjChseKp3DNw== dependencies: chalk "^2.4.2" core-js "^3.0.1" @@ -3437,17 +2884,17 @@ pretty-hrtime "^1.0.3" regenerator-runtime "^0.12.1" -"@storybook/react@^5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-5.2.8.tgz#8d44c2d34caa1d7d748ec1fc9cf0fe2a88b001f9" - integrity sha512-T1DoWpSz33vaGx85Dh7q2KYetg7dQyiYhuOnZm2WxZTFZOw1jP62si53JGFp0PKxnT6iOBLHo3v2QkRkjt2mdQ== +"@storybook/react@^5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-5.2.6.tgz#e61c0ed184add9e715191649ddb995eead756a90" + integrity sha512-yzhxxuoUx4jwn+PymU5wemzLb9ryXD9Y2Dv5kipCDkUS4cqDJwKcVO8tyhMigFUGTHREmJTmAESCKKPDR45SiQ== dependencies: - "@babel/plugin-transform-react-constant-elements" "^7.6.3" + "@babel/plugin-transform-react-constant-elements" "^7.2.0" "@babel/preset-flow" "^7.0.0" - "@babel/preset-react" "^7.7.0" - "@storybook/addons" "5.2.8" - "@storybook/core" "5.2.8" - "@storybook/node-logger" "5.2.8" + "@babel/preset-react" "^7.0.0" + "@storybook/addons" "5.2.6" + "@storybook/core" "5.2.6" + "@storybook/node-logger" "5.2.6" "@svgr/webpack" "^4.0.3" "@types/webpack-env" "^1.13.7" babel-plugin-add-react-displayname "^0.0.5" @@ -3465,10 +2912,10 @@ semver "^6.0.0" webpack "^4.33.0" -"@storybook/router@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/router/-/router-5.2.8.tgz#d7de2d401701857c033e28560c30e16512f7f72f" - integrity sha512-wnbyKESUMyv9fwo9W+n4Fev/jXylB8whpjtHrOttjguUOYX1zGSHdwNI66voPetbtVLxUeHyJteJwdyRDSirJg== +"@storybook/router@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-5.2.6.tgz#5180d3785501699283c6c3717986c877f84fead5" + integrity sha512-/FZd3fYg5s2QzOqSIP8UMOSnCIFFIlli/jKlOxvm3WpcpxgwQOY4lfHsLO+r9ThCLs2UvVg2R/HqGrOHqDFU7A== dependencies: "@reach/router" "^1.2.1" "@types/reach__router" "^1.2.3" @@ -3478,14 +2925,14 @@ memoizerific "^1.11.3" qs "^6.6.0" -"@storybook/theming@5.2.8", "@storybook/theming@^5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-5.2.8.tgz#a4c9e0e9a5789c1aa71e4fcb7a8ee86efe3dadcf" - integrity sha512-rGb66GkXb0jNJMH8UQ3Ru4FL+m1x0+UdxM8a8HSE/qb1GMv2qOwjVETfAL6nVL9u6ZmrtbhHoero4f6xDwZdRg== +"@storybook/theming@5.2.6", "@storybook/theming@^5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-5.2.6.tgz#e04170b3e53dcfc791b2381c8a39192ae88cd291" + integrity sha512-Xa9R/H8DDgmvxsCHloJUJ2d9ZQl80AeqHrL+c/AKNpx05s9lV74DcinusCf0kz72YGUO/Xt1bAjuOvLnAaS8Gw== dependencies: "@emotion/core" "^10.0.14" "@emotion/styled" "^10.0.14" - "@storybook/client-logger" "5.2.8" + "@storybook/client-logger" "5.2.6" common-tags "^1.8.0" core-js "^3.0.1" deep-object-diff "^1.1.0" @@ -3496,19 +2943,19 @@ prop-types "^15.7.2" resolve-from "^5.0.0" -"@storybook/ui@5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-5.2.8.tgz#da8afca9eb29a40ef3ddc6a9f6e76d7a3344f2ef" - integrity sha512-7t1ARBfylhEsLmGsZBUCj1Wf1oAgCDDrf7fi+Fhdg5Rr16CMoBbe24Gv/mPYv01/pUDhGodxzltKGX5x0Hto2w== - dependencies: - "@storybook/addons" "5.2.8" - "@storybook/api" "5.2.8" - "@storybook/channels" "5.2.8" - "@storybook/client-logger" "5.2.8" - "@storybook/components" "5.2.8" - "@storybook/core-events" "5.2.8" - "@storybook/router" "5.2.8" - "@storybook/theming" "5.2.8" +"@storybook/ui@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-5.2.6.tgz#33df2f2e03d9cf81dc52928a0dc4db280ee8f56a" + integrity sha512-jT3PtpEsTqnESO0U8BotC+5P971Xqy0s2leSZcgU9PNe4Eb7NaxypSULOulPgPAx1JOmMipUBdK54PP/nyudkA== + dependencies: + "@storybook/addons" "5.2.6" + "@storybook/api" "5.2.6" + "@storybook/channels" "5.2.6" + "@storybook/client-logger" "5.2.6" + "@storybook/components" "5.2.6" + "@storybook/core-events" "5.2.6" + "@storybook/router" "5.2.6" + "@storybook/theming" "5.2.6" copy-to-clipboard "^3.0.8" core-js "^3.0.1" core-js-pure "^3.0.1" @@ -3783,10 +3230,10 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" -"@types/babel__core@^7.1.3": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30" - integrity sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA== +"@types/babel__core@^7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f" + integrity sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -4018,10 +3465,10 @@ resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== -"@types/eslint@^6.1.3": - version "6.1.3" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-6.1.3.tgz#ec2a66e445a48efaa234020eb3b6e8f06afc9c61" - integrity sha512-llYf1QNZaDweXtA7uY6JczcwHmFwJL9TpK3E6sY0B18l6ulDT6VWNMAdEjYccFHiDfxLPxffd8QmSDV4QUUspA== +"@types/eslint@^6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-6.1.2.tgz#297ece0f3815f93d699b18bdade5e6bee747284f" + integrity sha512-t+smTKg1e9SshiIOI94Zi+Lvo3bfHF20MuKP8w3VGWdrS1dYm33A7xrSoyy9FQv6oE2TwYqEXVJ50I0or8+FWQ== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -4187,7 +3634,7 @@ resolved "https://registry.yarnpkg.com/@types/hoek/-/hoek-4.1.3.tgz#d1982d48fb0d2a0e5d7e9d91838264d8e428d337" integrity sha1-0ZgtSPsNKg5dfp2Rg4Jk2OQo0zc= -"@types/hoist-non-react-statics@*": +"@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== @@ -4380,7 +3827,7 @@ dependencies: "@types/linkify-it" "*" -"@types/memoize-one@^4.1.1": +"@types/memoize-one@^4.1.0": version "4.1.1" resolved "https://registry.yarnpkg.com/@types/memoize-one/-/memoize-one-4.1.1.tgz#41dd138a4335b5041f7d8fc038f9d593d88b3369" integrity sha512-+9djKUUn8hOyktLCfCy4hLaIPgDNovaU36fsnZe9trFHr6ddlbIn2q0SEsnkCkNR+pBWEU440Molz/+Mpyf+gQ== @@ -4814,13 +4261,20 @@ "@types/superagent" "*" "@types/supertest" "*" -"@types/supertest@*", "@types/supertest@^2.0.8": +"@types/supertest@*": version "2.0.8" resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.8.tgz#23801236e2b85204ed771a8e7c40febba7da2bda" integrity sha512-wcax7/ip4XSSJRLbNzEIUVy2xjcBIZZAuSd2vtltQfRK7kxhx5WMHbLHkYdxN3wuQCrwpYrg86/9byDjPXoGMA== dependencies: "@types/superagent" "*" +"@types/supertest@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.5.tgz#18d082a667eaed22759be98f4923e0061ae70c62" + integrity sha512-orl732spRnz4+Bqwk1OnXkJUX2YhiGvMUyfn8VTGXqMZSk6jrMhBq7IuoSXMyBrpsCV5eSddPiC3S0iIafYxsQ== + dependencies: + "@types/superagent" "*" + "@types/tar-fs@^1.16.1": version "1.16.1" resolved "https://registry.yarnpkg.com/@types/tar-fs/-/tar-fs-1.16.1.tgz#6e3fba276c173e365ae91e55f7b797a0e64298e5" @@ -5000,24 +4454,24 @@ resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg== -"@typescript-eslint/eslint-plugin@^2.10.0": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.10.0.tgz#c4cb103275e555e8a7e9b3d14c5951eb6d431e70" - integrity sha512-rT51fNLW0u3fnDGnAHVC5nu+Das+y2CpW10yqvf6/j5xbuUV3FxA3mBaIbM24CXODXjbgUznNb4Kg9XZOUxKAw== +"@typescript-eslint/eslint-plugin@^2.12.0": + version "2.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.12.0.tgz#0da7cbca7b24f4c6919e9eb31c704bfb126f90ad" + integrity sha512-1t4r9rpLuEwl3hgt90jY18wJHSyb0E3orVL3DaqwmpiSDHmHiSspVsvsFF78BJ/3NNG3qmeso836jpuBWYziAA== dependencies: - "@typescript-eslint/experimental-utils" "2.10.0" + "@typescript-eslint/experimental-utils" "2.12.0" eslint-utils "^1.4.3" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@2.10.0": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.10.0.tgz#8db1656cdfd3d9dcbdbf360b8274dea76f0b2c2c" - integrity sha512-FZhWq6hWWZBP76aZ7bkrfzTMP31CCefVIImrwP3giPLcoXocmLTmr92NLZxuIcTL4GTEOE33jQMWy9PwelL+yQ== +"@typescript-eslint/experimental-utils@2.12.0": + version "2.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.12.0.tgz#e0a76ffb6293e058748408a191921e453c31d40d" + integrity sha512-jv4gYpw5N5BrWF3ntROvCuLe1IjRenLy5+U57J24NbPGwZFAjhnM45qpq0nDH1y/AZMb3Br25YiNVwyPbz6RkA== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.10.0" + "@typescript-eslint/typescript-estree" "2.12.0" eslint-scope "^5.0.0" "@typescript-eslint/experimental-utils@^1.13.0": @@ -5029,14 +4483,14 @@ "@typescript-eslint/typescript-estree" "1.13.0" eslint-scope "^4.0.0" -"@typescript-eslint/parser@^2.10.0": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.10.0.tgz#24b2e48384ab6d5a6121e4c4faf8892c79657ad3" - integrity sha512-wQNiBokcP5ZsTuB+i4BlmVWq6o+oAhd8en2eSm/EE9m7BgZUIfEeYFd6z3S+T7bgNuloeiHA1/cevvbBDLr98g== +"@typescript-eslint/parser@^2.12.0": + version "2.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.12.0.tgz#393f1604943a4ca570bb1a45bc8834e9b9158884" + integrity sha512-lPdkwpdzxEfjI8TyTzZqPatkrswLSVu4bqUgnB03fHSOwpC7KSerPgJRgIAf11UGNf7HKjJV6oaPZI4AghLU6g== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.10.0" - "@typescript-eslint/typescript-estree" "2.10.0" + "@typescript-eslint/experimental-utils" "2.12.0" + "@typescript-eslint/typescript-estree" "2.12.0" eslint-visitor-keys "^1.1.0" "@typescript-eslint/typescript-estree@1.13.0": @@ -5047,10 +4501,10 @@ lodash.unescape "4.0.1" semver "5.5.0" -"@typescript-eslint/typescript-estree@2.10.0": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.10.0.tgz#89cdabd5e8c774e9d590588cb42fb9afd14dcbd9" - integrity sha512-oOYnplddQNm/LGVkqbkAwx4TIBuuZ36cAQq9v3nFIU9FmhemHuVzAesMSXNQDdAzCa5bFgCrfD3JWhYVKlRN2g== +"@typescript-eslint/typescript-estree@2.12.0": + version "2.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.12.0.tgz#bd9e547ccffd17dfab0c3ab0947c80c8e2eb914c" + integrity sha512-rGehVfjHEn8Frh9UW02ZZIfJs6SIIxIu/K1bbci8rFfDE/1lQ8krIJy5OXOV3DVnNdDPtoiPOdEANkLMrwXbiQ== dependencies: debug "^4.1.1" eslint-visitor-keys "^1.1.0" @@ -5320,6 +4774,11 @@ accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" +acorn-dynamic-import@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948" + integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw== + acorn-globals@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" @@ -5350,7 +4809,7 @@ acorn-jsx@^3.0.0: dependencies: acorn "^3.0.4" -acorn-jsx@^5.1.0: +acorn-jsx@^5.0.2: version "5.1.0" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== @@ -5394,7 +4853,7 @@ acorn@^6.0.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== -acorn@^6.2.1: +acorn@^6.0.5, acorn@^6.2.1: version "6.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== @@ -6522,6 +5981,13 @@ async@1.x, async@^1.4.2, async@^1.5.2, async@~1.5.2: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= +async@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.4.0.tgz#4990200f18ea5b837c2cc4f8c031a6985c385611" + integrity sha1-SZAgDxjqW4N8LMT4wDGmmFw4VhE= + dependencies: + lodash "^4.14.0" + async@2.6.1, async@^2.5.0, async@^2.6.0, async@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" @@ -6536,7 +6002,7 @@ async@^2.0.0, async@^2.1.4: dependencies: lodash "^4.14.0" -async@^2.6.2, async@^2.6.3: +async@^2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== @@ -6797,13 +6263,13 @@ babel-plugin-emotion@^10.0.22, babel-plugin-emotion@^10.0.23: find-root "^1.1.0" source-map "^0.5.7" -babel-plugin-filter-imports@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-filter-imports/-/babel-plugin-filter-imports-4.0.0.tgz#068f8da15236a96a9602c36dc6f4a6eeca70a4f4" - integrity sha512-jDLlxI8QnfKd7PtieH6pl4tZJzymzfCDCPGdTq/grgbiYAikwDPp/oL0IlFJn0HQjLpcLkyYhPKkUVneRESw5w== +babel-plugin-filter-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-filter-imports/-/babel-plugin-filter-imports-3.0.0.tgz#a849683837ad29960da17492fb32789ab6b09a11" + integrity sha512-p/chjzVTgCxUqyLM0q/pfWVZS7IJTwGQMwNg0LOvuQpKiTftQgZDtkGB8XvETnUw19rRcL7bJCTopSwibTN2tA== dependencies: - "@babel/types" "^7.7.2" - lodash "^4.17.15" + "@babel/types" "^7.4.0" + lodash "^4.17.11" babel-plugin-istanbul@^5.1.0: version "5.1.1" @@ -6956,10 +6422,10 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= -babel-plugin-transform-define@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-2.0.0.tgz#79c3536635f899aabaf830b194b25519465675a4" - integrity sha512-0dv5RNRUlUKxGYIIErl01lpvi8b7W2R04Qcl1mCj70ahwZcgiklfXnFlh4FGnRh6aayCfSZKdhiMryVzcq5Dmg== +babel-plugin-transform-define@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-1.3.1.tgz#b21b7bad3b84cf8e3f07cdc8c660b99cbbc01213" + integrity sha512-JXZ1xE9jIbKCGYZ4wbSMPSI5mdS4DRLi5+SkTHgZqWn5YIf/EucykkzUsPmzJlpkX8fsMVdLnA5vt/LvT97Zbg== dependencies: lodash "^4.17.11" traverse "0.6.6" @@ -7411,11 +6877,6 @@ bluebird@3.5.5, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== -bluebird@3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de" - integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg== - bluebird@^3.3.0, bluebird@^3.3.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -7795,15 +7256,6 @@ browserslist@^4.6.0, browserslist@^4.6.3: electron-to-chromium "^1.3.188" node-releases "^1.1.25" -browserslist@^4.8.2: - version "4.8.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.2.tgz#b45720ad5fbc8713b7253c20766f701c9a694289" - integrity sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA== - dependencies: - caniuse-lite "^1.0.30001015" - electron-to-chromium "^1.3.322" - node-releases "^1.1.42" - bser@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" @@ -7937,7 +7389,27 @@ cac@^4.3.4: string-width "^2.1.1" text-table "^0.2.0" -cacache@^12.0.2, cacache@^12.0.3: +cacache@^11.3.3: + version "11.3.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.3.tgz#8bd29df8c6a718a6ebd2d010da4d7972ae3bbadc" + integrity sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cacache@^12.0.2: version "12.0.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw== @@ -7958,7 +7430,7 @@ cacache@^12.0.2, cacache@^12.0.3: unique-filename "^1.1.1" y18n "^4.0.0" -cacache@^13.0.1: +cacache@^13.0.0: version "13.0.1" resolved "https://registry.yarnpkg.com/cacache/-/cacache-13.0.1.tgz#a8000c21697089082f85287a1aec6e382024a71c" integrity sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w== @@ -8163,6 +7635,11 @@ camelcase@^5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== +camelcase@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.2.0.tgz#e7522abda5ed94cc0489e1b8466610e88404cf45" + integrity sha512-IXFsBS2pC+X0j0N/GE7Dm7j3bsEBp+oTpb7F50dwEVX7rf3IgwO9XatnegTsDtniKCUtEJH4fSU6Asw7uoVLfQ== + camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -8178,20 +7655,10 @@ can-use-dom@^0.1.0: resolved "https://registry.yarnpkg.com/can-use-dom/-/can-use-dom-0.1.0.tgz#22cc4a34a0abc43950f42c6411024a3f6366b45a" integrity sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo= -caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000981: - version "1.0.30000983" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000983.tgz#ab3c70061ca2a3467182a10ac75109b199b647f8" - integrity sha512-/llD1bZ6qwNkt41AsvjsmwNOoA4ZB+8iqmf5LVyeSXuBODT/hAMFNVOh84NdUzoiYiSKqo5vQ3ZzeYHSi/olDQ== - -caniuse-lite@^1.0.30000984: - version "1.0.30000989" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz#b9193e293ccf7e4426c5245134b8f2a56c0ac4b9" - integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw== - -caniuse-lite@^1.0.30001015: - version "1.0.30001015" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001015.tgz#15a7ddf66aba786a71d99626bc8f2b91c6f0f5f0" - integrity sha512-/xL2AbW/XWHNu1gnIrO8UitBGoFthcsDgU9VLK1/dpsoxbaD5LscHozKze05R6WLsBvLhqv78dAPozMFQBYLbQ== +caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000984: + version "1.0.30001016" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001016.tgz#16ea48d7d6e8caf3cad3295c2d746fe38c4e7f66" + integrity sha512-yYQ2QfotceRiH4U+h1Us86WJXtVHDmy3nEKIdYPsZCYnOV5/tMgGbmoIlrMzmh2VXlproqYtVaKeGDBkMZifFA== capture-exit@^2.0.0: version "2.0.0" @@ -8495,10 +7962,10 @@ chokidar@2.1.2, chokidar@^2.0.2, chokidar@^2.0.3, chokidar@^2.0.4: optionalDependencies: fsevents "^1.2.7" -chokidar@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" - integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== +chokidar@3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.2.1.tgz#4634772a1924512d990d4505957bf3a510611387" + integrity sha512-/j5PPkb5Feyps9e+jo07jUZGvkB5Aj953NrI4s8xSVScrAo/RHeILrtdb4uzR7N6aaFFxxJ+gt8mA8HfNpw76w== dependencies: anymatch "~3.1.1" braces "~3.0.2" @@ -8506,9 +7973,9 @@ chokidar@3.3.0: is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.2.0" + readdirp "~3.1.3" optionalDependencies: - fsevents "~2.1.1" + fsevents "~2.1.0" chokidar@^2.0.0, chokidar@^2.1.2, chokidar@^2.1.8: version "2.1.8" @@ -8544,7 +8011,7 @@ chroma-js@^1.4.1: resolved "https://registry.yarnpkg.com/chroma-js/-/chroma-js-1.4.1.tgz#eb2d9c4d1ff24616be84b35119f4d26f8205f134" integrity sha512-jTwQiT859RTFN/vIf7s+Vl/Z2LcMrvMv3WUFmd/4u76AdlFC0NTNgqEEFPcRiHmAswPsMiQEDZLM8vX8qXpZNQ== -chrome-trace-event@^1.0.2: +chrome-trace-event@^1.0.0, chrome-trace-event@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== @@ -9096,11 +8563,6 @@ commander@^3.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.0.tgz#0641ea00838c7a964627f04cddc336a2deddd60a" integrity sha512-pl3QrGOBa9RZaslQiqnnKX2J068wcQw7j9AIaBQ9/JEp5RY6je4jKTImg0Bd+rpoONSe7GUFSgkxLeo17m3Pow== -commander@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.0.1.tgz#b67622721785993182e807f4883633e6401ba53c" - integrity sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA== - commander@~2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" @@ -9407,15 +8869,8 @@ convert-source-map@1.X, convert-source-map@^1.1.0, convert-source-map@^1.4.0, co convert-source-map@^1.5.1, convert-source-map@^1.6.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" - integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== - dependencies: - safe-buffer "~5.1.1" - -convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" + integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== dependencies: safe-buffer "~5.1.1" @@ -9487,12 +8942,12 @@ copy-to-clipboard@^3.2.0: dependencies: toggle-selection "^1.0.6" -copy-webpack-plugin@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz#5481a03dea1123d88a988c6ff8b78247214f0b88" - integrity sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg== +copy-webpack-plugin@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.0.4.tgz#c78126f604e24f194c6ec2f43a64e232b5d43655" + integrity sha512-YBuYGpSzoCHSSDGyHy6VJ7SHojKp6WHT4D7ItcQFNAYx2hrwkMe56e97xfVR0/ovDuMTrMffXUiltvQljtAGeg== dependencies: - cacache "^12.0.3" + cacache "^11.3.3" find-cache-dir "^2.1.0" glob-parent "^3.1.0" globby "^7.1.1" @@ -9500,9 +8955,9 @@ copy-webpack-plugin@^5.1.1: loader-utils "^1.2.3" minimatch "^3.0.4" normalize-path "^3.0.0" - p-limit "^2.2.1" + p-limit "^2.2.0" schema-utils "^1.0.0" - serialize-javascript "^2.1.2" + serialize-javascript "^1.7.0" webpack-log "^2.0.0" core-js-compat@^3.1.1: @@ -9514,14 +8969,6 @@ core-js-compat@^3.1.1: core-js-pure "3.1.3" semver "^6.1.0" -core-js-compat@^3.4.7: - version "3.5.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.5.0.tgz#5a11a619a9e9dd2dcf1c742b2060bc4a2143e5b6" - integrity sha512-E7iJB72svRjJTnm9HDvujzNVMCm3ZcDYEedkJ/sDTNsy/0yooCd9Cg7GSzE7b4e0LfIkjijdB1tqg0pGwxWeWg== - dependencies: - browserslist "^4.8.2" - semver "^6.3.0" - core-js-pure@3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.1.3.tgz#4c90752d5b9471f641514f3728f51c1e0783d0b5" @@ -9542,16 +8989,11 @@ core-js@^2.2.0, core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.1, core-js@^2.5.3, resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== -core-js@^3.0.1, core-js@^3.0.4: +core-js@^3.0.1, core-js@^3.0.4, core-js@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.2.1.tgz#cd41f38534da6cc59f7db050fe67307de9868b09" integrity sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw== -core-js@^3.4.1, core-js@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.5.0.tgz#66df8e49be4bd775e6f952a9d083b756ad41c1ed" - integrity sha512-Ifh3kj78gzQ7NAoJXeTu+XwzDld0QRIwjBLRqAMhuLhP3d2Av5wmgE9ycfnvK6NAEjTkQ1sDPeoEZAWO3Hx1Uw== - core-util-is@1.0.2, core-util-is@^1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -9885,23 +9327,22 @@ css-in-js-utils@^2.0.0: hyphenate-style-name "^1.0.2" isobject "^3.0.1" -css-loader@3.3.2, css-loader@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.3.2.tgz#41b2086528aa4fbf8c0692e874bc14f081129b21" - integrity sha512-4XSiURS+YEK2fQhmSaM1onnUm0VKWNf6WWBYjkp9YbSDGCBTVZ5XOM6Gkxo8tLgQlzkZOBJvk9trHlDk4gjEYg== +css-loader@2.1.1, css-loader@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" + integrity sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w== dependencies: - camelcase "^5.3.1" - cssesc "^3.0.0" - icss-utils "^4.1.1" + camelcase "^5.2.0" + icss-utils "^4.1.0" loader-utils "^1.2.3" normalize-path "^3.0.0" - postcss "^7.0.23" + postcss "^7.0.14" postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.2" - postcss-modules-scope "^2.1.1" - postcss-modules-values "^3.0.0" - postcss-value-parser "^4.0.2" - schema-utils "^2.6.0" + postcss-modules-local-by-default "^2.0.6" + postcss-modules-scope "^2.1.0" + postcss-modules-values "^2.0.0" + postcss-value-parser "^3.3.0" + schema-utils "^1.0.0" css-loader@^3.0.0: version "3.2.0" @@ -10495,7 +9936,7 @@ debug@3.1.0, debug@=3.1.0, debug@~3.1.0: dependencies: ms "2.0.0" -debug@3.2.6, debug@3.X, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: +debug@3.2.6, debug@3.X, debug@^3.1.0, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -10509,7 +9950,7 @@ debug@4.1.0: dependencies: ms "^2.1.1" -debug@4.1.1, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -11509,11 +10950,6 @@ electron-to-chromium@^1.3.191: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.246.tgz#38c30a380398b293f39a19d4346f18e2cb376b72" integrity sha512-CzR7VM16UmZQVgd5I5qu/rx0e67l6FF17rpJD2kRFX9n1ygHFIS+TV9DO55MSZKBGVuQ0Ph1JLLTFEReCKU6nQ== -electron-to-chromium@^1.3.322: - version "1.3.322" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz#a6f7e1c79025c2b05838e8e344f6e89eb83213a8" - integrity sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA== - elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" @@ -11657,9 +11093,9 @@ enhanced-resolve@4.1.0, enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: memory-fs "^0.4.0" tapable "^1.0.0" -enhanced-resolve@^0.9.1: +enhanced-resolve@~0.9.0: version "0.9.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" integrity sha1-TW5omzcl+GCQknzMhs2fFjW4ni4= dependencies: graceful-fs "^4.1.2" @@ -11794,6 +11230,13 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^1.3.5: + version "1.3.6" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-1.3.6.tgz#e0e73b93e417138d1cd7c0b746b1a4a14854c292" + integrity sha1-4Oc7k+QXE40c18C3RrGkoUhUwpI= + dependencies: + stackframe "^0.3.1" + error-stack-parser@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.4.tgz#a757397dc5d9de973ac9a5d7d4e8ade7cfae9101" @@ -11897,6 +11340,11 @@ es6-promise@^4.2.5: resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q== +es6-promise@^4.2.8: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + es6-promisify@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" @@ -12037,10 +11485,10 @@ escope@^3.6.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-config-prettier@^6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.7.0.tgz#9a876952e12df2b284adbd3440994bf1f39dfbb9" - integrity sha512-FamQVKM3jjUVwhG4hEMnbtsq7xOIDm+SY5iBPfR8gKsJoAB2IQnNF+bk1+8Fy44Nq7PPJaLvkRxILYdJWoguKQ== +eslint-config-prettier@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.4.0.tgz#0a04f147e31d33c6c161b2dd0971418ac52d0477" + integrity sha512-YrKucoFdc7SEko5Sxe4r6ixqXPDP1tunGw91POeZTTRKItf/AMFYt/YLEQtZMkR2LVpAVhcAcZgcWpm1oGPW7w== dependencies: get-stdin "^6.0.0" @@ -12063,28 +11511,36 @@ eslint-import-resolver-node@0.3.2, eslint-import-resolver-node@^0.3.2: debug "^2.6.9" resolve "^1.5.0" -eslint-import-resolver-webpack@0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.12.0.tgz#61b9879ff39441ed83cc2e304624506fa584b17e" - integrity sha512-S0hJUrbcTsVjPQRa8bdNQrv/sc45bGwhtoJk3Ru/1qZWnR4oArGKHYe61xT03iHZSo9K/xL63rTMiayW5+NGMQ== +eslint-import-resolver-webpack@0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.11.1.tgz#fcf1fd57a775f51e18f442915f85dd6ba45d2f26" + integrity sha512-eK3zR7xVQR/MaoBWwGuD+CULYVuqe5QFlDukman71aI6IboCGzggDUohHNfu1ZeBnbHcUHJc0ywWoXUBNB6qdg== dependencies: array-find "^1.0.0" - debug "^2.6.9" - enhanced-resolve "^0.9.1" + debug "^2.6.8" + enhanced-resolve "~0.9.0" find-root "^1.1.0" - has "^1.0.3" - interpret "^1.2.0" - lodash "^4.17.15" + has "^1.0.1" + interpret "^1.0.0" + lodash "^4.17.4" node-libs-browser "^1.0.0 || ^2.0.0" - resolve "^1.13.1" - semver "^5.7.1" + resolve "^1.10.0" + semver "^5.3.0" -eslint-module-utils@2.5.0, eslint-module-utils@^2.4.1: - version "2.5.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.5.0.tgz#cdf0b40d623032274ccd2abd7e64c4e524d6e19c" - integrity sha512-kCo8pZaNz2dsAW7nCUjuVoI11EBXXpIzfNxmaoLhXoRDOnqXLC4iSGVRdZPhOitfbdEfMEfKOiENaK6wDPZEGw== +eslint-module-utils@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz#7b4675875bf96b0dbf1b21977456e5bb1f5e018c" + integrity sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw== dependencies: - debug "^2.6.9" + debug "^2.6.8" + pkg-dir "^2.0.0" + +eslint-module-utils@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz#8b93499e9b00eab80ccb6614e69f03678e84e09a" + integrity sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw== + dependencies: + debug "^2.6.8" pkg-dir "^2.0.0" eslint-plugin-babel@^5.3.0: @@ -12101,10 +11557,10 @@ eslint-plugin-ban@^1.3.0: dependencies: requireindex "~1.2.0" -eslint-plugin-cypress@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.8.0.tgz#95a3fe4aa36dbeccd0337fad09496222905f6d8b" - integrity sha512-5J3xn0hH5KWh3wG0tEO7KXQc1LRqveBi0oTLG3+275j6W+P9uB9sNC1t+CWZieqz0rPkHoWWopntsE03WqJ84g== +eslint-plugin-cypress@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.7.0.tgz#117f14ce63698e4c4f3afea3d7e27025c8d504f0" + integrity sha512-52Lq5ePCD/8jc536e1RqtLfj33BAy1s7BlYgCjbG39J5kqUitcTlRY5i3NRoeAyPHueDwETsq0eASF44ugLosQ== dependencies: globals "^11.12.0" @@ -12116,23 +11572,22 @@ eslint-plugin-es@^2.0.0: eslint-utils "^1.4.2" regexpp "^3.0.0" -eslint-plugin-import@^2.19.1: - version "2.19.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.19.1.tgz#5654e10b7839d064dd0d46cd1b88ec2133a11448" - integrity sha512-x68131aKoCZlCae7rDXKSAQmbT5DQuManyXo2sK6fJJ0aK5CWAkv6A6HJZGgqC8IhjQxYPgo6/IY4Oz8AFsbBw== +eslint-plugin-import@^2.18.2: + version "2.18.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6" + integrity sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ== dependencies: array-includes "^3.0.3" - array.prototype.flat "^1.2.1" contains-path "^0.1.0" debug "^2.6.9" doctrine "1.5.0" eslint-import-resolver-node "^0.3.2" - eslint-module-utils "^2.4.1" + eslint-module-utils "^2.4.0" has "^1.0.3" minimatch "^3.0.4" object.values "^1.1.0" read-pkg-up "^2.0.0" - resolve "^1.12.0" + resolve "^1.11.0" eslint-plugin-jest@^22.19.0: version "22.19.0" @@ -12185,10 +11640,10 @@ eslint-plugin-prefer-object-spread@^1.2.1: resolved "https://registry.yarnpkg.com/eslint-plugin-prefer-object-spread/-/eslint-plugin-prefer-object-spread-1.2.1.tgz#27fb91853690cceb3ae6101d9c8aecc6a67a402c" integrity sha1-J/uRhTaQzOs65hAdnIrsxqZ6QCw= -eslint-plugin-prettier@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba" - integrity sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA== +eslint-plugin-prettier@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.1.tgz#507b8562410d02a03f0ddc949c616f877852f2ba" + integrity sha512-A+TZuHZ0KU0cnn56/9mfR7/KjUJ9QNVXUhwvRFSR7PGPe0zQR6PTkmyqg1AtUUEOzTqeRsUwyKFh0oVZKVCrtA== dependencies: prettier-linter-helpers "^1.0.0" @@ -12297,10 +11752,10 @@ eslint@^2.7.0: text-table "~0.2.0" user-home "^2.0.0" -eslint@^6.7.2: - version "6.7.2" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.7.2.tgz#c17707ca4ad7b2d8af986a33feba71e18a9fecd1" - integrity sha512-qMlSWJaCSxDFr8fBPvJM9kJwbazrhNcBU3+DszDW1OlEwKBBRWsJc7NJFelvwQpanHCR14cOLD41x8Eqvo3Nng== +eslint@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.5.1.tgz#828e4c469697d43bb586144be152198b91e96ed6" + integrity sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" @@ -12309,19 +11764,19 @@ eslint@^6.7.2: debug "^4.0.1" doctrine "^3.0.0" eslint-scope "^5.0.0" - eslint-utils "^1.4.3" + eslint-utils "^1.4.2" eslint-visitor-keys "^1.1.0" - espree "^6.1.2" + espree "^6.1.1" esquery "^1.0.1" esutils "^2.0.2" file-entry-cache "^5.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" - globals "^12.1.0" + globals "^11.7.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^7.0.0" + inquirer "^6.4.1" is-glob "^4.0.0" js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" @@ -12330,7 +11785,7 @@ eslint@^6.7.2: minimatch "^3.0.4" mkdirp "^0.5.1" natural-compare "^1.4.0" - optionator "^0.8.3" + optionator "^0.8.2" progress "^2.0.0" regexpp "^2.0.1" semver "^6.1.2" @@ -12348,13 +11803,13 @@ espree@^3.1.6: acorn "^5.5.0" acorn-jsx "^3.0.0" -espree@^6.1.2: - version "6.1.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" - integrity sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA== +espree@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de" + integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ== dependencies: - acorn "^7.1.0" - acorn-jsx "^5.1.0" + acorn "^7.0.0" + acorn-jsx "^5.0.2" eslint-visitor-keys "^1.1.0" esprima-fb@^15001.1.0-dev-harmony-fb: @@ -12572,10 +12027,10 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" - integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== +execa@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-3.2.0.tgz#18326b79c7ab7fbd6610fd900c1b9e95fa48f90a" + integrity sha512-kJJfVbI/lZE1PZYDI5VPxp8zXPO9rtxOkhpZ0jMKha56AI9y2gGVC6bkukStQf0ka5Rh15BA5m7cCCH4jmHqkw== dependencies: cross-spawn "^7.0.0" get-stream "^5.0.0" @@ -12988,7 +12443,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.4, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -13162,13 +12617,13 @@ file-exists-dazinatorfork@^1.0.2: resolved "https://registry.yarnpkg.com/file-exists-dazinatorfork/-/file-exists-dazinatorfork-1.0.2.tgz#cd8d0d85f63e39dc81eceb0b687c44a2cca95c47" integrity sha512-r70c72ln2YHzQINNfxDp02hAhbGkt1HffZ+Du8oetWDLjDtFja/Lm10lUaSh9e+wD+7VDvPee0b0C9SAy8pWZg== -file-loader@5.0.2, file-loader@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-5.0.2.tgz#7f3d8b4ac85a5e8df61338cfec95d7405f971caa" - integrity sha512-QMiQ+WBkGLejKe81HU8SZ9PovsU/5uaLo0JdTCEXOYv7i7jfAjHZi1tcwp9tSASJPOmmHZtbdCervFmXMH/Dcg== +file-loader@4.2.0, file-loader@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-4.2.0.tgz#5fb124d2369d7075d70a9a5abecd12e60a95215e" + integrity sha512-+xZnaK5R8kBJrHK0/6HRlrKNamvVS5rjyuju+rnyxRGuwUJwpAMsVzUl5dz6rK8brkzjV6JpcFNjp6NqV0g1OQ== dependencies: loader-utils "^1.2.3" - schema-utils "^2.5.0" + schema-utils "^2.0.0" file-loader@^3.0.1: version "3.0.1" @@ -13355,15 +12810,6 @@ find-cache-dir@^3.0.0: make-dir "^3.0.0" pkg-dir "^4.1.0" -find-cache-dir@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.2.0.tgz#e7fe44c1abc1299f516146e563108fd1006c1874" - integrity sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.0" - pkg-dir "^4.1.0" - find-root@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" @@ -13694,10 +13140,10 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -formidable@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659" - integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg== +formidable@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9" + integrity sha1-lriIb3w8NQi5Mta9cMTTqI818ak= formsy-react@^1.1.5: version "1.1.5" @@ -13882,10 +13328,10 @@ fsevents@^1.2.7: nan "^2.9.2" node-pre-gyp "^0.10.0" -fsevents@~2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" - integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== +fsevents@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.0.tgz#ce1a5f9ac71c6d75278b0c5bd236d7dfece4cbaa" + integrity sha512-+iXhW3LuDQsno8dOIrCIT/CBjeBWuP7PXe8w9shnj9Lebny/Gx1ZjVBYwexLz36Ri2jKuXMNpV6CYNh8lHHgrQ== fstream@^1.0.0: version "1.0.11" @@ -13996,15 +13442,15 @@ gaze@^1.0.0, gaze@^1.1.0: dependencies: globule "^1.0.0" -geckodriver@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-1.19.1.tgz#556f95fd6451b553cec89f81f81abbefce10d6e5" - integrity sha512-xWL/+eEhQ6+t98rc1c+xVM3hshDJibXtZf9WJA3sshxq4k5L1PBwfmswyBmmlKUfBr4xuC256gLVC2RxFhiCsQ== +geckodriver@^1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-1.19.0.tgz#b2b07e343c2e409ce645e65fe88132bd34fa400a" + integrity sha512-Zq98rXKjvB+NCfzKlJGkQkFAO8zvmUSNqYEIxUwlF1qxmv4taRwwBbEfDa6Dj7Auf7C0p+ZZZmIA8KmlL1cfsw== dependencies: adm-zip "0.4.11" bluebird "3.4.6" got "5.6.0" - https-proxy-agent "3.0.0" + https-proxy-agent "2.2.1" tar "4.4.2" generate-function@^2.0.0: @@ -14178,13 +13624,20 @@ getopts@^2.2.5: resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.2.5.tgz#67a0fe471cacb9c687d817cab6450b96dde8313b" integrity sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA== -getos@3.1.1, getos@^3.1.1: +getos@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.1.tgz#967a813cceafee0156b0483f7cffa5b3eff029c5" integrity sha512-oUP1rnEhAr97rkitiszGP9EgDVYnmchgFzfqRzSkgtfv7ai6tEi7Ko8GgjNXts7VLWEqrTWyhsOKLe5C5b/Zkg== dependencies: async "2.6.1" +getos@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.0.tgz#db3aa4df15a3295557ce5e81aa9e3e5cdfaa6567" + integrity sha512-i9vrxtDu5DlLVFcrbqUqGWYlZN/zZ4pGMICCAcZoYsX3JA54nYp8r5EThw5K+m2q3wszkx4Th746JstspB0H4Q== + dependencies: + async "2.4.0" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -14453,7 +13906,7 @@ global@^4.4.0: min-document "^2.19.0" process "^0.11.10" -globals@^11.1.0: +globals@^11.1.0, globals@^11.7.0: version "11.7.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" integrity sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg== @@ -14463,13 +13916,6 @@ globals@^11.12.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^12.1.0: - version "12.3.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.3.0.tgz#1e564ee5c4dded2ab098b0f88f24702a3c56be13" - integrity sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw== - dependencies: - type-fest "^0.8.1" - globals@^9.18.0, globals@^9.2.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -15898,15 +15344,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz#0106efa5d63d6d6f3ab87c999fa4877a3fd1ff97" - integrity sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ== - dependencies: - agent-base "^4.3.0" - debug "^3.1.0" - -https-proxy-agent@^2.2.1: +https-proxy-agent@2.2.1, https-proxy-agent@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== @@ -15989,6 +15427,11 @@ iconv-lite@^0.5.0: dependencies: safer-buffer ">= 2.1.2 < 3" +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= + icss-utils@^4.0.0, icss-utils@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" @@ -15996,6 +15439,13 @@ icss-utils@^4.0.0, icss-utils@^4.1.1: dependencies: postcss "^7.0.14" +icss-utils@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.0.tgz#339dbbffb9f8729a243b701e1c29d4cc58c52f0e" + integrity sha512-3DEun4VOeMvSczifM3F2cKQrDQ5Pj6WKhkOq6HD4QTnDUAq8MQRxy5TX6Sy1iY6WPBe4gQ3p5vTECjbIkglkkQ== + dependencies: + postcss "^7.0.14" + idx@^2.5.6: version "2.5.6" resolved "https://registry.yarnpkg.com/idx/-/idx-2.5.6.tgz#1f824595070100ae9ad585c86db08dc74f83a59d" @@ -16387,6 +15837,25 @@ inquirer@^6.2.0: strip-ansi "^4.0.0" through "^2.3.6" +inquirer@^6.4.1: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + inquirer@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.0.tgz#9e2b032dde77da1db5db804758b8fea3a970519a" @@ -17851,16 +17320,15 @@ jest@^24.9.0: import-local "^2.0.0" jest-cli "^24.9.0" -jimp@0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.9.3.tgz#85e8e80eea65a7e6de806c6bb622ec6a7244e6f3" - integrity sha512-dIxvT1OMRkd3+B18XUhJ5WZ2Dw7Hp8mvjaTqfi945zZ7fga6LT22h3NLYDorHHAiy9z30KjfNnOgpBoxrdjDZg== +jimp@0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.8.4.tgz#9c7c6ee4c8992e585a60914c62aee0c5e5c7955b" + integrity sha512-xCPvd2HIH8iR7+gWVnivzXwiQGnLBmLDpaEj5M0vQf3uur5MuLCOWbBduAdk6r3ur8X0kwgM4eEM0i7o+k9x9g== dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/custom" "^0.9.3" - "@jimp/plugins" "^0.9.3" - "@jimp/types" "^0.9.3" - core-js "^3.4.1" + "@jimp/custom" "^0.8.4" + "@jimp/plugins" "^0.8.4" + "@jimp/types" "^0.8.4" + core-js "^2.5.7" regenerator-runtime "^0.13.3" jit-grunt@0.10.0: @@ -18914,16 +18382,16 @@ load-source-map@^1.0.0: semver "^5.3.0" source-map "^0.5.6" +loader-runner@^2.3.0, loader-runner@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + loader-runner@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.1.tgz#026f12fe7c3115992896ac02ba022ba92971b979" integrity sha512-By6ZFY7ETWOc9RFaAIb23IjJVcM4dvJC/N57nmdz9RSkMXvAXGI7SyVlAw3v8vjtDRlqThgVDVmTnr9fqMlxkw== -loader-runner@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - loader-utils@1.2.3, loader-utils@^1.0.4, loader-utils@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" @@ -18933,7 +18401,7 @@ loader-utils@1.2.3, loader-utils@^1.0.4, loader-utils@^1.2.3: emojis-list "^2.0.0" json5 "^1.0.1" -loader-utils@^1.0.0, loader-utils@^1.0.2, loader-utils@^1.1.0: +loader-utils@^1.0.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" integrity sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0= @@ -19845,7 +19313,7 @@ memory-fs@^0.2.0: resolved "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" integrity sha1-8rslNovBIeORwlIN6Slpyu4KApA= -memory-fs@^0.4.0, memory-fs@^0.4.1: +memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= @@ -19939,7 +19407,7 @@ merge@^1.2.0: resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== -methods@^1.1.1, methods@^1.1.2, methods@~1.1.2: +methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= @@ -19949,7 +19417,7 @@ microevent.ts@~0.1.1: resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== -micromatch@3.1.10, micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@3.1.10, micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -20934,7 +20402,7 @@ node-jose@1.1.0: util "^0.11.0" vm-browserify "0.0.4" -node-libs-browser@^2.2.1: +node-libs-browser@^2.0.0, node-libs-browser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== @@ -21002,13 +20470,6 @@ node-releases@^1.1.25: dependencies: semver "^5.3.0" -node-releases@^1.1.42: - version "1.1.42" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.42.tgz#a999f6a62f8746981f6da90627a8d2fc090bbad7" - integrity sha512-OQ/ESmUqGawI2PRX+XIRao44qWYBBfN54ImQYdWVTQqUckuejOg76ysSqDBK8NG3zwySRVnX36JwDQ6x+9GxzA== - dependencies: - semver "^6.3.0" - node-sass@^4.9.4: version "4.9.4" resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.4.tgz#349bd7f1c89422ffe7e1e4b60f2055a69fbc5512" @@ -21590,6 +21051,11 @@ opener@^1.4.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== +opentracing@^0.14.3: + version "0.14.4" + resolved "https://registry.yarnpkg.com/opentracing/-/opentracing-0.14.4.tgz#a113408ea740da3a90fde5b3b0011a375c2e4268" + integrity sha512-nNnZDkUNExBwEpb7LZaeMeQgvrlO8l4bgY/LvGNZCR0xG/dGWqHqjKrAmR5GUoYo0FIz38kxasvA1aevxWs2CA== + opn@^5.3.0: version "5.4.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.4.0.tgz#cb545e7aab78562beb11aa3bfabc7042e1761035" @@ -21631,7 +21097,7 @@ optional-js@^2.0.0: resolved "https://registry.yarnpkg.com/optional-js/-/optional-js-2.1.1.tgz#c2dc519ad119648510b4d241dbb60b1167c36a46" integrity sha512-mUS4bDngcD5kKzzRUd1HVQkr9Lzzby3fSrrPR9wOHhQiyYo+hDS5NVli5YQzGjQRQ15k5Sno4xH9pfykJdeEUA== -optionator@^0.8.1: +optionator@^0.8.1, optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= @@ -21643,18 +21109,6 @@ optionator@^0.8.1: type-check "~0.3.2" wordwrap "~1.0.0" -optionator@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - ora@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" @@ -21794,6 +21248,15 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +output-file-sync@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-2.0.1.tgz#f53118282f5f553c2799541792b723a4c71430c0" + integrity sha512-mDho4qm7WgIXIGf4eYU1RHN2UU5tPfVYVSRwDJw0uTmj35DQUt/eNp19N7v6T3SrR0ESTEf2up2CGO73qI35zQ== + dependencies: + graceful-fs "^4.1.11" + is-plain-obj "^1.1.0" + mkdirp "^0.5.1" + p-any@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-any/-/p-any-1.1.0.tgz#1d03835c7eed1e34b8e539c47b7b60d0d015d4e1" @@ -21864,13 +21327,6 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" - integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== - dependencies: - p-try "^2.0.0" - p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -22690,14 +22146,14 @@ popper.js@^1.14.4: resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.7.tgz#e31ec06cfac6a97a53280c3e55e4e0c860e7738e" integrity sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ== -portfinder@^1.0.25: - version "1.0.25" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" - integrity sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg== +portfinder@^1.0.24: + version "1.0.24" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.24.tgz#11efbc6865f12f37624b6531ead1d809ed965cfa" + integrity sha512-ekRl7zD2qxYndYflwiryJwMioBI7LI7rVXg3EnLK3sjkouT5eOuhS3gS255XxBksa30VG8UPZYZCdgfGOfkSUg== dependencies: - async "^2.6.2" - debug "^3.1.1" - mkdirp "^0.5.1" + async "^1.5.2" + debug "^2.2.0" + mkdirp "0.5.x" posix-character-classes@^0.1.0: version "0.1.1" @@ -22736,6 +22192,15 @@ postcss-modules-extract-imports@^2.0.0: dependencies: postcss "^7.0.5" +postcss-modules-local-by-default@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz#dd9953f6dd476b5fd1ef2d8830c8929760b56e63" + integrity sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + postcss-value-parser "^3.3.1" + postcss-modules-local-by-default@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915" @@ -22754,13 +22219,13 @@ postcss-modules-scope@^2.1.0: postcss "^7.0.6" postcss-selector-parser "^6.0.0" -postcss-modules-scope@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz#33d4fc946602eb5e9355c4165d68a10727689dba" - integrity sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ== +postcss-modules-values@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz#479b46dc0c5ca3dc7fa5270851836b9ec7152f64" + integrity sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w== dependencies: + icss-replace-symbols "^1.1.0" postcss "^7.0.6" - postcss-selector-parser "^6.0.0" postcss-modules-values@^3.0.0: version "3.0.0" @@ -22797,6 +22262,11 @@ postcss-url@^8.0.0: postcss "^7.0.2" xxhashjs "^0.2.1" +postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + postcss-value-parser@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.0.tgz#99a983d365f7b2ad8d0f9b8c3094926eab4b936d" @@ -22834,15 +22304,6 @@ postcss@^7.0.16: source-map "^0.6.1" supports-color "^6.1.0" -postcss@^7.0.23: - version "7.0.24" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.24.tgz#972c3c5be431b32e40caefe6c81b5a19117704c2" - integrity sha512-Xl0XvdNWg+CblAXzNvbSOUvgJXwSjmbAKORqyw9V2AlHrm1js2gFw9y3jibBAhpKZi8b5JzJCVh/FyzPsTtgTA== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - potpack@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.1.tgz#d1b1afd89e4c8f7762865ec30bd112ab767e2ebf" @@ -23568,13 +23029,13 @@ raw-body@~1.1.0: bytes "1" string_decoder "0.10" -raw-loader@4.0.0, raw-loader@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.0.tgz#d639c40fb9d72b5c7f8abc1fb2ddb25b29d3d540" - integrity sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q== +raw-loader@3.1.0, raw-loader@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-3.1.0.tgz#5e9d399a5a222cc0de18f42c3bc5e49677532b3f" + integrity sha512-lzUVMuJ06HF4rYveaz9Tv0WRlUMxJ0Y1hgSkkgg+50iEdaI0TthyEDe08KIHb0XsF6rn8WYTqPCaGTZg3sX+qA== dependencies: - loader-utils "^1.2.3" - schema-utils "^2.5.0" + loader-utils "^1.1.0" + schema-utils "^2.0.1" raw-loader@^2.0.0: version "2.0.0" @@ -24622,10 +24083,10 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -readdirp@~3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" - integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== +readdirp@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.1.3.tgz#d6e011ed5b9240a92f08651eeb40f7942ceb6cc1" + integrity sha512-ZOsfTGkjO2kqeR5Mzr5RYDbTGYneSkdNKX2fOX2P5jF7vMrd/GNnIAUtDldeHHumHUCQ3V05YfWUdxMPAsRu9Q== dependencies: picomatch "^2.0.4" @@ -24845,13 +24306,6 @@ regenerate-unicode-properties@^8.0.2: dependencies: regenerate "^1.4.0" -regenerate-unicode-properties@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" - integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== - dependencies: - regenerate "^1.4.0" - regenerate@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" @@ -24948,18 +24402,6 @@ regexpu-core@^4.5.4: unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.1.0" -regexpu-core@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" - integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.1.0" - regjsgen "^0.5.0" - regjsparser "^0.6.0" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.1.0" - registry-auth-token@^3.0.1, registry-auth-token@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" @@ -25512,13 +24954,6 @@ resolve@^1.12.0, resolve@^1.4.0: dependencies: path-parse "^1.0.6" -resolve@^1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16" - integrity sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w== - dependencies: - path-parse "^1.0.6" - resolve@^1.5.0, resolve@^1.7.1: version "1.7.1" resolved "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" @@ -25919,15 +25354,15 @@ sass-lint@^1.12.1: path-is-absolute "^1.0.0" util "^0.10.3" -sass-loader@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-8.0.0.tgz#e7b07a3e357f965e6b03dd45b016b0a9746af797" - integrity sha512-+qeMu563PN7rPdit2+n5uuYVR0SSVwm0JsOUsaJXzgYcClWSlmX0iHDnmeOobPkf5kUglVot3QS6SyLyaQoJ4w== +sass-loader@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.3.1.tgz#a5bf68a04bcea1c13ff842d747150f7ab7d0d23f" + integrity sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA== dependencies: clone-deep "^4.0.1" - loader-utils "^1.2.3" - neo-async "^2.6.1" - schema-utils "^2.1.0" + loader-utils "^1.0.1" + neo-async "^2.5.0" + pify "^4.0.1" semver "^6.3.0" sass-lookup@^3.0.0: @@ -25982,7 +25417,7 @@ schema-utils@^0.3.0: dependencies: ajv "^5.0.0" -schema-utils@^0.4.0: +schema-utils@^0.4.0, schema-utils@^0.4.5: version "0.4.7" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ== @@ -26007,14 +25442,6 @@ schema-utils@^2.0.0, schema-utils@^2.0.1: ajv "^6.1.0" ajv-keywords "^3.1.0" -schema-utils@^2.1.0, schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.1.tgz#eb78f0b945c7bcfa2082b3565e8db3548011dc4f" - integrity sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg== - dependencies: - ajv "^6.10.2" - ajv-keywords "^3.4.1" - schema-utils@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.4.1.tgz#e89ade5d056dc8bcaca377574bb4a9c4e1b8be56" @@ -26145,11 +25572,6 @@ semver@^5.5.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== -semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - semver@^6.0.0: version "6.1.2" resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.2.tgz#079960381376a3db62eb2edc8a3bfb10c7cfe318" @@ -26216,7 +25638,7 @@ sentence-case@^2.1.0: no-case "^2.2.0" upper-case-first "^1.1.2" -serialize-javascript@^1.7.0, serialize-javascript@^2.1.1, serialize-javascript@^2.1.2: +serialize-javascript@^1.7.0, serialize-javascript@^2.1.0, serialize-javascript@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.1.tgz#952907a04a3e3a75af7f73d92d15e233862048b2" integrity sha512-MPLPRpD4FNqWq9tTIjYG5LesFouDhdyH0EPY3gVK4DRD5+g4aDqdNSzLIwceulo3Yj+PL1bPh6laE5+H6LTcrQ== @@ -26456,13 +25878,20 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= -simple-git@1.129.0, simple-git@^1.129.0: - version "1.129.0" - resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.129.0.tgz#eddd2611d2bf41c77e1d08cd70c0b7f3af785040" - integrity sha512-XbzNmugMTeV2crZnPl+b1ZJn+nqXCUNyrZxDXpLM0kHL3B85sbPlpd8q9I4qtAHI9D2FxTB6w4BuiAGKYtyzKw== +simple-git@1.116.0: + version "1.116.0" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.116.0.tgz#ea6e533466f1e0152186e306e004d4eefa6e3e00" + integrity sha512-Pbo3tceqMYy0j3U7jzMKabOWcx5+67GdgQUjpK83XUxGhA+1BX93UPvlWNzbCRoFwd7EJTyDSCC2XCoT4NTLYQ== dependencies: debug "^4.0.1" +simple-git@^1.91.0: + version "1.92.0" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.92.0.tgz#6061468eb7d19f0141078fc742e62457e910f547" + integrity sha1-YGFGjrfRnwFBB4/HQuYkV+kQ9Uc= + dependencies: + debug "^3.1.0" + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -27015,6 +26444,11 @@ stack-utils@^1.0.1: resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.1.tgz#d4f33ab54e8e38778b0ca5cfd3b3afb12db68620" integrity sha1-1PM6tU6OOHeLDKXP07OvsS22hiA= +stackframe@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-0.3.1.tgz#33aa84f1177a5548c8935533cbfeb3420975f5a4" + integrity sha1-M6qE8Rd6VUjIk1Uzy/6zQgl19aQ= + stackframe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.1.0.tgz#e3fc2eb912259479c9822f7d1f1ff365bd5cbc83" @@ -27544,15 +26978,7 @@ style-it@^2.1.3: dependencies: react-lib-adler32 "^1.0.3" -style-loader@1.0.1, style-loader@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.0.1.tgz#aec6d4c61d0ed8d0a442faed741d4dfc6573888a" - integrity sha512-CnpEkSR1C+REjudiTWCv4+ssP7SCiuaQZJTZDWBRwTJoS90mdqkB8uOGMHKgVeUzpaU7IfLWoyQbvvs5Joj3Xw== - dependencies: - loader-utils "^1.2.3" - schema-utils "^2.0.1" - -style-loader@^0.23.1: +style-loader@0.23.1, style-loader@^0.23.1: version "0.23.1" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.1.tgz#cb9154606f3e771ab6c4ab637026a1049174d925" integrity sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg== @@ -27624,21 +27050,21 @@ suffix@^0.1.0: resolved "https://registry.yarnpkg.com/suffix/-/suffix-0.1.0.tgz#3e46966de56af17600385e58db8ec659dd797907" integrity sha1-PkaWbeVq8XYAOF5Y247GWd15eQc= -superagent@^3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128" - integrity sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA== +superagent@3.8.2: + version "3.8.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.2.tgz#e4a11b9d047f7d3efeb3bbe536d9ec0021d16403" + integrity sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ== dependencies: component-emitter "^1.2.0" cookiejar "^2.1.0" debug "^3.1.0" extend "^3.0.0" form-data "^2.3.1" - formidable "^1.2.0" + formidable "^1.1.1" methods "^1.1.1" mime "^1.4.1" qs "^6.5.1" - readable-stream "^2.3.5" + readable-stream "^2.0.5" supercluster@^6.0.1: version "6.0.1" @@ -27655,13 +27081,13 @@ supertest-as-promised@^4.0.2: bluebird "^3.3.1" methods "^1.1.1" -supertest@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/supertest/-/supertest-4.0.2.tgz#c2234dbdd6dc79b6f15b99c8d6577b90e4ce3f36" - integrity sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ== +supertest@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-3.1.0.tgz#f9ebaf488e60f2176021ec580bdd23ad269e7bc6" + integrity sha512-O44AMnmJqx294uJQjfUmEyYOg7d9mylNFsMw/Wkz4evKd1njyPrtCN+U6ZIC7sKtfEVQhfTqFFijlXx8KP/Czw== dependencies: - methods "^1.1.2" - superagent "^3.8.3" + methods "~1.1.2" + superagent "3.8.2" supports-color@5.5.0, supports-color@^5.0.0, supports-color@^5.4.0, supports-color@^5.5.0: version "5.5.0" @@ -28030,7 +27456,7 @@ term-size@^1.2.0: dependencies: execa "^0.7.0" -terser-webpack-plugin@^1.2.4, terser-webpack-plugin@^1.4.1: +terser-webpack-plugin@^1.1.0, terser-webpack-plugin@^1.2.4, terser-webpack-plugin@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz#61b18e40eaee5be97e771cdbb10ed1280888c2b4" integrity sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg== @@ -28045,18 +27471,18 @@ terser-webpack-plugin@^1.2.4, terser-webpack-plugin@^1.4.1: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser-webpack-plugin@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.0.tgz#00fd8f792a330dc572e2e2b468fd7cb5ffd7ea51" - integrity sha512-yez0HdpDf/iQVYGf+e/o8ZYWLb1g9d1nRRi5FIOZ4KfXbfSPT259UoqxPiSLhCnr0mlDoh+bucpYQSFbU0cEsQ== +terser-webpack-plugin@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.1.2.tgz#2b9b8147a6f18918348200800cf9560c50f701bb" + integrity sha512-MF/C4KABwqYOfRDi87f7gG07GP7Wj/kyiX938UxIGIO6l5mkh8XJL7xtS0hX/CRdVQaZI7ThGUPZbznrCjsGpg== dependencies: - cacache "^13.0.1" - find-cache-dir "^3.1.0" + cacache "^13.0.0" + find-cache-dir "^3.0.0" jest-worker "^24.9.0" - schema-utils "^2.6.1" - serialize-javascript "^2.1.2" + schema-utils "^2.4.1" + serialize-javascript "^2.1.0" source-map "^0.6.1" - terser "^4.4.2" + terser "^4.3.4" webpack-sources "^1.4.3" terser@^4.1.2: @@ -28068,10 +27494,10 @@ terser@^4.1.2: source-map "~0.6.1" source-map-support "~0.5.12" -terser@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.4.2.tgz#448fffad0245f4c8a277ce89788b458bfd7706e8" - integrity sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ== +terser@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.3.4.tgz#ad91bade95619e3434685d69efa621a5af5f877d" + integrity sha512-Kcrn3RiW8NtHBP0ssOAzwa2MsIRQ8lJWiBG/K7JgqPlomA3mtb2DEmp4/hrUA+Jujx+WZ02zqd7GYD+QRBB/2Q== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -29664,16 +29090,7 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-loader@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-3.0.0.tgz#9f1f11b371acf6e51ed15a50db635e02eec18368" - integrity sha512-a84JJbIA5xTFTWyjjcPdnsu+41o/SNE8SpXMdUvXs6Q+LuhCD9E2+0VCiuDWqgo3GGXVlFHzArDmBpj9PgWn4A== - dependencies: - loader-utils "^1.2.3" - mime "^2.4.4" - schema-utils "^2.5.0" - -url-loader@^2.0.1: +url-loader@2.2.0, url-loader@^2.0.1: version "2.2.0" resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-2.2.0.tgz#af321aece1fd0d683adc8aaeb27829f29c75b46e" integrity sha512-G8nk3np8ZAnwhHXas1JxJEwJyQdqFXAKJehfgZ/XrC48volFBRtO+FIKtF2u0Ma3bw+4vnDVjHPAQYlF9p2vsw== @@ -29897,13 +29314,13 @@ v8flags@^3.0.1: dependencies: homedir-polyfill "^1.0.1" -val-loader@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/val-loader/-/val-loader-2.0.2.tgz#12cda9a55b3e9c0305763c03f872ea0951861df2" - integrity sha512-Wzce0uHkvt/gjCas7uW6Q0BeU2FxUv9KPw/n4WXYo/9pkBZhmtNpi2FgXmq2LhEpu52kHAifFhJrTgjCMmWAwQ== +val-loader@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/val-loader/-/val-loader-1.1.1.tgz#32ba8ed5c3607504134977251db2966499e15ef7" + integrity sha512-JLqLXJWCVLXTxbUeHhLpWkgl3+X3U8Bl0vY7rTFZgFSbLJaEtAxuD2ixy/cM8w/gzC7sS3NE5IDSzClDt332sw== dependencies: - loader-utils "^1.2.3" - schema-utils "^2.5.0" + loader-utils "^1.0.0" + schema-utils "^0.4.5" valid-url@1.0.9: version "1.0.9" @@ -30591,7 +30008,7 @@ warning@^4.0.2: dependencies: loose-envify "^1.0.0" -watchpack@^1.6.0: +watchpack@^1.5.0, watchpack@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== @@ -30631,10 +30048,10 @@ webidl-conversions@^4.0.1, webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -webpack-cli@^3.3.10: - version "3.3.10" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.10.tgz#17b279267e9b4fb549023fae170da8e6e766da13" - integrity sha512-u1dgND9+MXaEt74sJR4PR7qkPxXUSQ0RXYq8x1L6Jg1MYVEmGPrH6Ah6C4arD4r0J1P5HKjRqpab36k0eIzPqg== +webpack-cli@^3.3.9: + version "3.3.9" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.9.tgz#79c27e71f94b7fe324d594ab64a8e396b9daa91a" + integrity sha512-xwnSxWl8nZtBl/AFJCOn9pG7s5CYUYdZxmmukv+fAHLcBIHM36dImfpQg3WfShZXeArkWlf6QRw24Klcsv8a5A== dependencies: chalk "2.4.2" cross-spawn "6.0.5" @@ -30669,10 +30086,10 @@ webpack-dev-middleware@^3.7.2: range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-dev-server@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz#27c3b5d0f6b6677c4304465ac817623c8b27b89c" - integrity sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw== +webpack-dev-server@^3.8.2: + version "3.8.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.8.2.tgz#3292427bf6510da9a3ac2d500b924a4197667ff9" + integrity sha512-0xxogS7n5jHDQWy0WST0q6Ykp7UGj4YvWh+HVN71JoE7BwPxMZrwgraBvmdEMbDVMBzF0u+mEzn8TQzBm5NYJQ== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" @@ -30692,7 +30109,7 @@ webpack-dev-server@^3.9.0: loglevel "^1.6.4" opn "^5.5.0" p-retry "^3.0.1" - portfinder "^1.0.25" + portfinder "^1.0.24" schema-utils "^1.0.0" selfsigned "^1.10.7" semver "^6.3.0" @@ -30751,7 +30168,7 @@ webpack-sources@^1.1.0: source-list-map "^2.0.0" source-map "~0.6.1" -webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: +webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== @@ -30759,10 +30176,40 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@4.41.2, webpack@^4.41.2: - version "4.41.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.2.tgz#c34ec76daa3a8468c9b61a50336d8e3303dce74e" - integrity sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A== +webpack@4.33.0: + version "4.33.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.33.0.tgz#c30fc4307db432e5c5e3333aaa7c16a15a3b277e" + integrity sha512-ggWMb0B2QUuYso6FPZKUohOgfm+Z0sVFs8WwWuSH1IAvkWs428VDNmOlAxvHGTB9Dm/qOB/qtE5cRx5y01clxw== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + acorn "^6.0.5" + acorn-dynamic-import "^4.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chrome-trace-event "^1.0.0" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.0" + json-parse-better-errors "^1.0.2" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + micromatch "^3.1.8" + mkdirp "~0.5.0" + neo-async "^2.5.0" + node-libs-browser "^2.0.0" + schema-utils "^1.0.0" + tapable "^1.1.0" + terser-webpack-plugin "^1.1.0" + watchpack "^1.5.0" + webpack-sources "^1.3.0" + +webpack@4.41.0, webpack@^4.41.0: + version "4.41.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.0.tgz#db6a254bde671769f7c14e90a1a55e73602fc70b" + integrity sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g== dependencies: "@webassemblyjs/ast" "1.8.5" "@webassemblyjs/helper-module-context" "1.8.5" @@ -31035,11 +30482,6 @@ with@^5.0.0: acorn "^3.1.0" acorn-globals "^3.0.0" -word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"