diff --git a/.eslintrc.js b/.eslintrc.js index af05af0f6e402..e66331594b4ae 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -491,6 +491,7 @@ module.exports = { 'x-pack/dev-tools/mocha/setup_mocha.js', 'x-pack/scripts/*.js', ], + excludedFiles: ['**/integration_tests/**/*'], rules: { 'import/no-commonjs': 'off', 'prefer-object-spread/prefer-object-spread': 'off', diff --git a/.i18nrc.json b/.i18nrc.json index 42b7f6119eda1..c293b3103a39c 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -52,10 +52,7 @@ "visTypeVega": "src/legacy/core_plugins/vis_type_vega", "visTypeVislib": "src/legacy/core_plugins/vis_type_vislib", "visTypeXy": "src/legacy/core_plugins/vis_type_xy", - "visualizations": [ - "src/plugins/visualizations", - "src/legacy/core_plugins/visualizations" - ] + "visualizations": "src/plugins/visualizations" }, "exclude": [ "src/legacy/ui/ui_render/ui_render_mixin.js" diff --git a/docs/apm/custom-links.asciidoc b/docs/apm/custom-links.asciidoc new file mode 100644 index 0000000000000..75c1c9d0009a2 --- /dev/null +++ b/docs/apm/custom-links.asciidoc @@ -0,0 +1,218 @@ +[[custom-links]] +=== Custom links + +Elastic's custom link feature allows you to easily create up to 500 dynamic links +based on your specific APM data. +Custom links can be filtered to only appear in the APM app for relevant services, +environments, transaction types, or transaction names. + +Ready to dive in? Jump straight to the <>. + +[float] +[[custom-links-create]] +=== Create a link + +Each custom link consists of a label, url, and optional filter. +The easiest way to create a custom link is from within the actions dropdown in the transaction detail page. +This method will automatically apply filters, scoping the link to that specific service, +environment, transaction type, and transaction name. + +Alternatively, you can create a custom link in the APM app by navigating to **Settings** > **Customize UI**, +and selecting **Create custom link**. + +[float] +[[custom-links-label]] +==== Label + +The name of your custom link. +This text will be shown in the actions context menu, +so keep it as short as possible. + +TIP: Custom links are displayed alphabetically in the actions menu. + +[float] +[[custom-links-url]] +==== URL + +The URL your link points to. +URLs support dynamic field name variables, encapsulated in double curly brackets: `{{field.name}}`. +These variables will be replaced with transaction metadata when the link is clicked. + +Because everyone's data is different, +you'll need to examine your own traces to see what metadata is available for use. +The easiest way to do this is to select a trace in the APM app, and click **Metadata** in the **Trace Sample** table. + +[role="screenshot"] +image::apm/images/example-metadata.png[Example metadata] + +[float] +[[custom-links-filters]] +==== Filters + +Filter each link to only appear so it only appears for specific services or transactions. +You can filter on the following fields: + +* `service.name` +* `service.env` +* `transaction.type` +* `transaction.name` + +Multiple values are allowed when comma separated. + +[float] +[[custom-links-examples]] +=== Custom link examples + +// Relevant documentation links +:jira-query-params: https://confluence.atlassian.com/jirakb/how-to-create-issues-using-direct-html-links-in-jira-server-159474.html +:github-query-params: https://help.github.com/en/github/managing-your-work-on-github/about-automation-for-issues-and-pull-requests-with-query-parameters + +Not sure where to start with custom links? +Take a look at the examples below, and customize them to your liking! + +[float] +[[custom-links-examples-email]] +==== Email + +Email the owner of a service. + +|==== +|Label |`Email engineer` +|Link |`mailto:@.com` +|Filters |`service.name:` +|==== + +**Example** + +This link opens an email addressed to the team or owner of `python-backend`. +It will only appear on services with the name `python-backend`. + +|==== +|Label |`Email python-backend engineers` +|Link |`mailto:python_team@elastic.co` +|Filters |`service.name:python-backend` +|==== + +[float] +[[custom-links-examples-gh]] +==== GitHub issue + +Open a GitHub issue with pre-populated metadata from the selected trace sample. + +|==== +|Label |`Open an issue in ` +|Link |`https://github.com///issues/new?title=&body=<BODY>` +|Filters |`service.name:client` +|==== + +**Example** + +This link opens a new GitHub issue in the apm-agent-rum repository. +It populates the issue body with relevant metadata from the currently active trace. +Clicking this link results in the following issue being created: + +[role="screenshot"] +image::apm/images/create-github-issue.png[Example github issue] + +|==== +|Label |`Open an issue in apm-rum-js` +|Link |`https://github.com/elastic/apm-agent-rum-js/issues/new?title=Investigate+APM+trace&body=Investigate+the+following+APM+trace%3A%0D%0A%0D%0Aservice.name%3A+{{service.name}}%0D%0Atransaction.id%3A+{{transaction.id}}%0D%0Acontainer.id%3A+{{container.id}}%0D%0Aurl.full%3A+{{url.full}}` +|Filters |`service.name:client` +|==== + +See the {github-query-params}[GitHub automation documentation] for a full list of supported query parameters. + +[float] +[[custom-links-examples-jira]] +==== Jira task + +Create a Jira task with pre-populated metadata from the selected trace sample. + +|==== +|Label |`Open an issue in Jira` +|Link |`https://<JIRA_BASE_URL>/secure/CreateIssueDetails!init.jspa?<ARGUMENTS>` +|==== + +**Example** + +This link creates a new task on the Engineering board in Jira. +It populates the issue body with relevant metadata from the currently active trace. +Clicking this link results in the following task being created in Jira: + +[role="screenshot"] +image::apm/images/create-jira-issue.png[Example jira issue] + +|==== +|Label |`Open a task in Jira` +|Link |`https://test-site-33.atlassian.net/secure/CreateIssueDetails!init.jspa?pid=10000&issuetype=10001&summary=Created+via+APM&description=Investigate+the+following+APM+trace%3A%0D%0A%0D%0Aservice.name%3A+{{service.name}}%0D%0Atransaction.id%3A+{{transaction.id}}%0D%0Acontainer.id%3A+{{container.id}}%0D%0Aurl.full%3A+{{url.full}}` +|==== + +See the {jira-query-params}[Jira application administration knowledge base] +for a full list of supported query parameters. + +[float] +[[custom-links-examples-kib]] +==== Kibana dashboards + +Link to a custom dashboard in Kibana. + +|==== +|Label |`Open transaction in custom visualization` +|Link |`https://kibana-instance/app/kibana#/dashboard?_g=query:(language:kuery,query:'transaction.id:{{transaction.id}}'...` +|==== + +**Example** + +This link opens the current `transaction.id` in a custom kibana dashboard. +There are no filters set. + +|==== +|Label |`Open transaction in Python drilldown viz` +|URL |`https://kibana-instance/app/kibana#/dashboard?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-24h,to:now))&_a=(description:'',filters:!(),fullScreenMode:!f,options:(hidePanelTitles:!f,useMargins:!t),panels:!((embeddableConfig:(),gridData:(h:15,i:cb79c1c0-1af8-472c-aaf7-d158a76946fb,w:24,x:0,y:0),id:c8c74b20-6a30-11ea-92ab-b5d3feff11df,panelIndex:cb79c1c0-1af8-472c-aaf7-d158a76946fb,type:visualization,version:'7.7')),query:(language:kuery,query:'transaction.id:{{transaction.id}}'),timeRestore:!f,title:'',viewMode:edit)` +|==== + +[float] +[[custom-links-examples-slack]] +==== Slack channel + +Open a specified slack channel. + +|==== +|Label |`Open SLACK_CHANNEL` +|Link |`https://COMPANY_SLACK.slack.com/archives/SLACK_CHANNEL` +|Filters |`service.name` : `SERVICE_NAME` +|==== + +**Example** + +This link opens a company slack channel, #apm-support. +It only appears when `transaction.name` is `GET user/login`. + +|==== +|Label |`Open #apm-user-support` +|Link |`https://microsoft.slack.com/archives/efk52kt23k` +|Filters |`transaction.name:GET user/login` +|==== + +[float] +[[custom-links-examples-web]] +==== Website + +Open an internal or external website. + +|==== +|Label |`Open <WEBSITE>` +|Link |`https://<COMPANY_SLACK>.slack.com/archives/<SLACK_CHANNEL>` +|Filters |`service.name:<SERVICE_NAME>` +|==== + +**Example** + +This link opens more data on a specific `user.email`. +It only appears on front-end transactions. + +|==== +|Label |`View user internally` +|Link |`https://internal-site.company.com/user/{{user.email}}` +|Filters |`service.name:client` +|==== diff --git a/docs/apm/images/create-github-issue.png b/docs/apm/images/create-github-issue.png new file mode 100644 index 0000000000000..81ea4e5e78c27 Binary files /dev/null and b/docs/apm/images/create-github-issue.png differ diff --git a/docs/apm/images/create-jira-issue.png b/docs/apm/images/create-jira-issue.png new file mode 100644 index 0000000000000..962c98df3f6c6 Binary files /dev/null and b/docs/apm/images/create-jira-issue.png differ diff --git a/docs/apm/images/example-metadata.png b/docs/apm/images/example-metadata.png new file mode 100644 index 0000000000000..0e35f90691723 Binary files /dev/null and b/docs/apm/images/example-metadata.png differ diff --git a/docs/apm/using-the-apm-ui.asciidoc b/docs/apm/using-the-apm-ui.asciidoc index 95ec41cf8a403..1361dc046e3b1 100644 --- a/docs/apm/using-the-apm-ui.asciidoc +++ b/docs/apm/using-the-apm-ui.asciidoc @@ -37,6 +37,8 @@ include::metrics.asciidoc[] include::agent-configuration.asciidoc[] +include::custom-links.asciidoc[] + include::advanced-queries.asciidoc[] include::settings.asciidoc[] diff --git a/docs/canvas/canvas-elements.asciidoc b/docs/canvas/canvas-elements.asciidoc index 85f0ee1150ba0..163579d5763b2 100644 --- a/docs/canvas/canvas-elements.asciidoc +++ b/docs/canvas/canvas-elements.asciidoc @@ -23,7 +23,7 @@ By default, most of the elements you create use demo data until you change the d * *{es} SQL* — Access your data in {es} using SQL syntax. For information about SQL syntax, refer to {ref}/sql-spec.html[SQL language]. -* *{es} raw data* — Access your raw data in {es} without the use of aggregations. Use {es} raw data when you have low volume datasets, or to plot exact, non-aggregated values. +* *{es} documents* — Access your data in {es} without using aggregations. To use, select an index and fields, and optionally enter a query using the <<lucene-query,Lucene Query Syntax>>. Use the *{es} documents* data source when you have low volume datasets, to view raw documents, or to plot exact, non-aggregated values on a chart. * *Timelion* — Access your time series data using <<timelion,Timelion>> queries. To use Timelion queries, you can enter a query using the <<lucene-query,Lucene Query Syntax>>. diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md index 7e65ef85c8bec..afb6ea88f9fad 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md @@ -10,7 +10,7 @@ search: { aggs: { AggConfigs: typeof AggConfigs; - aggGroupNamesMap: () => Record<"buckets" | "metrics", string>; + aggGroupNamesMap: () => Record<"metrics" | "buckets", string>; aggTypeFilters: import("./search/aggs/filter/agg_type_filters").AggTypeFilters; CidrMask: typeof CidrMask; convertDateRangeToString: typeof convertDateRangeToString; diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index e756eb9b72905..259d725b3bf0d 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -23,6 +23,7 @@ | Function | Description | | --- | --- | | [getDefaultSearchParams(config)](./kibana-plugin-plugins-data-server.getdefaultsearchparams.md) | | +| [getTotalLoaded({ total, failed, successful })](./kibana-plugin-plugins-data-server.gettotalloaded.md) | | | [parseInterval(interval)](./kibana-plugin-plugins-data-server.parseinterval.md) | | | [plugin(initializerContext)](./kibana-plugin-plugins-data-server.plugin.md) | Static code to be shared externally | | [shouldReadFieldFromDocValues(aggregatable, esType)](./kibana-plugin-plugins-data-server.shouldreadfieldfromdocvalues.md) | | diff --git a/docs/management/alerting/alert-details.asciidoc b/docs/management/alerting/alert-details.asciidoc new file mode 100644 index 0000000000000..b108f79fe5bad --- /dev/null +++ b/docs/management/alerting/alert-details.asciidoc @@ -0,0 +1,34 @@ +[role="xpack"] +[[alert-details]] +=== Alert details + +beta[] + +The *Alert details* page tells you about the state of the alert and provides granular control over the actions it is taking. + +[role="screenshot"] +image::images/alerts-details-instances-active.png[Alert details page with three alert instances] + +In this example, alerts detect when a site serves more than a threshold number of bytes in a 24 hour period. Three sites are above the threshold. These are called alert instances - occurrences of the condition being detected - and the instance name, status, time of detection, and duration of the condition are shown in this view. + +Upon detection, each instance can trigger one or more actions. If the condition persists, the same actions will trigger either on the next scheduled alert check, or (if defined) after the re-notify period on the alert has passed. To prevent re-notification, you can suppress future actions by clicking on the eye icon to mute an individual alert instance. Muting means that the alert checks continue to run on a schedule, but that instance will not trigger any action. + +[role="screenshot"] +image::images/alerts-details-instance-muting.png[Muting an alert instance] + +Alert instances will come and go from the list depending on whether they meet the alert conditions or not - unless they are muted. If a muted instance no longer meets the alert conditions, it will appear as inactive in the list. This prevents an instance from triggering actions if it reappears in the future. + +[role="screenshot"] +image::images/alerts-details-instances-inactive.png[Alert details page with three inactive alert instances] + +If you want to suppress actions on all current and future instances, you can mute the entire alert. Alert checks continue to run and the instance list will update as instances activate or deactivate, but no actions will be triggered. + +[role="screenshot"] +image::images/alerts-details-muting.png[Use the mute toggle to suppress all action on current and future instances] + +You can also disable an alert altogether. When disabled, the alert stops running checks altogether and will clear any instances it is tracking. You may want to disable alerts that are not currently needed to reduce the load on {kib} and {es}. + +[role="screenshot"] +image::images/alerts-details-disabling.png[Use the disable toggle to turn off alert checks and clear instances tracked] + +* For further information on alerting concepts and examples, see <<alerting-getting-started>>. diff --git a/docs/management/alerting/alert-management.asciidoc b/docs/management/alerting/alert-management.asciidoc new file mode 100644 index 0000000000000..caf260937b7be --- /dev/null +++ b/docs/management/alerting/alert-management.asciidoc @@ -0,0 +1,59 @@ +[role="xpack"] +[[alert-management]] +=== Managing Alerts + +beta[] + +The *Alerts* tab provides a cross-app view of alerting. Different {kib} apps like <<xpack-infra, Metrics>>, <<xpack-apm, APM>>, <<xpack-uptime, Uptime>>, and <<xpack-siem, SIEM>> can offer their own alerts, and the *Alerts* tab provides a central place to: + +* <<create-edit-alerts, Create and edit>> alerts +* <<controlling-alerts, Control alerts>> including enabling/disabling, muting/unmuting, and deleting +* Drill-down to <<alert-details, alert details>> + +[role="screenshot"] +image:management/alerting/images/alerts-and-actions-ui.png[Example alert listing in the Alerts and Actions UI] + +For more information on alerting concepts and the types of alerts and actions available, see <<alerting-getting-started>>. + +[float] +==== Finding alerts + +The *Alerts* tab lists all alerts in the current space, including summary information about their execution frequency, tags, and type. + +The *search bar* can be used to quickly find alerts by name or tag. + +[role="screenshot"] +image::images/alerts-filter-by-search.png[Filtering the alerts list using the search bar] + +The *type* dropdown lets you filter to a subset of alert types. + +[role="screenshot"] +image::images/alerts-filter-by-type.png[Filtering the alerts list by types of alert] + +The *Action type* dropdown lets you filter by the type of action used in the alert. + +[role="screenshot"] +image::images/alerts-filter-by-action-type.png[Filtering the alert list by type of action] + +[float] +[[create-edit-alerts]] +==== Creating and editing alerts + +Many alerts must be created within the context of a {kib} app like <<xpack-infra, Metrics>>, <<xpack-apm, APM>>, or <<xpack-uptime, Uptime>>, but others are generic. Generic alert types can be created in the *Alerts* management UI by clicking the *Create* button. This will launch a flyout that guides you through selecting an alert type and configuring it's properties. Refer to <<alert-types>> for details on what types of alerts are available and how to configure them. + +After an alert is created, you can re-open the flyout and change an alerts properties by clicking the *Edit* button shown on each row of the alert listing. + + +[float] +[[controlling-alerts]] +==== Controlling alerts + +The alert listing allows you to quickly mute/unmute, disable/enable, and delete individual alerts by clicking the action button at the right of each row. + +[role="screenshot"] +image:management/alerting/images/individual-mute-disable.png[The actions button allows an individual alert to be muted, disabled, or deleted] + +These operations can also be performed in bulk by multi-selecting alerts and clicking the *Manage alerts* button: + +[role="screenshot"] +image:management/alerting/images/bulk-mute-disable.png[The Manage alerts button lets you mute/unmute, enable/disable, and delete in bulk] \ No newline at end of file diff --git a/docs/management/alerting/alerts-and-actions-intro.asciidoc b/docs/management/alerting/alerts-and-actions-intro.asciidoc new file mode 100644 index 0000000000000..4674c9ba902be --- /dev/null +++ b/docs/management/alerting/alerts-and-actions-intro.asciidoc @@ -0,0 +1,25 @@ +[role="xpack"] +[[managing-alerts-and-actions]] +== Alerts and Actions + +beta[] + +The *Alerts and Actions* UI lets you <<alert-management, see and control all the alerts>> in a space, and provides tools to <<connector-management, create and manage connectors>> so that alerts can trigger actions like notification, indexing, and ticketing. + +To manage alerting and connectors, go to *Management > {kib} > Alerts and Actions*. + +[role="screenshot"] +image:management/alerting/images/alerts-and-actions-ui.png[Example alert listing in the Alerts and Actions UI] + +[NOTE] +============================================================================ +Similar to dashboards, alerts and connectors reside in a <<xpack-spaces, space>>. +The *Alerts and Actions* UI only shows alerts and connectors for the current space. +============================================================================ + +[NOTE] +============================================================================ +{es} also offers alerting capabilities through Watcher, which +can be managed through the <<watcher-ui, Watcher UI>>. See +<<alerting-concepts-differences>> for more information. +============================================================================ \ No newline at end of file diff --git a/docs/management/alerting/connector-management.asciidoc b/docs/management/alerting/connector-management.asciidoc new file mode 100644 index 0000000000000..1002a372f9460 --- /dev/null +++ b/docs/management/alerting/connector-management.asciidoc @@ -0,0 +1,47 @@ +[role="xpack"] +[[connector-management]] +=== Managing Connectors + +beta[] + +Alerts use *Connectors* to route actions to different destinations like log files, ticketing systems, and messaging tools. While each {kib} app can offer their own types of alerts, they typically share connectors. The *Connectors* tab offers a central place to view and manage all the connectors in the current space. + +For more information on connectors and the types of actions available see <<action-types>>. + +[role="screenshot"] +image::images/connector-listing.png[Example connector listing in the Alerts and Actions UI] + + +[float] +==== Connector list + +The *Connectors* tab lists all connectors in the current space. The *search bar* can be used to find specific connectors by name and/or type. + +[role="screenshot"] +image::images/connector-filter-by-search.png[Filtering the connector list using the search bar] + + +The *type* dropdown also lets you filter to a subset of action types. + +[role="screenshot"] +image::images/connector-filter-by-type.png[Filtering the connector list by types of actions] + +The *Actions* column indicates the number of actions that reference the connector. This count helps you confirm a connector is unused before you delete it, and tells you how many actions will be affected when a connector is modified. + +[role="screenshot"] +image::images/connector-action-count.png[Filtering the connector list by types of actions] + +You can delete individual connectors using the trash icon on the right of each row. Connectors can also be deleted in bulk by multi-selecting them and clicking the *Delete* button to the left of the search box. + +[role="screenshot"] +image::images/connector-delete.png[Deleting connectors individually or in bulk] + +[NOTE] +============================================================================ +You can delete a connector even if there are still actions referencing it. +When this happens the action will fail to execute, and appear as errors in the {kib} logs. +============================================================================ + +==== Creating a new connector + +New connectors can be created by clicking the *Create connector* button, which will guide you to select the type of connector and configure it's properties. Refer to <<action-types>> for the types of connectors available and how to configure them. Once you create a connector it will be made available to you anytime you set up an action in the current space. \ No newline at end of file diff --git a/docs/management/alerting/images/alerts-and-actions-ui.png b/docs/management/alerting/images/alerts-and-actions-ui.png new file mode 100644 index 0000000000000..acf3f3b1f0be9 Binary files /dev/null and b/docs/management/alerting/images/alerts-and-actions-ui.png differ diff --git a/docs/management/alerting/images/alerts-details-disabling.png b/docs/management/alerting/images/alerts-details-disabling.png new file mode 100644 index 0000000000000..7057afc8ad07d Binary files /dev/null and b/docs/management/alerting/images/alerts-details-disabling.png differ diff --git a/docs/management/alerting/images/alerts-details-instance-muting.png b/docs/management/alerting/images/alerts-details-instance-muting.png new file mode 100644 index 0000000000000..9d26fad419e4f Binary files /dev/null and b/docs/management/alerting/images/alerts-details-instance-muting.png differ diff --git a/docs/management/alerting/images/alerts-details-instances-active.png b/docs/management/alerting/images/alerts-details-instances-active.png new file mode 100644 index 0000000000000..d6895bd4952b8 Binary files /dev/null and b/docs/management/alerting/images/alerts-details-instances-active.png differ diff --git a/docs/management/alerting/images/alerts-details-instances-inactive.png b/docs/management/alerting/images/alerts-details-instances-inactive.png new file mode 100644 index 0000000000000..b049b4ba082f6 Binary files /dev/null and b/docs/management/alerting/images/alerts-details-instances-inactive.png differ diff --git a/docs/management/alerting/images/alerts-details-muting.png b/docs/management/alerting/images/alerts-details-muting.png new file mode 100644 index 0000000000000..9b47d82a74639 Binary files /dev/null and b/docs/management/alerting/images/alerts-details-muting.png differ diff --git a/docs/management/alerting/images/alerts-filter-by-action-type.png b/docs/management/alerting/images/alerts-filter-by-action-type.png new file mode 100644 index 0000000000000..94336a20e1d6c Binary files /dev/null and b/docs/management/alerting/images/alerts-filter-by-action-type.png differ diff --git a/docs/management/alerting/images/alerts-filter-by-search.png b/docs/management/alerting/images/alerts-filter-by-search.png new file mode 100644 index 0000000000000..df4b6d7d9d9aa Binary files /dev/null and b/docs/management/alerting/images/alerts-filter-by-search.png differ diff --git a/docs/management/alerting/images/alerts-filter-by-type.png b/docs/management/alerting/images/alerts-filter-by-type.png new file mode 100644 index 0000000000000..75ffb3ff69bab Binary files /dev/null and b/docs/management/alerting/images/alerts-filter-by-type.png differ diff --git a/docs/management/alerting/images/bulk-mute-disable.png b/docs/management/alerting/images/bulk-mute-disable.png new file mode 100644 index 0000000000000..04cdc3f03f34d Binary files /dev/null and b/docs/management/alerting/images/bulk-mute-disable.png differ diff --git a/docs/management/alerting/images/connector-action-count.png b/docs/management/alerting/images/connector-action-count.png new file mode 100644 index 0000000000000..b792ae2cc11c8 Binary files /dev/null and b/docs/management/alerting/images/connector-action-count.png differ diff --git a/docs/management/alerting/images/connector-delete.png b/docs/management/alerting/images/connector-delete.png new file mode 100644 index 0000000000000..ccb6bcea4bade Binary files /dev/null and b/docs/management/alerting/images/connector-delete.png differ diff --git a/docs/management/alerting/images/connector-filter-by-search.png b/docs/management/alerting/images/connector-filter-by-search.png new file mode 100644 index 0000000000000..1f052ea23a577 Binary files /dev/null and b/docs/management/alerting/images/connector-filter-by-search.png differ diff --git a/docs/management/alerting/images/connector-filter-by-type.png b/docs/management/alerting/images/connector-filter-by-type.png new file mode 100644 index 0000000000000..06c7d6f4dad47 Binary files /dev/null and b/docs/management/alerting/images/connector-filter-by-type.png differ diff --git a/docs/management/alerting/images/connector-listing.png b/docs/management/alerting/images/connector-listing.png new file mode 100644 index 0000000000000..43014ff88057f Binary files /dev/null and b/docs/management/alerting/images/connector-listing.png differ diff --git a/docs/management/alerting/images/individual-mute-disable.png b/docs/management/alerting/images/individual-mute-disable.png new file mode 100644 index 0000000000000..ca00240a4af61 Binary files /dev/null and b/docs/management/alerting/images/individual-mute-disable.png differ diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index e3b9e61667bdf..d7f1ec637d1df 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -27,7 +27,7 @@ If not set, {kib} will generate a random key on startup, but all alert and actio Although the key can be specified in clear text in `kibana.yml`, it's recommended to store this key securely in the <<secure-settings,{kib} Keystore>>. [float] -[[alert-settings]] +[[action-settings]] ==== Action settings `xpack.actions.whitelistedHosts`:: @@ -41,7 +41,7 @@ A list of action types that are enabled. It defaults to `[*]`, enabling all type Disabled action types will not appear as an option when creating new connectors, but existing connectors and actions of that type will remain in {kib} and will not function. [float] -[[action-settings]] +[[alert-settings]] ==== Alert settings You do not need to configure any additional settings to use alerting in {kib}. diff --git a/docs/user/alerting/action-types.asciidoc b/docs/user/alerting/action-types.asciidoc new file mode 100644 index 0000000000000..02c09736e1fa0 --- /dev/null +++ b/docs/user/alerting/action-types.asciidoc @@ -0,0 +1,182 @@ +[role="xpack"] +[[action-types]] +== Action and connector types + +{kib} provides the following types of actions: + +* <<email-action-type, Email>> +* <<index-action-type, Index>> +* <<pagerduty-action-type, PagerDuty>> +* <<server-log-action-type, ServerLog>> +* <<slack-action-type, Slack>> +* <<webhook-action-type, Webhook>> + +This section describes how to configure connectors and actions for each type. + +[NOTE] +============================================== +Some action types are paid commercial features, while others are free. +For a comparison of the Elastic license levels, +see https://www.elastic.co/subscriptions[the subscription page]. +============================================== + +[float] +[[email-action-type]] +=== Email + +The email action type uses the SMTP protocol to send mail message, using an integration of https://nodemailer.com/[Nodemailer]. Email message text is sent as both plain text and html text. + +[float] +[[email-connector-configuration]] +==== Connector configuration + +Email connectors have the following configuration properties: + +Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action. +Sender:: The from address for all emails sent with this connector, specified in `user@host-name` format. +Host:: Host name of the service provider. If you are using the <<action-settings, `xpack.actions.whitelistedHosts`>> setting, make sure this hostname is whitelisted. +Port:: The port to connect to on the service provider. +Secure:: If true the connection will use TLS when connecting to the service provider. See https://nodemailer.com/smtp/#tls-options[nodemailer TLS documentation] for more information. +Username:: username for 'login' type authentication. +Password:: password for 'login' type authentication. + +[float] +[[email-action-configuration]] +==== Action configuration + +Email actions have the following configuration properties: + +To, CC, BCC:: Each is a list of addresses. Addresses can be specified in `user@host-name` format, or in `name <user@host-name>` format. One of To, CC, or BCC must contain an entry. +Subject:: The subject line of the email. +Message:: The message text of the email. Markdown format is supported. + +[float] +[[index-action-type]] +=== Index + +The index action type will index a document into {es}. + +[float] +[[index-connector-configuration]] +==== Connector configuration + +Index connectors have the following configuration properties: + +Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action. +Index:: The {es} index to be written to. +Refresh:: Setting for the {ref}/docs-refresh.html[refresh] policy for the write request. +Execution time field:: This field will be automatically set to the time the alert condition was detected. + +[float] +[[index-action-configuration]] +==== Action configuration + +Index actions have the following properties: + +Document:: The document to index in json format. + +[float] +[[pagerduty-action-type]] +=== PagerDuty + +The PagerDuty action type uses the https://v2.developer.pagerduty.com/docs/events-api-v2[v2 Events API] to trigger, acknowledge, and resolve PagerDuty alerts. + +[float] +[[pagerduty-connector-configuration]] +==== Connector configuration + +PagerDuty connectors have the following configuration properties: + +Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action. +API URL:: An optional PagerDuty event URL. Defaults to `https://events.pagerduty.com/v2/enqueue`. If you are using the <<action-settings, `xpack.actions.whitelistedHosts`>> setting, make sure the hostname is whitelisted. +Routing Key:: A 32 character PagerDuty Integration Key for an integration on a service or on a global ruleset. + +[float] +[[pagerduty-action-configuration]] +==== Action configuration + +PagerDuty actions have the following properties: + +Severity:: The perceived severity of on the affected system. This can be one of `Critical`, `Error`, `Warning` or `Info`(default). +Event action:: One of `Trigger` (default), `Resolve`, or `Acknowledge`. See https://v2.developer.pagerduty.com/docs/events-api-v2#event-action[event action] for more details. +Dedup Key:: All actions sharing this key will be associated with the same PagerDuty alert. This value is used to correlate trigger and resolution. This value is *optional*, and if unset defaults to `action:<action saved object id>`. The maximum length is *255* characters. See https://v2.developer.pagerduty.com/docs/events-api-v2#alert-de-duplication[alert deduplication] for details. +Timestamp:: An *optional* https://v2.developer.pagerduty.com/v2/docs/types#datetime[ISO-8601 format date-time], indicating the time the event was detected or generated. +Component:: An *optional* value indicating the component of the source machine that is responsible for the event, for example `mysql` or `eth0`. +Group:: An *optional* value indicating the logical grouping of components of a service, for example `app-stack`. +Source:: An *optional* value indicating the affected system, preferably a hostname or fully qualified domain name. Defaults to the {kib} saved object id of the action. +Summary:: An *optional* text summary of the event, defaults to `No summary provided`. The maximum length is 1024 characters. +Class:: An *optional* value indicating the class/type of the event, for example `ping failure` or `cpu load`. + +For more details on these properties, see https://v2.developer.pagerduty.com/v2/docs/send-an-event-events-api-v2[PagerDuty v2 event parameters]. + +[float] +[[server-log-action-type]] +=== Server log + +This action type writes and entry to the {kib} server log. + +[float] +[[server-log-connector-configuration]] +==== Connector configuration + +Server log connectors have the following configuration properties: + +Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action. + +[float] +[[server-log-action-configuration]] +==== Action configuration + +Server log actions have the following properties: + +Message:: The message to log. + +[float] +[[slack-action-type]] +=== Slack + +The Slack action type uses https://api.slack.com/incoming-webhooks[Slack Incoming Webhooks]. + +[float] +[[slack-connector-configuration]] +==== Connector configuration + +Slack connectors have the following configuration properties: + +Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action. +Webhook URL:: The URL of the incoming webhook. See https://api.slack.com/messaging/webhooks#getting_started[Slack Incoming Webhooks] for instructions on generating this URL. If you are using the <<action-settings, `xpack.actions.whitelistedHosts`>> setting, make sure the hostname is whitelisted. + +[float] +[[slack-action-configuration]] +==== Action configuration + +Slack actions have the following properties: + +Message:: The message text, converted to the `text` field in the Webhook JSON payload. Currently only the text field is supported. Markdown, images, and other advanced formatting are not yet supported. + +[float] +[[webhook-action-type]] +=== Webhook + +The Webhook action type uses https://github.com/axios/axios[axios] to send a POST or PUT request to a web service. + +[float] +[[webhook-connector-configuration]] +==== Connector configuration + +Webhook connectors have the following configuration properties: + +Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action. +URL:: The request URL. If you are using the <<action-settings, `xpack.actions.whitelistedHosts`>> setting, make sure the hostname is whitelisted. +Method:: HTTP request method, either `post`(default) or `put`. +Headers:: A set of key-value pairs sent as headers with the request +User:: An optional username. If set, HTTP basic authentication is used. Currently only basic authentication is supported. +Password:: An optional password. If set, HTTP basic authentication is used. Currently only basic authentication is supported. + +[float] +[[webhook-action-configuration]] +==== Action configuration + +Webhook actions have the following properties: + +Body:: A json payload sent to the request URL. \ No newline at end of file diff --git a/docs/user/alerting/alert-types.asciidoc b/docs/user/alerting/alert-types.asciidoc new file mode 100644 index 0000000000000..4a99c70f9d961 --- /dev/null +++ b/docs/user/alerting/alert-types.asciidoc @@ -0,0 +1,115 @@ +[role="xpack"] +[[alert-types]] +== Alert types + +{kib} supplies alerts types in two ways: some are built into {kib}, while domain-specific alert types are registered by {kib} apps such as <<xpack-apm,*APM*>>, <<xpack-infra,*Metrics*>>, and <<xpack-uptime,*Uptime*>>. + +This section covers built-in alert types. For domain-specific alert types, refer to the documentation for that app. + +Currently {kib} provides one built-in alert type: the <<alert-type-index-threshold>> type. + +[float] +[[alert-type-index-threshold]] +=== Index threshold + +The index threshold alert type is designed to run an {es} query over indices, aggregating field values from documents, comparing them to threshold values, and scheduling actions to run when the thresholds are met. + +[float] +==== Creating the alert + +An index threshold alert can be created from the *Create* button in the <<alert-management, alert management UI>>. Fill in the <<defining-alerts-general-details, general alert details>>, then select *Index Threshold*. + +[role="screenshot"] +image::images/alert-types-index-threshold-select.png[Choosing an index threshold alert type] + +[float] +==== Defining the conditions + +The index threshold has 5 clauses that define the condition to detect. + +[role="screenshot"] +image::images/alert-types-index-threshold-conditions.png[Five clauses define the condition to detect] + +Index:: This clause requires an *index or index pattern* and a *time field* that will be used for the *time window*. +When:: This clause specifies how the value to be compared to the threshold is calculated. The value is calculated by aggregating a numeric field a the *time window*. The aggregation options are: `count`, `average`, `sum`, `min`, and `max`. When using `count` the document count is used, and an aggregation field is not necessary. +Over/Grouped Over:: This clause lets you configure whether the aggregation is applied over all documents, or should be split into groups using a grouping field. If grouping is used, an <<alerting-concepts-alert-instances, alert instance>> will be created for each group when it exceeds the threshold. To limit the number of instances on high cardinality fields, you must specify the number of groups to check against the threshold. Only the *top* groups are checked. +Threshold:: This clause defines a threshold value and a comparison operator (one of `is above`, `is above or equals`, `is below`, `is below or equals`, or `is between`). The result of the aggregation is compared to this threshold. +Time window:: This clause determines how far back to search for documents, using the *time field* set in the *index* clause. Generally this value should be to a value higher than the *check every* value in the <<defining-alerts-general-details, general alert details>>, to avoid gaps in detection. + +If data is available and all clauses have been defined, a preview chart will render the threshold value and display a line chart showing the value for the last 30 intervals. This can provide an indication of recent values and their proximity to the threshold, and help you tune the clauses. + +[role="screenshot"] +image::images/alert-types-index-threshold-preview.png[Five clauses define the condition to detect] + +[float] +=== Example + +In this section, you will use the {kib} <<add-sample-data, weblog sample dataset>> to setup and tune the conditions on an index threshold alert. For this example, we want to detect when any of our top three sites have served more than 420,000 bytes over a 24 hour period. + +From the <<alert-management, alert management UI>>, create a new alert, and fill in the <<defining-alerts-general-details, general alert details>>. This alert will be checked every 4 hours, and will not execute actions more than once per day. Choose the index threshold alert type. + +[role="screenshot"] +image::images/alert-types-index-threshold-select.png[Choosing an index threshold alert type] + +Click on each clause to open a control that helps you set the value: + +[float] +==== Index clause +The index clause control will list and allow you to search for available indices. Choose *kibana_sample_data_logs* + +[role="screenshot"] +image::images/alert-types-index-threshold-example-index.png[Choosing an index] + +Once an index is selected, the list of time fields for that index will be available to select. Choose *@timestamp*. + +[role="screenshot"] +image::images/alert-types-index-threshold-example-timefield.png[Choosing a time field] + +[float] +==== When clause + +We want to detect the number of bytes served during the time window, so we select `sum` as the aggregation, and `bytes` as the field to aggregate. + +[role="screenshot"] +image::images/alert-types-index-threshold-example-aggregation.png[Choosing the aggregation] + +[float] +==== Over/Grouped over clause + +We want to alert on the three sites that have the most traffic, so we'll group the sum of bytes by the `host.keyword` field and take the top 3 values. + +[role="screenshot"] +image::images/alert-types-index-threshold-example-grouping.png[Choosing the groups] + +[float] +==== Threshold clause + +We want to alert when any site exceeds 420,000 bytes over a 24 hour period, so we'll set the threshold to 420,000 and use the `is above` comparison. + +[role="screenshot"] +image::images/alert-types-index-threshold-example-threshold.png[Setting the threshold] + +[float] +==== Time window clause + +Finally, set the time window to 24 hours to complete the alert configuration. + +[role="screenshot"] +image::images/alert-types-index-threshold-example-window.png[Setting the time window] + +The preview chart will render showing the 24 hour sum of bytes at 4 hours intervals (the *check every* interval) for the past 120 hours (the last 30 intervals). + +[role="screenshot"] +image::images/alert-types-index-threshold-example-preview.png[Setting the time window] + +[float] +==== Comparing time windows + +You can interactively change the time window and observe the effect it has on the chart. Compare a 24 window to a 12 hour window. Notice the variability in the sum of bytes, due to different traffic levels during the day compared to at night. This variability would result in noisy alerts, so the 24 hour window is better. The preview chart can help you find the right values for your alert. + +[role="screenshot"] +image::images/alert-types-index-threshold-example-comparison.png[Comparing two time windows] + + + + diff --git a/docs/user/alerting/alerting-scale-performance.asciidoc b/docs/user/alerting/alerting-scale-performance.asciidoc new file mode 100644 index 0000000000000..644a7143f8278 --- /dev/null +++ b/docs/user/alerting/alerting-scale-performance.asciidoc @@ -0,0 +1,28 @@ +[role="xpack"] +[[alerting-scale-performance]] +== Scale and performance + +{kib} alerting run both alert checks and actions as persistent background tasks. This has two major benefits: + +* *Persistence*: all task state and scheduling is stored in {es}, so if {kib} is restarted, alerts and actions will pick up where they left off. +* *Scaling*: multiple {kib} instances can read from and update the same task queue in {es}, allowing the alerting and action load to be distributed across instances. In cases where a {kib} instance no longer has capacity to run alert checks or actions, capacity can be increased by adding additional {kib} instances. + +[float] +=== Running background alert checks and actions + +{kib} background tasks are managed by: + +* Polling an {es} task index for overdue tasks at 3 second intervals. +* Tasks are then claiming them by updating them in the {es} index, using optimistic concurrency control to prevent conflicts. Each {kib} instance can run a maximum of 10 concurrent tasks, so a maximum of 10 tasks are claimed each interval. +* Tasks are run on the {kib} server. +* In the case of alerts which are recurring background checks, upon completion the task is scheduled again according to the <<defining-alerts-general-details, check interval>>. + +[IMPORTANT] +============================================== +Because tasks are polled at 3 second intervals and only 10 tasks can run concurrently per {kib} instance, it is possible for alert and action tasks to be run late. This can happen if: + +* Alerts use a small *check interval*. The lowest interval possible is 3 seconds, though intervals of 30 seconds or higher are recommended. +* Many alerts or actions must be *run at once*. In this case pending tasks will queue in {es}, and be pulled 10 at a time from the queue at 3 second intervals. +* *Long running tasks* occupy slots for an extended time, leaving fewer slots for other tasks. + +============================================== \ No newline at end of file diff --git a/docs/user/alerting/defining-alerts.asciidoc b/docs/user/alerting/defining-alerts.asciidoc new file mode 100644 index 0000000000000..89c4c88708d58 --- /dev/null +++ b/docs/user/alerting/defining-alerts.asciidoc @@ -0,0 +1,80 @@ +[role="xpack"] +[[defining-alerts]] +== Defining alerts + +{kib} alerts can be created in a variety of apps including <<xpack-apm,*APM*>>, <<xpack-infra,*Metrics*>>, <<xpack-siem,*SIEM*>>, <<xpack-uptime,*Uptime*>> and from <<management,*Management*>> UI. While alerting details may differ from app to app, they share a common interface for defining and configuring alerts that this section describes in more detail. + +[float] +=== Alert flyout + +When an alert is created in an app, the app will display a flyout panel with three main sections to configure: + +. <<defining-alerts-general-details, General alert details>> +. <<defining-alerts-type-conditions, Alert type and conditions>> +. <<defining-alerts-actions-details, Action type and action details>> + +image::images/alert-flyout-sections.png[The three sections of an alert definition] + +[float] +[[defining-alerts-general-details]] +=== General alert details + +All alert share the following four properties in common: + +[role="screenshot"] +image::images/alert-flyout-general-details.png[All alerts have name, tags, check every, and re-notify every properties in common] + +Name:: The name of the alert. While this name does not have to be unique, the name can be referenced in actions and also appears in the searchable alert listing in the management UI. A distinctive name can help identify and find an alert. +Tags:: A list of tag names that can be applied to an alert. Tags can help you organize and find alerts, because tags appear in the alert listing in the management UI which is searchable by tag. +Check every:: This value determines how frequently the alert conditions below are checked. Note that the timing of background alert checks are not guaranteed, particularly for intervals of less than 10 seconds. See <<alerting-scale-performance>> for more information. +Re-notify every:: This value limits how often actions are repeated when an alert instance remains active across alert checks. See <<alerting-concepts-suppressing-duplicate-notifications>> for more information. + +[float] +[[defining-alerts-type-conditions]] +=== Alert type and conditions + +Depending upon the {kib} app and context, you may be prompted to choose the type of alert you wish to create. Some apps will pre-select the type of alert for you. + +[role="screenshot"] +image::images/alert-flyout-alert-type-selection.png[Choosing the type of alert to create] + +Each alert type provides its own way of defining the conditions to detect, but an expression formed by a series of clauses is a common pattern. Each clause has a UI control that allows you to define the clause. For example, in an index threshold alert the `WHEN` clause allows you to select an aggregation operation to apply to a numeric field. + +[role="screenshot"] +image::images/alert-flyout-alert-conditions.png[UI for defining alert conditions on an index threshold alert] + +[float] +[[defining-alerts-actions-details]] +=== Action type and action details + +To add an action to an alert, you first select the type of action: + +[role="screenshot"] +image::images/alert-flyout-action-type-selection.png[UI for selecting an action type] + +Each action must specify a <<alerting-concepts-connectors, connector>> instance. If no connectors exist for that action type, click "Add new" to create one. + +Each action type exposes different properties. For example an email action allows you to set the recipients, the subject, and a message body in markdown format. See <<action-types>> for details on the types of actions provided by {kib} and their properties. + +[role="screenshot"] +image::images/alert-flyout-action-details.png[UI for defining an email action] + +Using the https://mustache.github.io/[Mustache] template syntax `{{variable name}}`, you can pass alert values at the time a condition is detected to an action. Available variables differ by alert type, and a list can be accessed using the "add variable" button at the right of the text box. + +[role="screenshot"] +image::images/alert-flyout-action-variables.png[Passing alert values to an action] + +You can attach more than one action. Clicking the "Add action" button will prompt you to select another alert type and repeat the above steps again. + +[role="screenshot"] +image::images/alert-flyout-add-action.png[You can add multiple actions on an alert] + +[NOTE] +============================================== +Actions are not required on alerts. In some cases you may want to run an alert without actions first to understand its behavior, and configure actions later. +============================================== + +[float] +=== Managing alerts + +To modify an alert after it was created, including muting or disabling it, use the <<alert-management, alert listing in the Management UI>>. \ No newline at end of file diff --git a/docs/user/alerting/images/alert-concepts-connectors.svg b/docs/user/alerting/images/alert-concepts-connectors.svg new file mode 100644 index 0000000000000..9ae4c21ee5870 --- /dev/null +++ b/docs/user/alerting/images/alert-concepts-connectors.svg @@ -0,0 +1 @@ +<svg version="1.1" viewBox="0.0 0.0 756.2965879265092 405.96587926509187" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l756.2966 0l0 405.96588l-756.2966 0l0 -405.96588z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l756.2966 0l0 405.96588l-756.2966 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m13.120735 28.889763l164.37796 0l0 217.51181l-164.37796 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m13.120735 28.889763l164.37796 0l0 217.51181l-164.37796 0z" fill-rule="evenodd"/><path fill="#000000" d="m22.089485 55.80976l5.125 -13.359375l1.90625 0l5.46875 13.359375l-2.015625 0l-1.546875 -4.046875l-5.59375 0l-1.46875 4.046875l-1.875 0zm3.859375 -5.484375l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm9.802948 5.484375l0 -13.359375l1.640625 0l0 13.359375l-1.640625 0zm10.816696 -3.109375l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.296875q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm9.125717 5.765625l0 -9.671875l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.0625l-1.625 0zm9.806427 -1.46875l0.234375 1.453125q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm12.507233 1.46875l-1.640625 0l0 -10.453125q-0.59375 0.5625 -1.5625 1.140625q-0.953125 0.5625 -1.71875 0.84375l0 -1.59375q1.375 -0.640625 2.40625 -1.5625q1.03125 -0.921875 1.453125 -1.78125l1.0625 0l0 13.40625z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m17.565195 73.062996l152.09448 0l0 25.51181l-152.09448 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m17.565195 73.062996l152.09448 0l0 25.51181l-152.09448 0z" fill-rule="evenodd"/><path fill="#000000" d="m35.174572 87.471115l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875019 0 -2.750002 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546877 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.2187519 0 -2.031252 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.7812519 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.4592743 -0.125q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307358 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm13.072983 0l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm7.291733 -5.21875l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm6.0686455 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594757 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717743 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307358 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m17.565195 108.86232l152.09448 0l0 29.952759l-152.09448 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m17.565195 108.86232l152.09448 0l0 29.952759l-152.09448 0z" fill-rule="evenodd"/><path fill="#000000" d="m27.221445 123.56732l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.7968769 0 1.4062519 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.8750019 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.1718769 0 2.062502 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125019 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.7031269 0.375 2.328127 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015627 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm15.041231 0.59375l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm2.375 2.78125l0 -10.484375l1.28125 0l0 3.75q0.90625 -1.03125 2.28125 -1.03125q0.84375 0 1.46875 0.328125q0.625 0.328125 0.890625 0.921875q0.265625 0.578125 0.265625 1.703125l0 4.8125l-1.28125 0l0 -4.8125q0 -0.96875 -0.421875 -1.40625q-0.421875 -0.4375 -1.1875 -0.4375q-0.578125 0 -1.078125 0.296875q-0.5 0.296875 -0.71875 0.8125q-0.21875 0.5 -0.21875 1.390625l0 4.15625l-1.28125 0zm13.354233 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm12.104229 4.53125l0 -0.953125q-0.71875 1.125 -2.1249962 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6874962 0 1.2187462 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.062496 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.8437462 0 1.4374962 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.4843712 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm12.276104 3.796875l0 -1.109375q-0.890625 1.28125 -2.421875 1.28125q-0.671875 0 -1.25 -0.25q-0.578125 -0.265625 -0.875 -0.65625q-0.28125 -0.390625 -0.390625 -0.953125q-0.078125 -0.375 -0.078125 -1.203125l0 -4.703125l1.28125 0l0 4.203125q0 1.015625 0.078125 1.359375q0.125 0.515625 0.515625 0.8125q0.40625 0.28125 0.984375 0.28125q0.578125 0 1.078125 -0.296875q0.515625 -0.296875 0.71875 -0.8125q0.21875 -0.515625 0.21875 -1.484375l0 -4.0625l1.28125 0l0 7.59375l-1.140625 0zm3.1354828 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.490524 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m17.565195 146.05766l152.09448 0l0 93.41733l-152.09448 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m17.565195 146.05766l152.09448 0l0 93.41733l-152.09448 0z" fill-rule="evenodd"/><path fill="#000000" d="m26.54957 164.13766l4.015625 -10.484375l1.5000019 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375002 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.562502 0l-1.09375 -2.90625q-0.5000019 -1.3125 -0.7500019 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm12.697481 1.53125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm5.1875 1.625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594757 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717743 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307358 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6874962 0 1.2656212 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.3749962 -0.234375 -0.8906212 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm7.635479 -2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m30.065617 172.23885l125.79527 0l0 56.850388l-125.79527 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m30.065617 172.23885l125.79527 0l0 56.850388l-125.79527 0z" fill-rule="evenodd"/><path fill="#000000" d="m42.17499 191.60684l0 -7.578125l-2.828125 0l0 -1.015625l6.8125 0l0 1.015625l-2.84375 0l0 7.578125l-1.140625 0zm4.3085938 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm6.046875 0l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.0937538 0.125q-0.25 0.953125 -0.9531288 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.7968788 0.84375 0.7968788 2.390625q0 0.09375 0 0.28125l-4.640629 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm6.158207 -1.3125l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m113.39179 189.49747l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.173828 3.65625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.199211 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.0937424 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.3593674 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.0937424 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.7968674 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.9218674 0.078125 -1.3124924 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.5312424 0 1.0156174 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm3.5957031 -4.234375l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6191406 0l1.796875 -8.59375l1.0625 0l-1.796875 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m46.128117 205.59122l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.9843788 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.6718788 0 -1.1718788 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923832 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm0.6464844 -2.171875q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.970703 3.109375l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm4.2929688 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m108.15742 208.60684l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm5.796875 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm7.3339767 -2.265625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.6562424 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.1562424 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.26561737 0.828125 -0.26561737 1.578125q0 0.796875 0.34374237 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.90625 -0.09375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125z" fill-rule="nonzero"/><path fill="#000000" d="m40.159367 221.60684l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm3.3339844 0l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm3.3339844 0l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#ffffff" d="m259.5328 278.57742l245.03937 0l0 113.5748l-245.03937 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m259.5328 278.57742l245.03937 0l0 113.5748l-245.03937 0z" fill-rule="evenodd"/><path fill="#000000" d="m270.11093 306.77744l0 -14.3125l10.359375 0l0 1.6875l-8.453125 0l0 4.375l7.90625 0l0 1.6875l-7.90625 0l0 4.875l8.78125 0l0 1.6875l-10.6875 0zm13.074219 0l0 -10.375l1.578125 0l0 1.453125q0.484375 -0.75 1.296875 -1.21875q0.8125 -0.46875 1.84375 -0.46875q1.15625 0 1.890625 0.484375q0.734375 0.46875 1.046875 1.328125q1.234375 -1.8125 3.203125 -1.8125q1.546875 0 2.375 0.859375q0.828125 0.859375 0.828125 2.625l0 7.125l-1.75 0l0 -6.53125q0 -1.0625 -0.171875 -1.515625q-0.171875 -0.46875 -0.625 -0.75q-0.4375 -0.28125 -1.046875 -0.28125q-1.09375 0 -1.828125 0.734375q-0.71875 0.71875 -0.71875 2.3125l0 6.03125l-1.75 0l0 -6.734375q0 -1.171875 -0.4375 -1.75q-0.421875 -0.59375 -1.40625 -0.59375q-0.734375 0 -1.375 0.390625q-0.625 0.390625 -0.90625 1.140625q-0.28125 0.75 -0.28125 2.171875l0 5.375l-1.765625 0zm23.441406 -1.28125q-0.984375 0.828125 -1.890625 1.171875q-0.90625 0.34375 -1.9375 0.34375q-1.703125 0 -2.625 -0.828125q-0.921875 -0.84375 -0.921875 -2.140625q0 -0.765625 0.34375 -1.390625q0.359375 -0.625 0.921875 -1.0q0.5625 -0.390625 1.265625 -0.59375q0.515625 -0.125 1.5625 -0.265625q2.125 -0.25 3.125 -0.59375q0.015625 -0.359375 0.015625 -0.46875q0 -1.0625 -0.5 -1.515625q-0.671875 -0.59375 -2.0 -0.59375q-1.25 0 -1.84375 0.4375q-0.578125 0.4375 -0.859375 1.546875l-1.71875 -0.234375q0.234375 -1.109375 0.765625 -1.78125q0.53125 -0.6875 1.546875 -1.046875q1.015625 -0.375 2.359375 -0.375q1.328125 0 2.15625 0.3125q0.828125 0.3125 1.21875 0.796875q0.390625 0.46875 0.546875 1.1875q0.09375 0.453125 0.09375 1.625l0 2.34375q0 2.453125 0.109375 3.109375q0.109375 0.640625 0.453125 1.234375l-1.84375 0q-0.265625 -0.546875 -0.34375 -1.28125zm-0.15625 -3.921875q-0.953125 0.390625 -2.875 0.65625q-1.078125 0.15625 -1.53125 0.359375q-0.4375 0.1875 -0.6875 0.5625q-0.25 0.375 -0.25 0.84375q0 0.703125 0.53125 1.171875q0.53125 0.46875 1.5625 0.46875q1.015625 0 1.796875 -0.4375q0.796875 -0.453125 1.171875 -1.21875q0.28125 -0.609375 0.28125 -1.765625l0 -0.640625zm4.513672 -7.09375l0 -2.015625l1.765625 0l0 2.015625l-1.765625 0zm0 12.296875l0 -10.375l1.765625 0l0 10.375l-1.765625 0zm4.3964844 0l0 -14.3125l1.75 0l0 14.3125l-1.75 0zm16.8125 -3.796875l1.71875 0.21875q-0.28125 1.796875 -1.453125 2.8125q-1.15625 1.0 -2.859375 1.0q-2.125 0 -3.421875 -1.390625q-1.296875 -1.390625 -1.296875 -3.984375q0 -1.6875 0.546875 -2.9375q0.5625 -1.265625 1.703125 -1.890625q1.140625 -0.640625 2.484375 -0.640625q1.6875 0 2.75 0.859375q1.078125 0.859375 1.390625 2.421875l-1.71875 0.265625q-0.234375 -1.046875 -0.859375 -1.5625q-0.625 -0.53125 -1.5 -0.53125q-1.328125 0 -2.15625 0.953125q-0.828125 0.953125 -0.828125 3.0q0 2.09375 0.796875 3.046875q0.796875 0.9375 2.09375 0.9375q1.03125 0 1.71875 -0.625q0.703125 -0.640625 0.890625 -1.953125zm2.578125 -1.390625q0 -2.875 1.59375 -4.265625q1.34375 -1.15625 3.265625 -1.15625q2.140625 0 3.484375 1.40625q1.359375 1.40625 1.359375 3.875q0 2.0 -0.59375 3.15625q-0.59375 1.140625 -1.75 1.78125q-1.140625 0.625 -2.5 0.625q-2.1875 0 -3.53125 -1.390625q-1.328125 -1.40625 -1.328125 -4.03125zm1.796875 0q0 2.0 0.859375 2.984375q0.875 0.984375 2.203125 0.984375q1.3125 0 2.171875 -0.984375q0.875 -1.0 0.875 -3.046875q0 -1.921875 -0.875 -2.90625q-0.875 -1.0 -2.171875 -1.0q-1.328125 0 -2.203125 1.0q-0.859375 0.984375 -0.859375 2.96875zm9.966797 5.1875l0 -10.375l1.59375 0l0 1.484375q1.140625 -1.71875 3.296875 -1.71875q0.9375 0 1.71875 0.34375q0.796875 0.328125 1.1875 0.875q0.390625 0.546875 0.546875 1.296875q0.09375 0.5 0.09375 1.71875l0 6.375l-1.765625 0l0 -6.3125q0 -1.078125 -0.203125 -1.609375q-0.203125 -0.53125 -0.734375 -0.84375q-0.515625 -0.3125 -1.21875 -0.3125q-1.125 0 -1.9375 0.71875q-0.8125 0.703125 -0.8125 2.6875l0 5.671875l-1.765625 0zm11.123047 0l0 -10.375l1.59375 0l0 1.484375q1.140625 -1.71875 3.296875 -1.71875q0.9375 0 1.71875 0.34375q0.796875 0.328125 1.1875 0.875q0.390625 0.546875 0.546875 1.296875q0.09375 0.5 0.09375 1.71875l0 6.375l-1.765625 0l0 -6.3125q0 -1.078125 -0.203125 -1.609375q-0.203125 -0.53125 -0.734375 -0.84375q-0.515625 -0.3125 -1.21875 -0.3125q-1.125 0 -1.9375 0.71875q-0.8125 0.703125 -0.8125 2.6875l0 5.671875l-1.765625 0zm18.232422 -3.34375l1.8125 0.234375q-0.421875 1.578125 -1.59375 2.46875q-1.15625 0.875 -2.96875 0.875q-2.265625 0 -3.609375 -1.390625q-1.328125 -1.40625 -1.328125 -3.9375q0 -2.625 1.34375 -4.0625q1.34375 -1.453125 3.5 -1.453125q2.078125 0 3.390625 1.421875q1.328125 1.40625 1.328125 3.984375q0 0.15625 -0.015625 0.46875l-7.734375 0q0.09375 1.703125 0.96875 2.609375q0.875 0.90625 2.171875 0.90625q0.96875 0 1.640625 -0.5q0.6875 -0.515625 1.09375 -1.625zm-5.78125 -2.84375l5.796875 0q-0.109375 -1.296875 -0.65625 -1.953125q-0.84375 -1.015625 -2.1875 -1.015625q-1.203125 0 -2.03125 0.8125q-0.828125 0.796875 -0.921875 2.15625zm16.576172 2.390625l1.71875 0.21875q-0.28125 1.796875 -1.453125 2.8125q-1.15625 1.0 -2.859375 1.0q-2.125 0 -3.421875 -1.390625q-1.296875 -1.390625 -1.296875 -3.984375q0 -1.6875 0.546875 -2.9375q0.5625 -1.265625 1.703125 -1.890625q1.140625 -0.640625 2.484375 -0.640625q1.6875 0 2.75 0.859375q1.078125 0.859375 1.390625 2.421875l-1.71875 0.265625q-0.234375 -1.046875 -0.859375 -1.5625q-0.625 -0.53125 -1.5 -0.53125q-1.328125 0 -2.15625 0.953125q-0.828125 0.953125 -0.828125 3.0q0 2.09375 0.796875 3.046875q0.796875 0.9375 2.09375 0.9375q1.03125 0 1.71875 -0.625q0.703125 -0.640625 0.890625 -1.953125zm7.0625 2.21875l0.25 1.5625q-0.734375 0.15625 -1.328125 0.15625q-0.953125 0 -1.484375 -0.296875q-0.515625 -0.3125 -0.734375 -0.796875q-0.21875 -0.5 -0.21875 -2.078125l0 -5.96875l-1.28125 0l0 -1.375l1.28125 0l0 -2.5625l1.75 -1.0625l0 3.625l1.765625 0l0 1.375l-1.765625 0l0 6.0625q0 0.75 0.09375 0.96875q0.09375 0.203125 0.296875 0.34375q0.21875 0.125 0.609375 0.125q0.28125 0 0.765625 -0.078125zm1.0722656 -3.609375q0 -2.875 1.59375 -4.265625q1.34375 -1.15625 3.265625 -1.15625q2.140625 0 3.484375 1.40625q1.359375 1.40625 1.359375 3.875q0 2.0 -0.59375 3.15625q-0.59375 1.140625 -1.75 1.78125q-1.140625 0.625 -2.5 0.625q-2.1875 0 -3.53125 -1.390625q-1.328125 -1.40625 -1.328125 -4.03125zm1.796875 0q0 2.0 0.859375 2.984375q0.875 0.984375 2.203125 0.984375q1.3125 0 2.171875 -0.984375q0.875 -1.0 0.875 -3.046875q0 -1.921875 -0.875 -2.90625q-0.875 -1.0 -2.171875 -1.0q-1.328125 0 -2.203125 1.0q-0.859375 0.984375 -0.859375 2.96875zm9.951172 5.1875l0 -10.375l1.578125 0l0 1.578125q0.609375 -1.109375 1.125 -1.453125q0.515625 -0.359375 1.125 -0.359375q0.890625 0 1.8125 0.5625l-0.609375 1.640625q-0.640625 -0.390625 -1.28125 -0.390625q-0.578125 0 -1.046875 0.359375q-0.453125 0.34375 -0.65625 0.953125q-0.28125 0.9375 -0.28125 2.046875l0 5.4375l-1.765625 0zm11.560547 -4.296875l0 -1.765625l5.390625 0l0 1.765625l-5.390625 0zm12.888672 4.296875l0 -10.375l1.578125 0l0 1.453125q0.484375 -0.75 1.296875 -1.21875q0.8125 -0.46875 1.84375 -0.46875q1.15625 0 1.890625 0.484375q0.734375 0.46875 1.046875 1.328125q1.234375 -1.8125 3.203125 -1.8125q1.546875 0 2.375 0.859375q0.828125 0.859375 0.828125 2.625l0 7.125l-1.75 0l0 -6.53125q0 -1.0625 -0.171875 -1.515625q-0.171875 -0.46875 -0.625 -0.75q-0.4375 -0.28125 -1.046875 -0.28125q-1.09375 0 -1.828125 0.734375q-0.71875 0.71875 -0.71875 2.3125l0 6.03125l-1.75 0l0 -6.734375q0 -1.171875 -0.4375 -1.75q-0.421875 -0.59375 -1.40625 -0.59375q-0.734375 0 -1.375 0.390625q-0.625 0.390625 -0.90625 1.140625q-0.28125 0.75 -0.28125 2.171875l0 5.375l-1.765625 0zm16.582031 4.0l-0.1875 -1.65625q0.578125 0.15625 1.0 0.15625q0.59375 0 0.9375 -0.203125q0.359375 -0.1875 0.578125 -0.53125q0.171875 -0.265625 0.546875 -1.3125q0.046875 -0.15625 0.15625 -0.4375l-3.9375 -10.390625l1.890625 0l2.15625 6.015625q0.421875 1.140625 0.75 2.390625q0.3125 -1.203125 0.71875 -2.359375l2.21875 -6.046875l1.765625 0l-3.953125 10.546875q-0.625 1.71875 -0.984375 2.359375q-0.46875 0.875 -1.078125 1.265625q-0.59375 0.40625 -1.4375 0.40625q-0.515625 0 -1.140625 -0.203125zm9.09375 -4.0l0 -2.0l2.0 0l0 2.0l-2.0 0zm11.837891 -3.796875l1.71875 0.21875q-0.28125 1.796875 -1.453125 2.8125q-1.15625 1.0 -2.859375 1.0q-2.125 0 -3.421875 -1.390625q-1.296875 -1.390625 -1.296875 -3.984375q0 -1.6875 0.546875 -2.9375q0.5625 -1.265625 1.703125 -1.890625q1.140625 -0.640625 2.484375 -0.640625q1.6875 0 2.75 0.859375q1.078125 0.859375 1.390625 2.421875l-1.71875 0.265625q-0.234375 -1.046875 -0.859375 -1.5625q-0.625 -0.53125 -1.5 -0.53125q-1.328125 0 -2.15625 0.953125q-0.828125 0.953125 -0.828125 3.0q0 2.09375 0.796875 3.046875q0.796875 0.9375 2.09375 0.9375q1.03125 0 1.71875 -0.625q0.703125 -0.640625 0.890625 -1.953125zm2.578125 -1.390625q0 -2.875 1.59375 -4.265625q1.34375 -1.15625 3.265625 -1.15625q2.140625 0 3.484375 1.40625q1.359375 1.40625 1.359375 3.875q0 2.0 -0.59375 3.15625q-0.59375 1.140625 -1.75 1.78125q-1.140625 0.625 -2.5 0.625q-2.1875 0 -3.53125 -1.390625q-1.328125 -1.40625 -1.328125 -4.03125zm1.796875 0q0 2.0 0.859375 2.984375q0.875 0.984375 2.203125 0.984375q1.3125 0 2.171875 -0.984375q0.875 -1.0 0.875 -3.046875q0 -1.921875 -0.875 -2.90625q-0.875 -1.0 -2.171875 -1.0q-1.328125 0 -2.203125 1.0q-0.859375 0.984375 -0.859375 2.96875z" fill-rule="nonzero"/><path fill="#000000" d="m288.13907 337.09744l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm6.861328 -2.359375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.533203 0.34375l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm8.375 1.265625l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm2.1933594 -4.15625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm-1.046875 5.015625l0.25 -1.203125l1.203125 0l-0.265625 1.203125l-1.1875 0z" fill-rule="nonzero"/><path fill="#000000" d="m364.92343 337.09744l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm5.796875 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm7.3339844 -2.265625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.90625 -0.09375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125z" fill-rule="nonzero"/><path fill="#000000" d="m287.60782 353.48804l1.796875 -8.609375l0.984375 0l-0.1875 0.859375q0.546875 -0.5625 0.96875 -0.78125q0.4375 -0.21875 0.921875 -0.21875q0.890625 0 1.46875 0.65625q0.59375 0.640625 0.59375 1.84375q0 0.96875 -0.328125 1.765625q-0.3125 0.796875 -0.78125 1.296875q-0.46875 0.484375 -0.953125 0.71875q-0.484375 0.21875 -0.984375 0.21875q-1.125 0 -1.734375 -1.140625l-0.703125 3.390625l-1.0625 0zm2.09375 -4.9375q0 0.703125 0.109375 0.96875q0.140625 0.375 0.46875 0.609375q0.34375 0.234375 0.78125 0.234375q0.921875 0 1.484375 -1.015625q0.5625 -1.03125 0.5625 -2.109375q0 -0.78125 -0.390625 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.40625 0 -0.75 0.21875q-0.34375 0.21875 -0.640625 0.640625q-0.296875 0.421875 -0.5 1.046875q-0.1875 0.609375 -0.1875 1.0625zm5.298828 0.1875q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm6.4648438 -0.859375l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm2.1933594 -4.15625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm-1.046875 5.015625l0.25 -1.203125l1.203125 0l-0.265625 1.203125l-1.1875 0z" fill-rule="nonzero"/><path fill="#000000" d="m365.37656 348.6443l1.078125 -0.109375q-0.015625 0.234375 -0.015625 0.28125q0 0.375 0.1875 0.78125q0.203125 0.390625 0.53125 0.59375q0.34375 0.203125 0.71875 0.203125q0.5 0 1.0 -0.328125q0.515625 -0.34375 0.828125 -0.984375q0.3125 -0.65625 0.3125 -1.296875q0 -0.71875 -0.421875 -1.15625q-0.421875 -0.4375 -1.109375 -0.4375q-0.46875 0 -0.890625 0.234375q-0.40625 0.21875 -0.765625 0.65625l-0.921875 -0.0625l1.296875 -4.390625l4.203125 0l-0.21875 0.96875l-3.25 0l-0.65625 2.1875q0.375 -0.265625 0.75 -0.390625q0.390625 -0.140625 0.796875 -0.140625q0.984375 0 1.609375 0.65625q0.640625 0.640625 0.640625 1.78125q0 0.984375 -0.4375 1.828125q-0.4375 0.828125 -1.203125 1.28125q-0.75 0.4375 -1.625 0.4375q-0.75 0 -1.3125 -0.328125q-0.5625 -0.328125 -0.859375 -0.90625q-0.28125 -0.59375 -0.28125 -1.1875q0 -0.0625 0.015625 -0.171875zm8.533203 -2.265625q-0.46875 -0.265625 -0.71875 -0.671875q-0.234375 -0.421875 -0.234375 -0.890625q0 -0.78125 0.5625 -1.453125q0.75 -0.890625 2.03125 -0.890625q1.125 0 1.78125 0.609375q0.65625 0.609375 0.65625 1.484375q0 0.625 -0.359375 1.125q-0.34375 0.5 -1.09375 0.8125q0.515625 0.3125 0.734375 0.671875q0.28125 0.484375 0.28125 1.109375q0 1.15625 -0.8125 2.0625q-0.8125 0.90625 -2.171875 0.90625q-1.09375 0 -1.78125 -0.671875q-0.6875 -0.671875 -0.6875 -1.671875q0 -0.9375 0.484375 -1.609375q0.5 -0.6875 1.328125 -0.921875zm0.09375 -1.515625q0 0.546875 0.34375 0.890625q0.359375 0.328125 0.984375 0.328125q0.71875 0 1.171875 -0.4375q0.453125 -0.453125 0.453125 -1.09375q0 -0.546875 -0.359375 -0.890625q-0.34375 -0.34375 -0.96875 -0.34375q-0.46875 0 -0.859375 0.21875q-0.390625 0.21875 -0.578125 0.59375q-0.1875 0.375 -0.1875 0.734375zm-0.828125 4.140625q0 0.359375 0.171875 0.6875q0.171875 0.328125 0.515625 0.515625q0.359375 0.1875 0.8125 0.1875q0.859375 0 1.390625 -0.796875q0.421875 -0.640625 0.421875 -1.375q0 -0.59375 -0.390625 -0.96875q-0.390625 -0.390625 -1.015625 -0.390625q-0.796875 0 -1.359375 0.59375q-0.546875 0.59375 -0.546875 1.546875zm6.439453 2.09375q0.140625 -0.875 0.609375 -2.09375q0.546875 -1.46875 1.421875 -2.90625q0.890625 -1.453125 1.96875 -2.5l-4.28125 0l0.203125 -0.96875l5.484375 0l-0.203125 0.96875q-1.390625 1.21875 -2.4375 3.15625q-1.203125 2.234375 -1.65625 4.34375l-1.109375 0z" fill-rule="nonzero"/><path fill="#000000" d="m292.37344 363.9724q-1.109375 1.265625 -2.28125 1.265625q-0.71875 0 -1.171875 -0.40625q-0.4375 -0.421875 -0.4375 -1.015625q0 -0.390625 0.203125 -1.34375l0.75 -3.59375l1.0625 0l-0.84375 3.96875q-0.09375 0.5 -0.09375 0.78125q0 0.34375 0.203125 0.546875q0.21875 0.1875 0.640625 0.1875q0.4375 0 0.859375 -0.203125q0.4375 -0.21875 0.734375 -0.59375q0.3125 -0.375 0.5 -0.875q0.140625 -0.3125 0.296875 -1.125l0.5625 -2.6875l1.0625 0l-1.296875 6.21875l-0.984375 0l0.234375 -1.125zm2.5332031 -1.0l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm10.46875 0.015625l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.189453 3.65625l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm3.9960938 0l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0zm10.861328 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm2.0800781 3.15625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.574219 -2.109375l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm6.517578 -1.359375l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm-1.046875 5.015625l0.25 -1.203125l1.203125 0l-0.265625 1.203125l-1.1875 0z" fill-rule="nonzero"/><path fill="#000000" d="m364.92343 365.0974l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm10.640625 -3.53125q-1.109375 1.265625 -2.28125 1.265625q-0.71875 0 -1.171875 -0.40625q-0.4375 -0.421875 -0.4375 -1.015625q0 -0.390625 0.203125 -1.34375l0.75 -3.59375l1.0625 0l-0.84375 3.96875q-0.09375 0.5 -0.09375 0.78125q0 0.34375 0.203125 0.546875q0.21875 0.1875 0.640625 0.1875q0.4375 0 0.859375 -0.203125q0.4375 -0.21875 0.734375 -0.59375q0.3125 -0.375 0.5 -0.875q0.140625 -0.3125 0.296875 -1.125l0.5625 -2.6875l1.0625 0l-1.296875 6.21875l-0.984375 0l0.234375 -1.125zm2.5332031 -1.0l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm10.46875 0.015625l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.189453 3.65625l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0z" fill-rule="nonzero"/><path fill="#000000" d="m287.60782 381.48804l1.796875 -8.609375l0.984375 0l-0.1875 0.859375q0.546875 -0.5625 0.96875 -0.78125q0.4375 -0.21875 0.921875 -0.21875q0.890625 0 1.46875 0.65625q0.59375 0.640625 0.59375 1.84375q0 0.96875 -0.328125 1.765625q-0.3125 0.796875 -0.78125 1.296875q-0.46875 0.484375 -0.953125 0.71875q-0.484375 0.21875 -0.984375 0.21875q-1.125 0 -1.734375 -1.140625l-0.703125 3.390625l-1.0625 0zm2.09375 -4.9375q0 0.703125 0.109375 0.96875q0.140625 0.375 0.46875 0.609375q0.34375 0.234375 0.78125 0.234375q0.921875 0 1.484375 -1.015625q0.5625 -1.03125 0.5625 -2.109375q0 -0.78125 -0.390625 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.40625 0 -0.75 0.21875q-0.34375 0.21875 -0.640625 0.640625q-0.296875 0.421875 -0.5 1.046875q-0.1875 0.609375 -0.1875 1.0625zm9.298828 1.765625q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm2.1894531 1.03125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm6.0 0l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm7.078125 2.125l-0.640625 -6.21875l1.015625 0l0.28125 2.796875l0.109375 1.5625q0.015625 0.171875 0.03125 0.703125q0.1875 -0.59375 0.296875 -0.90625q0.125 -0.328125 0.40625 -0.921875l1.46875 -3.234375l1.15625 0l0.203125 3.109375q0.046875 0.703125 0.0625 1.890625q0.296875 -0.78125 0.953125 -2.1875l1.34375 -2.8125l1.0625 0l-3.046875 6.21875l-1.09375 0l-0.234375 -3.65625q-0.03125 -0.328125 -0.046875 -1.3125q-0.234375 0.625 -0.5625 1.34375l-1.640625 3.625l-1.125 0zm7.6816406 -2.359375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm8.183594 -0.90625q-0.90625 1.046875 -1.890625 1.046875q-0.890625 0 -1.484375 -0.65625q-0.578125 -0.65625 -0.578125 -1.890625q0 -1.140625 0.453125 -2.078125q0.46875 -0.9375 1.171875 -1.40625q0.703125 -0.46875 1.40625 -0.46875q1.15625 0 1.75 1.109375l0.703125 -3.34375l1.046875 0l-1.78125 8.59375l-0.984375 0l0.1875 -0.90625zm-2.90625 -1.703125q0 0.65625 0.125 1.03125q0.140625 0.375 0.4375 0.625q0.3125 0.25 0.75 0.25q0.71875 0 1.3125 -0.75q0.78125 -1.0 0.78125 -2.46875q0 -0.734375 -0.390625 -1.140625q-0.390625 -0.421875 -0.96875 -0.421875q-0.375 0 -0.703125 0.171875q-0.3125 0.15625 -0.625 0.5625q-0.296875 0.40625 -0.515625 1.046875q-0.203125 0.625 -0.203125 1.09375zm6.720703 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm-1.046875 5.015625l0.25 -1.203125l1.203125 0l-0.265625 1.203125l-1.1875 0z" fill-rule="nonzero"/><path fill="#000000" d="m369.2203 376.8318l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.71875 2.265625l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm10.861328 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm2.0957031 3.15625l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0zm6.611328 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm9.970703 0.640625l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.173828 3.65625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.574219 -2.109375l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625z" fill-rule="nonzero"/><path fill="#ffffff" d="m200.14961 28.889763l164.37794 0l0 217.51181l-164.37794 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m200.14961 28.889763l164.37794 0l0 217.51181l-164.37794 0z" fill-rule="evenodd"/><path fill="#000000" d="m209.11836 55.80976l5.125 -13.359375l1.90625 0l5.46875 13.359375l-2.015625 0l-1.546875 -4.046875l-5.59375 0l-1.46875 4.046875l-1.875 0zm3.859375 -5.484375l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm9.802948 5.484375l0 -13.359375l1.640625 0l0 13.359375l-1.640625 0zm10.816696 -3.109375l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.296875q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm9.125717 5.765625l0 -9.671875l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.0625l-1.625 0zm9.806427 -1.46875l0.234375 1.453125q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm14.944717 -0.109375l0 1.578125l-8.82811 0q-0.015625 -0.59375 0.1875 -1.140625q0.34375 -0.90625 1.078125 -1.78125q0.75 -0.875 2.1562347 -2.015625q2.171875 -1.78125 2.9375 -2.828125q0.765625 -1.046875 0.765625 -1.96875q0 -0.984375 -0.703125 -1.640625q-0.6875 -0.671875 -1.8125 -0.671875q-1.1875 0 -1.9062347 0.71875q-0.703125 0.703125 -0.703125 1.953125l-1.6875 -0.171875q0.171875 -1.890625 1.296875 -2.875q1.1406097 -0.984375 3.0312347 -0.984375q1.921875 0 3.046875 1.0625q1.125 1.0625 1.125 2.640625q0 0.796875 -0.328125 1.578125q-0.328125 0.78125 -1.09375 1.640625q-0.75 0.84375 -2.53125 2.34375q-1.46875 1.234375 -1.890625 1.6875q-0.42185974 0.4375 -0.68748474 0.875l6.5468597 0z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m204.59407 73.062996l152.0945 0l0 25.51181l-152.0945 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m204.59407 73.062996l152.0945 0l0 25.51181l-152.0945 0z" fill-rule="evenodd"/><path fill="#000000" d="m222.20345 87.471115l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.4592743 -0.125q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307358 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm13.072983 0l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm7.291733 -5.21875l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm6.0686493 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594604 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717896 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.3073425 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m204.59407 108.86232l152.0945 0l0 29.952759l-152.0945 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m204.59407 108.86232l152.0945 0l0 29.952759l-152.0945 0z" fill-rule="evenodd"/><path fill="#000000" d="m214.25032 123.56732l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm15.041229 0.59375l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm2.375 2.78125l0 -10.484375l1.28125 0l0 3.75q0.90625 -1.03125 2.28125 -1.03125q0.84375 0 1.46875 0.328125q0.625 0.328125 0.890625 0.921875q0.265625 0.578125 0.265625 1.703125l0 4.8125l-1.28125 0l0 -4.8125q0 -0.96875 -0.421875 -1.40625q-0.421875 -0.4375 -1.1875 -0.4375q-0.578125 0 -1.078125 0.296875q-0.5 0.296875 -0.71875 0.8125q-0.21875 0.5 -0.21875 1.390625l0 4.15625l-1.28125 0zm13.354233 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm12.104233 4.53125l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm12.276108 3.796875l0 -1.109375q-0.890625 1.28125 -2.421875 1.28125q-0.671875 0 -1.25 -0.25q-0.578125 -0.265625 -0.875 -0.65625q-0.28125 -0.390625 -0.390625 -0.953125q-0.078125 -0.375 -0.078125 -1.203125l0 -4.703125l1.28125 0l0 4.203125q0 1.015625 0.078125 1.359375q0.125 0.515625 0.515625 0.8125q0.40625 0.28125 0.984375 0.28125q0.578125 0 1.078125 -0.296875q0.515625 -0.296875 0.71875 -0.8125q0.21875 -0.515625 0.21875 -1.484375l0 -4.0625l1.28125 0l0 7.59375l-1.140625 0zm3.1354675 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.49054 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m204.59407 146.05766l152.0945 0l0 93.41733l-152.0945 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m204.59407 146.05766l152.0945 0l0 93.41733l-152.0945 0z" fill-rule="evenodd"/><path fill="#000000" d="m213.57845 164.13766l4.015625 -10.484375l1.5 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm12.697479 1.53125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm5.1875 1.625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594757 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717743 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307358 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm7.635483 -2.265625l1.2656097 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.54685974 -0.265625 -0.82810974 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.29685974 -0.21875 0.81248474 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.2656097 -0.609375q-0.765625 -0.625 -0.984375 -1.828125z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m217.09448 172.23885l125.79529 0l0 56.850388l-125.79529 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m217.09448 172.23885l125.79529 0l0 56.850388l-125.79529 0z" fill-rule="evenodd"/><path fill="#000000" d="m229.20386 191.60684l0 -7.578125l-2.828125 0l0 -1.015625l6.8125 0l0 1.015625l-2.84375 0l0 7.578125l-1.140625 0zm4.3085938 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm6.046875 0l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm6.158203 -1.3125l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m300.42065 189.49747l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.173828 3.65625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.199219 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm3.5957031 -4.234375l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6191406 0l1.796875 -8.59375l1.0625 0l-1.796875 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m233.15698 205.59122l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923828 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm0.6464844 -2.171875q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.970703 3.109375l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm4.2929688 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m295.18628 208.60684l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm5.796875 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm7.3339844 -2.265625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.90625 -0.09375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125z" fill-rule="nonzero"/><path fill="#000000" d="m227.18823 221.60684l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm3.3339844 0l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm3.3339844 0l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#ffffff" d="m387.17847 28.889763l164.37799 0l0 217.51181l-164.37799 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m387.17847 28.889763l164.37799 0l0 217.51181l-164.37799 0z" fill-rule="evenodd"/><path fill="#000000" d="m396.14722 55.80976l5.125 -13.359375l1.90625 0l5.46875 13.359375l-2.015625 0l-1.546875 -4.046875l-5.59375 0l-1.46875 4.046875l-1.875 0zm3.859375 -5.484375l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm9.802948 5.484375l0 -13.359375l1.640625 0l0 13.359375l-1.640625 0zm10.816711 -3.109375l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.296875q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm9.125702 5.765625l0 -9.671875l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.0625l-1.625 0zm9.806427 -1.46875l0.234375 1.453125q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm6.3353577 -2.0625l1.640625 -0.21875q0.28125 1.40625 0.953125 2.015625q0.6875 0.609375 1.65625 0.609375q1.15625 0 1.953125 -0.796875q0.796875 -0.796875 0.796875 -1.984375q0 -1.125 -0.734375 -1.859375q-0.734375 -0.734375 -1.875 -0.734375q-0.46875 0 -1.15625 0.171875l0.1875 -1.4375q0.15625 0.015625 0.265625 0.015625q1.046875 0 1.875 -0.546875q0.84375 -0.546875 0.84375 -1.671875q0 -0.90625 -0.609375 -1.5q-0.609375 -0.59375 -1.578125 -0.59375q-0.953125 0 -1.59375 0.609375q-0.640625 0.59375 -0.8125 1.796875l-1.640625 -0.296875q0.296875 -1.640625 1.359375 -2.546875q1.0625 -0.90625 2.65625 -0.90625q1.09375 0 2.0 0.46875q0.921875 0.46875 1.40625 1.28125q0.5 0.8125 0.5 1.71875q0 0.859375 -0.46875 1.578125q-0.46875 0.703125 -1.375 1.125q1.1875 0.28125 1.84375 1.140625q0.65625 0.859375 0.65625 2.15625q0 1.734375 -1.28125 2.953125q-1.265625 1.21875 -3.21875 1.21875q-1.765625 0 -2.921875 -1.046875q-1.15625 -1.046875 -1.328125 -2.71875z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m391.62292 73.062996l152.09448 0l0 25.51181l-152.09448 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m391.62292 73.062996l152.09448 0l0 25.51181l-152.09448 0z" fill-rule="evenodd"/><path fill="#000000" d="m409.2323 87.471115l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.4592896 -0.125q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.3073425 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm13.072998 0l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm7.2917175 -5.21875l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm6.0686646 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594604 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717896 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.3073425 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m391.62292 108.86232l152.09448 0l0 29.952759l-152.09448 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m391.62292 108.86232l152.09448 0l0 29.952759l-152.09448 0z" fill-rule="evenodd"/><path fill="#000000" d="m401.27917 123.56732l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm15.041229 0.59375l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm2.375 2.78125l0 -10.484375l1.28125 0l0 3.75q0.90625 -1.03125 2.28125 -1.03125q0.84375 0 1.46875 0.328125q0.625 0.328125 0.890625 0.921875q0.265625 0.578125 0.265625 1.703125l0 4.8125l-1.28125 0l0 -4.8125q0 -0.96875 -0.421875 -1.40625q-0.421875 -0.4375 -1.1875 -0.4375q-0.578125 0 -1.078125 0.296875q-0.5 0.296875 -0.71875 0.8125q-0.21875 0.5 -0.21875 1.390625l0 4.15625l-1.28125 0zm13.354248 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm12.104218 4.53125l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm12.276123 3.796875l0 -1.109375q-0.890625 1.28125 -2.421875 1.28125q-0.671875 0 -1.25 -0.25q-0.578125 -0.265625 -0.875 -0.65625q-0.28125 -0.390625 -0.390625 -0.953125q-0.078125 -0.375 -0.078125 -1.203125l0 -4.703125l1.28125 0l0 4.203125q0 1.015625 0.078125 1.359375q0.125 0.515625 0.515625 0.8125q0.40625 0.28125 0.984375 0.28125q0.578125 0 1.078125 -0.296875q0.515625 -0.296875 0.71875 -0.8125q0.21875 -0.515625 0.21875 -1.484375l0 -4.0625l1.28125 0l0 7.59375l-1.140625 0zm3.1354675 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.49054 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m391.62292 146.05766l152.09448 0l0 93.41733l-152.09448 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m391.62292 146.05766l152.09448 0l0 93.41733l-152.09448 0z" fill-rule="evenodd"/><path fill="#000000" d="m400.6073 164.13766l4.015625 -10.484375l1.5 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm12.697479 1.53125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm5.1875 1.625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.259491 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.771759 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307373 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm7.6354675 -2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m404.12335 172.23885l125.79529 0l0 56.850388l-125.79529 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m404.12335 172.23885l125.79529 0l0 56.850388l-125.79529 0z" fill-rule="evenodd"/><path fill="#000000" d="m416.23273 191.60684l0 -7.578125l-2.828125 0l0 -1.015625l6.8125 0l0 1.015625l-2.84375 0l0 7.578125l-1.140625 0zm4.3085938 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm6.046875 0l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm6.158203 -1.3125l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m487.44952 189.49747l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.173828 3.65625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.199219 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm3.5957031 -4.234375l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6191406 0l1.796875 -8.59375l1.0625 0l-1.796875 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m420.18585 205.59122l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923828 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm0.6464844 -2.171875q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.970703 3.109375l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm4.2929688 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m482.21515 208.60684l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm5.796875 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm7.3339844 -2.265625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.90625 -0.09375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125z" fill-rule="nonzero"/><path fill="#000000" d="m414.2171 221.60684l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm3.3339844 0l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm3.3339844 0l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#ffffff" d="m578.65094 28.889763l164.37793 0l0 217.51181l-164.37793 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m578.65094 28.889763l164.37793 0l0 217.51181l-164.37793 0z" fill-rule="evenodd"/><path fill="#000000" d="m587.6197 55.80976l5.125 -13.359375l1.90625 0l5.46875 13.359375l-2.015625 0l-1.546875 -4.046875l-5.59375 0l-1.46875 4.046875l-1.875 0zm3.859375 -5.484375l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm9.8029175 5.484375l0 -13.359375l1.640625 0l0 13.359375l-1.640625 0zm10.816711 -3.109375l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.296875q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm9.125732 5.765625l0 -9.671875l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.0625l-1.625 0zm9.8063965 -1.46875l0.234375 1.453125q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm11.585388 1.46875l0 -3.203125l-5.796875 0l0 -1.5l6.09375 -8.65625l1.34375 0l0 8.65625l1.796875 0l0 1.5l-1.796875 0l0 3.203125l-1.640625 0zm0 -4.703125l0 -6.015625l-4.1875 6.015625l4.1875 0z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m583.0954 73.062996l152.09448 0l0 25.51181l-152.09448 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m583.0954 73.062996l152.09448 0l0 25.51181l-152.09448 0z" fill-rule="evenodd"/><path fill="#000000" d="m600.7048 87.471115l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.4592285 -0.125q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307373 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm13.072998 0l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm7.291748 -5.21875l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm6.0686035 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2595215 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717285 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307373 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m583.0954 108.86232l152.09448 0l0 29.952759l-152.09448 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m583.0954 108.86232l152.09448 0l0 29.952759l-152.09448 0z" fill-rule="evenodd"/><path fill="#000000" d="m592.75165 123.56732l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm15.041199 0.59375l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm2.375 2.78125l0 -10.484375l1.28125 0l0 3.75q0.90625 -1.03125 2.28125 -1.03125q0.84375 0 1.46875 0.328125q0.625 0.328125 0.890625 0.921875q0.265625 0.578125 0.265625 1.703125l0 4.8125l-1.28125 0l0 -4.8125q0 -0.96875 -0.421875 -1.40625q-0.421875 -0.4375 -1.1875 -0.4375q-0.578125 0 -1.078125 0.296875q-0.5 0.296875 -0.71875 0.8125q-0.21875 0.5 -0.21875 1.390625l0 4.15625l-1.28125 0zm13.354248 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm12.104248 4.53125l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm12.276062 3.796875l0 -1.109375q-0.890625 1.28125 -2.421875 1.28125q-0.671875 0 -1.25 -0.25q-0.578125 -0.265625 -0.875 -0.65625q-0.28125 -0.390625 -0.390625 -0.953125q-0.078125 -0.375 -0.078125 -1.203125l0 -4.703125l1.28125 0l0 4.203125q0 1.015625 0.078125 1.359375q0.125 0.515625 0.515625 0.8125q0.40625 0.28125 0.984375 0.28125q0.578125 0 1.078125 -0.296875q0.515625 -0.296875 0.71875 -0.8125q0.21875 -0.515625 0.21875 -1.484375l0 -4.0625l1.28125 0l0 7.59375l-1.140625 0zm3.135498 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.49054 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m583.0954 146.05766l152.09448 0l0 93.41733l-152.09448 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m583.0954 146.05766l152.09448 0l0 93.41733l-152.09448 0z" fill-rule="evenodd"/><path fill="#000000" d="m592.0798 164.13766l4.015625 -10.484375l1.5 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm12.697449 1.53125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm5.1875 1.625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594604 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717896 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307373 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm7.635498 -2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m595.5958 172.23885l125.79523 0l0 56.850388l-125.79523 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m595.5958 172.23885l125.79523 0l0 56.850388l-125.79523 0z" fill-rule="evenodd"/><path fill="#000000" d="m607.7052 191.60684l0 -7.578125l-2.828125 0l0 -1.015625l6.8125 0l0 1.015625l-2.84375 0l0 7.578125l-1.140625 0zm4.3085938 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm6.046875 0l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm6.158203 -1.3125l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m678.922 189.49747l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.173828 3.65625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.199219 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm3.5957031 -4.234375l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6191406 0l1.796875 -8.59375l1.0625 0l-1.796875 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m611.6583 205.59122l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923828 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm0.6464844 -2.171875q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.970703 3.109375l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm4.2929688 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m673.6876 208.60684l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm5.796875 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm7.3339844 -2.265625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.90625 -0.09375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125z" fill-rule="nonzero"/><path fill="#000000" d="m605.6896 221.60684l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm3.3339844 0l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm3.3339844 0l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m124.698166 215.20735l257.3543 63.37007" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m124.698166 215.20735l257.3543 63.37007" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m307.37534 216.08923l74.677155 62.48819" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m307.37534 216.08923l74.677155 62.48819" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m495.28085 215.20735l-113.22836 63.37007" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m495.28085 215.20735l-113.22836 63.37007" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m685.76904 215.20735l-303.71655 63.37007" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m685.76904 215.20735l-303.71655 63.37007" fill-rule="evenodd"/></g></svg> \ No newline at end of file diff --git a/docs/user/alerting/images/alert-concepts-summary.svg b/docs/user/alerting/images/alert-concepts-summary.svg new file mode 100644 index 0000000000000..d11023b706418 --- /dev/null +++ b/docs/user/alerting/images/alert-concepts-summary.svg @@ -0,0 +1 @@ +<svg version="1.1" viewBox="0.0 0.0 995.4120734908137 468.84776902887137" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l995.41205 0l0 468.84778l-995.41205 0l0 -468.84778z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l995.41205 0l0 468.84778l-995.41205 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m13.120735 52.889763l260.8819 0l0 266.61417l-260.8819 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m13.120735 52.889763l260.8819 0l0 266.61417l-260.8819 0z" fill-rule="evenodd"/><path fill="#000000" d="m22.089485 79.80976l5.125 -13.359375l1.90625 0l5.46875 13.359375l-2.015625 0l-1.546875 -4.046875l-5.59375 0l-1.46875 4.046875l-1.875 0zm3.859375 -5.484375l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm9.802948 5.484375l0 -13.359375l1.640625 0l0 13.359375l-1.640625 0zm10.816696 -3.109375l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.296875q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm9.125717 5.765625l0 -9.671875l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.0625l-1.625 0zm9.806427 -1.46875l0.234375 1.453125q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 97.062996l233.00789 0l0 33.511803l-233.00789 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 97.062996l233.00789 0l0 33.511803l-233.00789 0z" fill-rule="evenodd"/><path fill="#000000" d="m43.750084 117.40737l1.515625 0.375q-0.46875 1.875 -1.71875 2.859375q-1.234375 0.984375 -3.015625 0.984375q-1.859375 0 -3.015625 -0.75q-1.15625 -0.765625 -1.765625 -2.1875q-0.609375 -1.4375 -0.609375 -3.078125q0 -1.796875 0.6875 -3.125q0.6875 -1.328125 1.9375 -2.015625q1.265625 -0.703125 2.78125 -0.703125q1.71875 0 2.890625 0.875q1.171875 0.875 1.640625 2.46875l-1.5 0.34375q-0.390625 -1.25 -1.15625 -1.8125q-0.75 -0.578125 -1.90625 -0.578125q-1.3125 0 -2.203125 0.640625q-0.890625 0.625 -1.25 1.703125q-0.359375 1.0625 -0.359375 2.1875q0 1.46875 0.421875 2.5625q0.4375 1.078125 1.328125 1.625q0.90625 0.53125 1.953125 0.53125q1.265625 0 2.140625 -0.734375q0.890625 -0.734375 1.203125 -2.171875zm2.6796875 -0.140625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0zm14.2734375 0l0 -1.046875q-0.78125 1.234375 -2.3125 1.234375q-1.0 0 -1.828125 -0.546875q-0.828125 -0.546875 -1.296875 -1.53125q-0.453125 -0.984375 -0.453125 -2.25q0 -1.25 0.40625 -2.25q0.421875 -1.015625 1.25 -1.546875q0.828125 -0.546875 1.859375 -0.546875q0.75 0 1.328125 0.3125q0.59375 0.3125 0.953125 0.828125l0 -4.109375l1.40625 0l0 11.453125l-1.3125 0zm-4.4375 -4.140625q0 1.59375 0.671875 2.390625q0.671875 0.78125 1.578125 0.78125q0.921875 0 1.5625 -0.75q0.65625 -0.765625 0.65625 -2.3125q0 -1.703125 -0.65625 -2.5q-0.65625 -0.796875 -1.625 -0.796875q-0.9375 0 -1.5625 0.765625q-0.625 0.765625 -0.625 2.421875zm7.9609375 -5.703125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm6.6171875 -1.265625l0.203125 1.25q-0.59375 0.125 -1.0625 0.125q-0.765625 0 -1.1875 -0.234375q-0.421875 -0.25 -0.59375 -0.640625q-0.171875 -0.40625 -0.171875 -1.671875l0 -4.765625l-1.03125 0l0 -1.09375l1.03125 0l0 -2.0625l1.40625 -0.84375l0 2.90625l1.40625 0l0 1.09375l-1.40625 0l0 4.84375q0 0.609375 0.0625 0.78125q0.078125 0.171875 0.25 0.28125q0.171875 0.09375 0.484375 0.09375q0.234375 0 0.609375 -0.0625zm1.3828125 -8.578125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm3.0234375 -4.15625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0z" fill-rule="nonzero"/><path fill="#000000" d="m136.46883 120.39175q-0.734375 0.625 -1.421875 0.921875q-0.671875 0.296875 -1.4375 0.296875q-1.15625 0 -1.859375 -0.671875q-0.703125 -0.6875 -0.703125 -1.734375q0 -0.703125 0.3125 -1.234375q0.328125 -0.53125 0.84375 -0.859375q0.53125 -0.328125 1.28125 -0.46875q0.484375 -0.09375 1.8125 -0.140625q1.328125 -0.0625 1.90625 -0.28125q0.171875 -0.578125 0.171875 -0.96875q0 -0.484375 -0.359375 -0.765625q-0.5 -0.390625 -1.4375 -0.390625q-0.890625 0 -1.46875 0.390625q-0.5625 0.390625 -0.8125 1.125l-1.4375 -0.125q0.4375 -1.234375 1.390625 -1.890625q0.953125 -0.65625 2.390625 -0.65625q1.546875 0 2.4375 0.734375q0.6875 0.546875 0.6875 1.421875q0 0.65625 -0.1875 1.53125l-0.46875 2.0625q-0.21875 0.984375 -0.21875 1.609375q0 0.390625 0.171875 1.125l-1.421875 0q-0.125 -0.40625 -0.171875 -1.03125zm0.53125 -3.171875q-0.296875 0.109375 -0.640625 0.171875q-0.34375 0.0625 -1.140625 0.140625q-1.234375 0.109375 -1.75 0.28125q-0.5 0.15625 -0.765625 0.53125q-0.25 0.359375 -0.25 0.8125q0 0.59375 0.40625 0.984375q0.421875 0.375 1.171875 0.375q0.703125 0 1.34375 -0.375q0.65625 -0.375 1.03125 -1.03125q0.375 -0.671875 0.59375 -1.890625zm4.8671875 4.203125l-1.359375 -8.296875l1.375 0l0.71875 4.5625q0.109375 0.75 0.28125 2.453125q0.40625 -0.890625 1.046875 -2.03125l2.765625 -4.984375l1.5 0l-4.75 8.296875l-1.578125 0zm5.828125 0.75l1.4375 0.140625q-0.015625 0.484375 0.109375 0.71875q0.140625 0.25 0.421875 0.375q0.390625 0.171875 1.0 0.171875q1.28125 0 1.84375 -0.65625q0.359375 -0.453125 0.671875 -1.953125l0.140625 -0.671875q-1.09375 1.125 -2.34375 1.125q-1.25 0 -2.109375 -0.921875q-0.84375 -0.9375 -0.84375 -2.625q0 -1.40625 0.671875 -2.578125q0.671875 -1.171875 1.59375 -1.765625q0.921875 -0.59375 1.890625 -0.59375q1.640625 0 2.515625 1.546875l0.28125 -1.359375l1.296875 0l-1.671875 8.0q-0.265625 1.328125 -0.71875 2.0625q-0.4375 0.75 -1.234375 1.15625q-0.796875 0.40625 -1.828125 0.40625q-1.0 0 -1.734375 -0.265625q-0.71875 -0.25 -1.078125 -0.75q-0.34375 -0.5 -0.34375 -1.140625q0 -0.1875 0.03125 -0.421875zm1.765625 -4.421875q0 0.84375 0.171875 1.28125q0.234375 0.59375 0.65625 0.921875q0.4375 0.3125 0.96875 0.3125q0.703125 0 1.390625 -0.484375q0.6875 -0.5 1.109375 -1.515625q0.421875 -1.03125 0.421875 -1.953125q0 -1.03125 -0.5625 -1.625q-0.5625 -0.609375 -1.40625 -0.609375q-0.515625 0 -1.0 0.28125q-0.484375 0.265625 -0.90625 0.84375q-0.40625 0.5625 -0.625 1.359375q-0.21875 0.78125 -0.21875 1.1875zm20.78125 -0.359375l1.515625 0.203125q-0.71875 1.984375 -2.09375 3.015625q-1.375 1.015625 -3.09375 1.015625q-2.078125 0 -3.3125 -1.28125q-1.21875 -1.28125 -1.21875 -3.65625q0 -3.09375 1.859375 -5.109375q1.65625 -1.8125 4.125 -1.8125q1.8125 0 2.9375 0.984375q1.140625 0.96875 1.3125 2.625l-1.421875 0.125q-0.234375 -1.234375 -0.9375 -1.84375q-0.703125 -0.625 -1.828125 -0.625q-2.09375 0 -3.390625 1.875q-1.125 1.609375 -1.125 3.8125q0 1.765625 0.859375 2.703125q0.875 0.921875 2.265625 0.921875q1.1875 0 2.140625 -0.765625q0.96875 -0.78125 1.40625 -2.1875zm2.5859375 4.03125l2.40625 -11.453125l4.78125 0q1.234375 0 1.859375 0.28125q0.625 0.28125 1.03125 0.984375q0.40625 0.6875 0.40625 1.546875q0 0.703125 -0.296875 1.4375q-0.28125 0.734375 -0.734375 1.21875q-0.4375 0.46875 -0.890625 0.71875q-0.453125 0.234375 -0.96875 0.359375q-1.09375 0.25 -2.21875 0.25l-2.875 0l-0.96875 4.65625l-1.53125 0zm2.78125 -5.953125l2.515625 0q1.46875 0 2.15625 -0.3125q0.6875 -0.3125 1.09375 -0.953125q0.421875 -0.65625 0.421875 -1.390625q0 -0.5625 -0.21875 -0.90625q-0.21875 -0.359375 -0.625 -0.53125q-0.390625 -0.171875 -1.515625 -0.171875l-2.9375 0l-0.890625 4.265625zm10.375 -5.5l1.53125 0l-1.46875 7.03125q-0.171875 0.875 -0.171875 1.296875q0 0.921875 0.71875 1.484375q0.734375 0.5625 1.828125 0.5625q0.875 0 1.625 -0.390625q0.765625 -0.40625 1.203125 -1.1875q0.4375 -0.78125 0.78125 -2.5l1.328125 -6.296875l1.53125 0l-1.40625 6.71875q-0.359375 1.71875 -0.953125 2.71875q-0.578125 1.0 -1.625 1.609375q-1.03125 0.609375 -2.40625 0.609375q-1.296875 0 -2.25 -0.421875q-0.9375 -0.4375 -1.421875 -1.1875q-0.46875 -0.765625 -0.46875 -1.734375q0 -0.59375 0.328125 -2.0625l1.296875 -6.25zm21.859375 6.4375l-7.59375 3.25l0 -1.40625l6.015625 -2.484375l-6.015625 -2.46875l0 -1.40625l7.59375 3.203125l0 1.3125zm5.8984375 1.25q0 -1.453125 0.421875 -2.953125q0.4375 -1.5 0.96875 -2.390625q0.53125 -0.90625 1.09375 -1.40625q0.578125 -0.515625 1.125 -0.75q0.5625 -0.234375 1.234375 -0.234375q1.3125 0 2.1875 0.984375q0.875 0.96875 0.875 2.8125q0 1.890625 -0.609375 3.75q-0.71875 2.203125 -2.0 3.3125q-0.984375 0.84375 -2.234375 0.84375q-1.296875 0 -2.1875 -1.015625q-0.875 -1.015625 -0.875 -2.953125zm1.359375 0.359375q0 1.0625 0.359375 1.65625q0.46875 0.8125 1.40625 0.8125q0.828125 0 1.5 -0.734375q0.96875 -1.046875 1.453125 -3.0625q0.484375 -2.03125 0.484375 -3.3125q0 -1.234375 -0.46875 -1.78125q-0.46875 -0.546875 -1.25 -0.546875q-0.5625 0 -1.046875 0.296875q-0.484375 0.28125 -0.9375 0.984375q-0.625 1.0 -1.09375 2.96875q-0.40625 1.6875 -0.40625 2.71875zm7.3203125 3.40625l0.34375 -1.609375l1.59375 0l-0.34375 1.609375l-1.59375 0zm4.6015625 -2.625l1.328125 -0.140625q0.140625 0.96875 0.546875 1.375q0.421875 0.40625 0.984375 0.40625q0.8125 0 1.515625 -0.703125q1.015625 -1.03125 1.5 -2.96875q-0.703125 0.53125 -1.265625 0.765625q-0.546875 0.21875 -1.125 0.21875q-1.046875 0 -1.875 -0.703125q-1.09375 -0.90625 -1.09375 -2.625q0 -1.9375 1.265625 -3.3125q1.09375 -1.1875 2.671875 -1.1875q1.421875 0 2.359375 1.046875q0.9375 1.046875 0.9375 2.96875q0 1.875 -0.625 3.6875q-0.75 2.140625 -2.015625 3.15625q-1.03125 0.84375 -2.3125 0.84375q-1.1875 0 -1.953125 -0.734375q-0.75 -0.75 -0.84375 -2.09375zm1.890625 -4.53125q0 1.078125 0.5625 1.71875q0.578125 0.640625 1.375 0.640625q0.578125 0 1.15625 -0.390625q0.578125 -0.40625 0.984375 -1.203125q0.421875 -0.796875 0.421875 -1.625q0 -0.65625 -0.28125 -1.21875q-0.265625 -0.5625 -0.734375 -0.84375q-0.46875 -0.296875 -0.953125 -0.296875q-0.46875 0 -0.921875 0.25q-0.453125 0.25 -0.84375 0.734375q-0.390625 0.484375 -0.578125 1.125q-0.1875 0.625 -0.1875 1.109375z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 144.07611l233.00789 0l0 39.3071l-233.00789 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 144.07611l233.00789 0l0 39.3071l-233.00789 0z" fill-rule="evenodd"/><path fill="#000000" d="m35.062584 164.74861l1.4375 -0.125q0.09375 0.859375 0.46875 1.421875q0.375 0.546875 1.15625 0.890625q0.78125 0.328125 1.75 0.328125q0.875 0 1.53125 -0.25q0.671875 -0.265625 0.984375 -0.703125q0.328125 -0.453125 0.328125 -0.984375q0 -0.546875 -0.3125 -0.9375q-0.3125 -0.40625 -1.03125 -0.6875q-0.453125 -0.171875 -2.03125 -0.546875q-1.578125 -0.390625 -2.21875 -0.71875q-0.8125 -0.4375 -1.21875 -1.0625q-0.40625 -0.640625 -0.40625 -1.4375q0 -0.859375 0.484375 -1.609375q0.5 -0.765625 1.4375 -1.15625q0.953125 -0.390625 2.109375 -0.390625q1.28125 0 2.25 0.421875q0.96875 0.40625 1.484375 1.203125q0.53125 0.796875 0.578125 1.796875l-1.453125 0.109375q-0.125 -1.078125 -0.796875 -1.625q-0.671875 -0.5625 -2.0 -0.5625q-1.375 0 -2.0 0.5q-0.625 0.5 -0.625 1.21875q0 0.609375 0.4375 1.015625q0.4375 0.390625 2.28125 0.8125q1.859375 0.421875 2.546875 0.734375q1.0 0.453125 1.46875 1.171875q0.484375 0.703125 0.484375 1.625q0 0.90625 -0.53125 1.71875q-0.515625 0.8125 -1.5 1.265625q-0.984375 0.453125 -2.203125 0.453125q-1.5625 0 -2.609375 -0.453125q-1.046875 -0.46875 -1.65625 -1.375q-0.59375 -0.90625 -0.625 -2.0625zm16.421875 0.640625l1.390625 0.1875q-0.234375 1.421875 -1.171875 2.234375q-0.921875 0.8125 -2.28125 0.8125q-1.703125 0 -2.75 -1.109375q-1.03125 -1.125 -1.03125 -3.203125q0 -1.34375 0.4375 -2.34375q0.453125 -1.015625 1.359375 -1.515625q0.921875 -0.5 1.984375 -0.5q1.359375 0 2.21875 0.6875q0.859375 0.671875 1.09375 1.9375l-1.359375 0.203125q-0.203125 -0.828125 -0.703125 -1.25q-0.484375 -0.421875 -1.1875 -0.421875q-1.0625 0 -1.734375 0.765625q-0.65625 0.75 -0.65625 2.40625q0 1.671875 0.640625 2.4375q0.640625 0.75 1.671875 0.75q0.828125 0 1.375 -0.5q0.5625 -0.515625 0.703125 -1.578125zm2.59375 3.046875l0 -11.453125l1.40625 0l0 4.109375q0.984375 -1.140625 2.484375 -1.140625q0.921875 0 1.59375 0.359375q0.6875 0.359375 0.96875 1.0q0.296875 0.640625 0.296875 1.859375l0 5.265625l-1.40625 0l0 -5.265625q0 -1.046875 -0.453125 -1.53125q-0.453125 -0.484375 -1.296875 -0.484375q-0.625 0 -1.171875 0.328125q-0.546875 0.328125 -0.78125 0.890625q-0.234375 0.546875 -0.234375 1.515625l0 4.546875l-1.40625 0zm14.5703125 -2.671875l1.453125 0.171875q-0.34375 1.28125 -1.28125 1.984375q-0.921875 0.703125 -2.359375 0.703125q-1.828125 0 -2.890625 -1.125q-1.0625 -1.125 -1.0625 -3.140625q0 -2.09375 1.078125 -3.25q1.078125 -1.15625 2.796875 -1.15625q1.65625 0 2.703125 1.140625q1.0625 1.125 1.0625 3.171875q0 0.125 0 0.375l-6.1875 0q0.078125 1.375 0.765625 2.109375q0.703125 0.71875 1.734375 0.71875q0.78125 0 1.328125 -0.40625q0.546875 -0.40625 0.859375 -1.296875zm-4.609375 -2.28125l4.625 0q-0.09375 -1.046875 -0.53125 -1.5625q-0.671875 -0.8125 -1.734375 -0.8125q-0.96875 0 -1.640625 0.65625q-0.65625 0.640625 -0.71875 1.71875zm13.2109375 4.953125l0 -1.046875q-0.78125 1.234375 -2.3125 1.234375q-1.0 0 -1.828125 -0.546875q-0.828125 -0.546875 -1.296875 -1.53125q-0.453125 -0.984375 -0.453125 -2.25q0 -1.25 0.40625 -2.25q0.421875 -1.015625 1.25 -1.546875q0.828125 -0.546875 1.859375 -0.546875q0.75 0 1.328125 0.3125q0.59375 0.3125 0.953125 0.828125l0 -4.109375l1.40625 0l0 11.453125l-1.3125 0zm-4.4375 -4.140625q0 1.59375 0.671875 2.390625q0.671875 0.78125 1.578125 0.78125q0.921875 0 1.5625 -0.75q0.65625 -0.765625 0.65625 -2.3125q0 -1.703125 -0.65625 -2.5q-0.65625 -0.796875 -1.625 -0.796875q-0.9375 0 -1.5625 0.765625q-0.625 0.765625 -0.625 2.421875zm13.3984375 4.140625l0 -1.21875q-0.96875 1.40625 -2.640625 1.40625q-0.734375 0 -1.375 -0.28125q-0.625 -0.28125 -0.9375 -0.703125q-0.3125 -0.4375 -0.4375 -1.046875q-0.078125 -0.421875 -0.078125 -1.3125l0 -5.140625l1.40625 0l0 4.59375q0 1.109375 0.078125 1.484375q0.140625 0.5625 0.5625 0.875q0.4375 0.3125 1.0625 0.3125q0.640625 0 1.1875 -0.3125q0.5625 -0.328125 0.78125 -0.890625q0.234375 -0.5625 0.234375 -1.625l0 -4.4375l1.40625 0l0 8.296875l-1.25 0zm3.4296875 0l0 -11.453125l1.40625 0l0 11.453125l-1.40625 0zm9.2578125 -2.671875l1.453125 0.171875q-0.34375 1.28125 -1.28125 1.984375q-0.921875 0.703125 -2.359375 0.703125q-1.828125 0 -2.890625 -1.125q-1.0625 -1.125 -1.0625 -3.140625q0 -2.09375 1.078125 -3.25q1.078125 -1.15625 2.796875 -1.15625q1.65625 0 2.703125 1.140625q1.0625 1.125 1.0625 3.171875q0 0.125 0 0.375l-6.1875 0q0.078125 1.375 0.765625 2.109375q0.703125 0.71875 1.734375 0.71875q0.78125 0 1.328125 -0.40625q0.546875 -0.40625 0.859375 -1.296875zm-4.609375 -2.28125l4.625 0q-0.09375 -1.046875 -0.53125 -1.5625q-0.671875 -0.8125 -1.734375 -0.8125q-0.96875 0 -1.640625 0.65625q-0.65625 0.640625 -0.71875 1.71875z" fill-rule="nonzero"/><path fill="#000000" d="m136.96883 165.60799l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm9.0078125 4.875l-1.359375 -8.296875l1.375 0l0.71875 4.5625q0.109375 0.75 0.28125 2.453125q0.40625 -0.890625 1.046875 -2.03125l2.765625 -4.984375l1.5 0l-4.75 8.296875l-1.578125 0zm12.0 -2.828125l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm6.9140625 4.875l1.734375 -8.296875l1.25 0l-0.34375 1.6875q0.640625 -0.953125 1.25 -1.40625q0.609375 -0.46875 1.25 -0.46875q0.421875 0 1.046875 0.296875l-0.578125 1.3125q-0.375 -0.265625 -0.8125 -0.265625q-0.734375 0 -1.515625 0.828125q-0.78125 0.828125 -1.234375 2.984375l-0.703125 3.328125l-1.34375 0zm4.796875 3.203125l0.09375 -1.328125q0.4375 0.125 0.84375 0.125q0.4375 0 0.703125 -0.1875q0.34375 -0.265625 0.75 -0.96875l0.453125 -0.8125l-1.390625 -8.328125l1.390625 0l0.625 4.1875q0.1875 1.25 0.328125 2.484375l3.703125 -6.671875l1.484375 0l-5.296875 9.40625q-0.765625 1.375 -1.359375 1.8125q-0.59375 0.453125 -1.359375 0.453125q-0.5 0 -0.96875 -0.171875zm12.9765625 -3.203125l1.734375 -8.296875l1.40625 0l-0.28125 1.359375q0.78125 -0.875 1.40625 -1.203125q0.625 -0.34375 1.34375 -0.34375q0.78125 0 1.296875 0.40625q0.515625 0.40625 0.6875 1.140625q0.625 -0.78125 1.328125 -1.15625q0.703125 -0.390625 1.46875 -0.390625q1.046875 0 1.5625 0.5q0.515625 0.484375 0.515625 1.375q0 0.375 -0.171875 1.265625l-1.125 5.34375l-1.40625 0l1.140625 -5.484375q0.140625 -0.671875 0.140625 -0.96875q0 -0.40625 -0.265625 -0.640625q-0.25 -0.234375 -0.71875 -0.234375q-0.625 0 -1.28125 0.390625q-0.65625 0.375 -1.03125 1.0q-0.359375 0.625 -0.625 1.90625l-0.84375 4.03125l-1.421875 0l1.171875 -5.609375q0.125 -0.5625 0.125 -0.8125q0 -0.40625 -0.25 -0.65625q-0.25 -0.25 -0.65625 -0.25q-0.609375 0 -1.265625 0.390625q-0.65625 0.375 -1.078125 1.0625q-0.40625 0.6875 -0.671875 1.953125l-0.828125 3.921875l-1.40625 0zm15.34375 -9.859375l0.328125 -1.59375l1.40625 0l-0.328125 1.59375l-1.40625 0zm-2.0625 9.859375l1.734375 -8.296875l1.40625 0l-1.734375 8.296875l-1.40625 0zm3.6015625 0l1.734375 -8.296875l1.28125 0l-0.296875 1.4375q0.828125 -0.84375 1.546875 -1.234375q0.734375 -0.390625 1.5 -0.390625q1.0 0 1.578125 0.546875q0.578125 0.546875 0.578125 1.453125q0 0.46875 -0.203125 1.453125l-1.0625 5.03125l-1.40625 0l1.09375 -5.265625q0.171875 -0.765625 0.171875 -1.125q0 -0.421875 -0.296875 -0.671875q-0.28125 -0.265625 -0.8125 -0.265625q-1.09375 0 -1.9375 0.78125q-0.84375 0.78125 -1.25 2.6875l-0.796875 3.859375l-1.421875 0zm14.5703125 -1.5q-1.5 1.6875 -3.0625 1.6875q-0.953125 0 -1.546875 -0.546875q-0.59375 -0.5625 -0.59375 -1.359375q0 -0.515625 0.265625 -1.796875l1.0 -4.78125l1.421875 0l-1.109375 5.296875q-0.140625 0.671875 -0.140625 1.03125q0 0.46875 0.28125 0.734375q0.28125 0.265625 0.84375 0.265625q0.59375 0 1.15625 -0.28125q0.5625 -0.296875 0.96875 -0.78125q0.421875 -0.5 0.671875 -1.171875q0.171875 -0.4375 0.40625 -1.515625l0.75 -3.578125l1.40625 0l-1.734375 8.296875l-1.296875 0l0.3125 -1.5zm6.5390625 0.34375l-0.234375 1.171875q-0.515625 0.125 -0.984375 0.125q-0.84375 0 -1.34375 -0.421875q-0.375 -0.3125 -0.375 -0.84375q0 -0.28125 0.203125 -1.265625l1.0 -4.8125l-1.109375 0l0.21875 -1.09375l1.125 0l0.421875 -2.046875l1.625 -0.96875l-0.640625 3.015625l1.390625 0l-0.234375 1.09375l-1.375 0l-0.96875 4.578125q-0.171875 0.875 -0.171875 1.046875q0 0.25 0.140625 0.390625q0.140625 0.125 0.46875 0.125q0.46875 0 0.84375 -0.09375zm7.2265625 -1.671875l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 192.92389l233.00789 0l0 112.12598l-233.00789 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 192.92389l233.00789 0l0 112.12598l-233.00789 0z" fill-rule="evenodd"/><path fill="#000000" d="m34.312584 217.28389l4.40625 -11.453125l1.640625 0l4.6875 11.453125l-1.734375 0l-1.328125 -3.46875l-4.796875 0l-1.25 3.46875l-1.625 0zm3.3125 -4.703125l3.890625 0l-1.203125 -3.171875q-0.546875 -1.453125 -0.8125 -2.375q-0.21875 1.09375 -0.609375 2.1875l-1.265625 3.359375zm13.859375 1.65625l1.390625 0.1875q-0.234375 1.421875 -1.171875 2.234375q-0.921875 0.8125 -2.28125 0.8125q-1.703125 0 -2.75 -1.109375q-1.03125 -1.125 -1.03125 -3.203125q0 -1.34375 0.4375 -2.34375q0.453125 -1.015625 1.359375 -1.515625q0.921875 -0.5 1.984375 -0.5q1.359375 0 2.21875 0.6875q0.859375 0.671875 1.09375 1.9375l-1.359375 0.203125q-0.203125 -0.828125 -0.703125 -1.25q-0.484375 -0.421875 -1.1875 -0.421875q-1.0625 0 -1.734375 0.765625q-0.65625 0.75 -0.65625 2.40625q0 1.671875 0.640625 2.4375q0.640625 0.75 1.671875 0.75q0.828125 0 1.375 -0.5q0.5625 -0.515625 0.703125 -1.578125zm5.65625 1.78125l0.203125 1.25q-0.59375 0.125 -1.0625 0.125q-0.765625 0 -1.1875 -0.234375q-0.421875 -0.25 -0.59375 -0.640625q-0.171875 -0.40625 -0.171875 -1.671875l0 -4.765625l-1.03125 0l0 -1.09375l1.03125 0l0 -2.0625l1.40625 -0.84375l0 2.90625l1.40625 0l0 1.09375l-1.40625 0l0 4.84375q0 0.609375 0.0625 0.78125q0.078125 0.171875 0.25 0.28125q0.171875 0.09375 0.484375 0.09375q0.234375 0 0.609375 -0.0625zm1.3828125 -8.578125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm3.0234375 -4.15625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0zm8.3359375 -2.484375l1.390625 -0.21875q0.109375 0.84375 0.640625 1.296875q0.546875 0.4375 1.5 0.4375q0.96875 0 1.4375 -0.390625q0.46875 -0.40625 0.46875 -0.9375q0 -0.46875 -0.40625 -0.75q-0.296875 -0.1875 -1.4375 -0.46875q-1.546875 -0.390625 -2.15625 -0.671875q-0.59375 -0.296875 -0.90625 -0.796875q-0.296875 -0.5 -0.296875 -1.109375q0 -0.5625 0.25 -1.03125q0.25 -0.46875 0.6875 -0.78125q0.328125 -0.25 0.890625 -0.40625q0.578125 -0.171875 1.21875 -0.171875q0.984375 0 1.71875 0.28125q0.734375 0.28125 1.078125 0.765625q0.359375 0.46875 0.5 1.28125l-1.375 0.1875q-0.09375 -0.640625 -0.546875 -1.0q-0.453125 -0.359375 -1.265625 -0.359375q-0.96875 0 -1.390625 0.328125q-0.40625 0.3125 -0.40625 0.734375q0 0.28125 0.171875 0.5q0.171875 0.21875 0.53125 0.375q0.21875 0.078125 1.25 0.359375q1.484375 0.390625 2.078125 0.65625q0.59375 0.25 0.921875 0.734375q0.34375 0.484375 0.34375 1.203125q0 0.703125 -0.421875 1.328125q-0.40625 0.609375 -1.1875 0.953125q-0.765625 0.34375 -1.734375 0.34375q-1.625 0 -2.46875 -0.671875q-0.84375 -0.671875 -1.078125 -2.0z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m45.49346 236.11156l195.77954 0l0 53.48033l-195.77954 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m45.49346 236.11156l195.77954 0l0 53.48033l-195.77954 0z" fill-rule="evenodd"/><path fill="#000000" d="m55.446587 256.63156l0 -8.59375l6.203125 0l0 1.015625l-5.0625 0l0 2.625l4.75 0l0 1.015625l-4.75 0l0 2.921875l5.265625 0l0 1.015625l-6.40625 0zm7.8476562 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm14.058594 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.7050781 -4.25l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6347656 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m61.55596 267.61594l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923828 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm0.6464844 -2.171875q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.970703 3.109375l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm4.2929688 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm6.3867188 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm9.949219 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34374237 0 0.5468674 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.3749924 -6.234375l1.140625 0l1.2968674 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.8593674 0.25q-0.296875 0 -0.671875 -0.140625zm5.4531174 -2.390625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm7.0996094 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm1.546875 -0.828125q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125z" fill-rule="nonzero"/><path fill="#000000" d="m59.08721 277.63156l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0z" fill-rule="nonzero"/><path fill="#010000" fill-opacity="0.0" d="m103.84112 273.76828l58.04882 0l0 13.798828l-58.04882 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m104.34112 282.2253l0.3125 -1.40625q0.421875 -0.015625 0.671875 -0.125q0.25 -0.109375 0.453125 -0.375q0.21875 -0.265625 0.34375 -0.609375q0.140625 -0.34375 0.296875 -1.09375q0.21875 -1.0625 0.4375 -1.515625q0.234375 -0.453125 0.546875 -0.703125q0.328125 -0.25 0.890625 -0.40625q0.359375 -0.09375 1.125 -0.09375l0.3125 0l-0.3125 1.390625q-0.65625 0 -0.875 0.09375q-0.21875 0.078125 -0.328125 0.265625q-0.078125 0.109375 -0.265625 0.859375q-0.328125 1.296875 -0.4375 1.578125q-0.203125 0.515625 -0.5 0.828125q-0.28125 0.3125 -0.796875 0.609375q0.34375 0.234375 0.5 0.5625q0.171875 0.3125 0.171875 0.84375q0 0.203125 -0.078125 0.625q-0.1875 1.15625 -0.25 1.359375q-0.046875 0.21875 -0.046875 0.359375q0 0.15625 0.078125 0.265625q0.09375 0.109375 0.25 0.15625q0.171875 0.0625 0.75 0.0625l-0.3125 1.40625l-0.3125 0q-0.796875 0 -1.203125 -0.140625q-0.390625 -0.125 -0.609375 -0.4375q-0.21875 -0.296875 -0.21875 -0.78125q0 -0.390625 0.203125 -1.328125q0.203125 -0.9375 0.203125 -1.25q0 -0.5 -0.21875 -0.734375q-0.21875 -0.234375 -0.78125 -0.265625zm4.669922 0l0.3125 -1.40625q0.421875 -0.015625 0.671875 -0.125q0.25 -0.109375 0.453125 -0.375q0.21875 -0.265625 0.34375 -0.609375q0.140625 -0.34375 0.296875 -1.09375q0.21875 -1.0625 0.4375 -1.515625q0.234375 -0.453125 0.546875 -0.703125q0.328125 -0.25 0.890625 -0.40625q0.359375 -0.09375 1.125 -0.09375l0.3125 0l-0.3125 1.390625q-0.65625 0 -0.875 0.09375q-0.21875 0.078125 -0.328125 0.265625q-0.078125 0.109375 -0.265625 0.859375q-0.328125 1.296875 -0.4375 1.578125q-0.203125 0.515625 -0.5 0.828125q-0.28125 0.3125 -0.796875 0.609375q0.34375 0.234375 0.5 0.5625q0.171875 0.3125 0.171875 0.84375q0 0.203125 -0.078125 0.625q-0.1875 1.15625 -0.25 1.359375q-0.046875 0.21875 -0.046875 0.359375q0 0.15625 0.078125 0.265625q0.09375 0.109375 0.25 0.15625q0.171875 0.0625 0.75 0.0625l-0.3125 1.40625l-0.3125 0q-0.796875 0 -1.203125 -0.140625q-0.390625 -0.125 -0.609375 -0.4375q-0.21875 -0.296875 -0.21875 -0.78125q0 -0.390625 0.203125 -1.328125q0.203125 -0.9375 0.203125 -1.25q0 -0.5 -0.21875 -0.734375q-0.21875 -0.234375 -0.78125 -0.265625zm4.435547 0.671875l1.625 -0.265625q0.21875 0.515625 0.53125 0.734375q0.328125 0.203125 0.890625 0.203125q0.578125 0 0.921875 -0.25q0.234375 -0.171875 0.234375 -0.421875q0 -0.171875 -0.125 -0.3125q-0.125 -0.125 -0.6875 -0.3125q-1.53125 -0.5 -1.890625 -0.796875q-0.5625 -0.453125 -0.5625 -1.203125q0 -0.734375 0.546875 -1.265625q0.765625 -0.75 2.28125 -0.75q1.203125 0 1.8125 0.453125q0.609375 0.4375 0.78125 1.1875l-1.5625 0.265625q-0.109375 -0.34375 -0.390625 -0.515625q-0.375 -0.21875 -0.90625 -0.21875q-0.53125 0 -0.765625 0.171875q-0.21875 0.171875 -0.21875 0.40625q0 0.234375 0.234375 0.375q0.140625 0.09375 0.9375 0.328125q1.234375 0.359375 1.65625 0.703125q0.578125 0.5 0.578125 1.171875q0 0.890625 -0.75 1.546875q-0.734375 0.640625 -2.09375 0.640625q-1.34375 0 -2.078125 -0.484375q-0.734375 -0.5 -1.0 -1.390625zm12.955078 -0.859375l-4.21875 0q-0.015625 0.109375 -0.015625 0.15625q0 0.625 0.359375 1.0q0.359375 0.375 0.859375 0.375q0.84375 0 1.3125 -0.859375l1.5 0.25q-0.4375 0.90625 -1.171875 1.359375q-0.734375 0.453125 -1.65625 0.453125q-1.25 0 -2.03125 -0.78125q-0.78125 -0.796875 -0.78125 -2.109375q0 -1.28125 0.703125 -2.265625q0.96875 -1.359375 2.765625 -1.359375q1.140625 0 1.8125 0.71875q0.671875 0.703125 0.671875 1.96875q0 0.609375 -0.109375 1.09375zm-1.421875 -1.015625q0 -0.125 0 -0.171875q0 -0.703125 -0.3125 -1.046875q-0.3125 -0.34375 -0.828125 -0.34375q-0.515625 0 -0.9375 0.390625q-0.40625 0.390625 -0.5625 1.171875l2.640625 0zm3.2363205 -2.609375l1.578125 0l-0.265625 1.203125q0.890625 -1.359375 1.90625 -1.359375q0.375 0 0.78125 0.1875l-0.640625 1.375q-0.234375 -0.078125 -0.484375 -0.078125q-0.4375 0 -0.890625 0.328125q-0.4375 0.328125 -0.703125 0.875q-0.25 0.546875 -0.484375 1.734375l-0.421875 1.953125l-1.6718674 0l1.2968674 -6.21875zm6.576172 6.21875l-1.453125 0l-1.25 -6.21875l1.65625 0l0.4375 2.703125q0.1875 1.109375 0.21875 1.46875q0.046875 -0.09375 0.40625 -0.75q0.359375 -0.671875 0.453125 -0.796875l1.546875 -2.625l1.8125 0l-3.828125 6.21875zm9.626953 -2.59375l-4.21875 0q-0.015625 0.109375 -0.015625 0.15625q0 0.625 0.359375 1.0q0.359375 0.375 0.859375 0.375q0.84375 0 1.3125 -0.859375l1.5 0.25q-0.4375 0.90625 -1.171875 1.359375q-0.734375 0.453125 -1.65625 0.453125q-1.25 0 -2.03125 -0.78125q-0.78125 -0.796875 -0.78125 -2.109375q0 -1.28125 0.703125 -2.265625q0.96875 -1.359375 2.765625 -1.359375q1.140625 0 1.8125 0.71875q0.671875 0.703125 0.671875 1.96875q0 0.609375 -0.109375 1.09375zm-1.421875 -1.015625q0 -0.125 0 -0.171875q0 -0.703125 -0.3125 -1.046875q-0.3125 -0.34375 -0.828125 -0.34375q-0.515625 0 -0.9375 0.390625q-0.40625 0.390625 -0.5625 1.171875l2.640625 0zm3.2363281 -2.609375l1.578125 0l-0.265625 1.203125q0.890625 -1.359375 1.90625 -1.359375q0.375 0 0.78125 0.1875l-0.640625 1.375q-0.234375 -0.078125 -0.484375 -0.078125q-0.4375 0 -0.890625 0.328125q-0.4375 0.328125 -0.703125 0.875q-0.25 0.546875 -0.484375 1.734375l-0.421875 1.953125l-1.671875 0l1.296875 -6.21875zm7.044922 3.8125q-0.53125 0.03125 -0.828125 0.21875q-0.296875 0.1875 -0.515625 0.609375q-0.21875 0.40625 -0.4375 1.4375q-0.203125 1.015625 -0.4375 1.453125q-0.21875 0.453125 -0.546875 0.703125q-0.3125 0.25 -0.875 0.40625q-0.375 0.109375 -1.125 0.109375l-0.3125 0l0.296875 -1.40625q0.640625 0 0.859375 -0.09375q0.234375 -0.09375 0.359375 -0.265625q0.078125 -0.125 0.25 -0.84375q0.328125 -1.296875 0.421875 -1.546875q0.21875 -0.515625 0.53125 -0.875q0.3125 -0.359375 0.796875 -0.609375q-0.390625 -0.28125 -0.546875 -0.578125q-0.140625 -0.296875 -0.140625 -0.828125q0 -0.203125 0.078125 -0.625q0.1875 -1.125 0.234375 -1.34375q0.0625 -0.234375 0.0625 -0.390625q0 -0.15625 -0.078125 -0.25q-0.078125 -0.109375 -0.25 -0.15625q-0.15625 -0.0625 -0.75 -0.0625l0.3125 -1.390625l0.3125 0q0.796875 0 1.1875 0.140625q0.40625 0.125 0.625 0.421875q0.21875 0.296875 0.21875 0.78125q0 0.390625 -0.203125 1.328125q-0.203125 0.9375 -0.203125 1.25q0 0.5 0.21875 0.734375q0.21875 0.234375 0.78125 0.265625l-0.296875 1.40625zm4.669922 0q-0.53125 0.03125 -0.828125 0.21875q-0.296875 0.1875 -0.515625 0.609375q-0.21875 0.40625 -0.4375 1.4375q-0.203125 1.015625 -0.4375 1.453125q-0.21875 0.453125 -0.546875 0.703125q-0.3125 0.25 -0.875 0.40625q-0.375 0.109375 -1.125 0.109375l-0.3125 0l0.296875 -1.40625q0.640625 0 0.859375 -0.09375q0.234375 -0.09375 0.359375 -0.265625q0.078125 -0.125 0.25 -0.84375q0.328125 -1.296875 0.421875 -1.546875q0.21875 -0.515625 0.53125 -0.875q0.3125 -0.359375 0.796875 -0.609375q-0.390625 -0.28125 -0.546875 -0.578125q-0.140625 -0.296875 -0.140625 -0.828125q0 -0.203125 0.078125 -0.625q0.1875 -1.125 0.234375 -1.34375q0.0625 -0.234375 0.0625 -0.390625q0 -0.15625 -0.078125 -0.25q-0.078125 -0.109375 -0.25 -0.15625q-0.15625 -0.0625 -0.75 -0.0625l0.3125 -1.390625l0.3125 0q0.796875 0 1.1875 0.140625q0.40625 0.125 0.625 0.421875q0.21875 0.296875 0.21875 0.78125q0 0.390625 -0.203125 1.328125q-0.203125 0.9375 -0.203125 1.25q0 0.5 0.21875 0.734375q0.21875 0.234375 0.78125 0.265625l-0.296875 1.40625z" fill-rule="nonzero"/><path fill="#000000" d="m163.79619 277.24094l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m103.84112 285.41156l58.04882 0l0 1.0799866l-58.04882 0l0 -1.0799866z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m637.41473 52.889763l139.37012 0l0 56.598427l-139.37012 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m637.41473 52.889763l139.37012 0l0 56.598427l-139.37012 0z" fill-rule="evenodd"/><path fill="#000000" d="m647.36786 73.40977l0 -8.59375l6.203125 0l0 1.015625l-5.0625 0l0 2.625l4.75 0l0 1.015625l-4.75 0l0 2.921875l5.265625 0l0 1.015625l-6.40625 0zm7.8476562 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm14.058594 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.7050781 -4.25l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6347656 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m653.47723 84.39414l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923828 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm0.6464844 -2.171875q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.970703 3.109375l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm4.2929688 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm6.3867188 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm9.949219 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm5.453125 -2.390625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm7.0996094 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm1.546875 -0.828125q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125z" fill-rule="nonzero"/><path fill="#000000" d="m651.0085 94.40977l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0z" fill-rule="nonzero"/><path fill="#010000" fill-opacity="0.0" d="m695.7624 90.546486l28.02539 0l0 13.798828l-28.02539 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m697.68427 101.40977l-2.28125 0l3.78125 -4.34375l-2.125 -4.25l1.84375 0l0.84375 1.609375q0.03125 0.09375 0.5625 1.1875q0.046875 0.0625 0.078125 0.171875q0.71875 -0.875 1.203125 -1.4375l1.359375 -1.53125l2.21875 0l-3.9375 4.53125l2.125 4.0625l-2.0 0l-0.640625 -1.296875q-0.5 -1.03125 -0.671875 -1.46875q-0.21875 0.34375 -1.171875 1.421875l-1.1875 1.34375zm7.5039062 -4.9375l0.3125 -1.5q2.1875 -0.953125 3.375 -2.203125l1.015625 0l-1.8125 8.640625l-1.6875 0l1.25 -5.953125q-0.53125 0.34375 -1.21875 0.625q-0.6875 0.28125 -1.234375 0.390625zm8.423828 -0.96875l-1.640625 -0.25q0.1875 -1.203125 0.921875 -1.828125q0.734375 -0.640625 1.828125 -0.640625q1.203125 0 1.875 0.65625q0.6875 0.640625 0.6875 1.640625q0 0.515625 -0.21875 1.03125q-0.203125 0.515625 -0.671875 1.078125q-0.34375 0.390625 -1.140625 1.125q-0.78125 0.71875 -1.046875 1.0q-0.25 0.265625 -0.484375 0.5625l2.984375 0l-0.3125 1.53125l-5.21875 0q0.078125 -0.640625 0.359375 -1.203125q0.296875 -0.578125 0.71875 -1.0625q0.4375 -0.5 1.59375 -1.59375q0.875 -0.828125 1.09375 -1.078125q0.421875 -0.453125 0.5625 -0.78125q0.15625 -0.34375 0.15625 -0.609375q0 -0.4375 -0.25 -0.703125q-0.25 -0.28125 -0.65625 -0.28125q-0.390625 0 -0.6875 0.3125q-0.296875 0.296875 -0.453125 1.09375zm4.111328 3.65625l1.609375 -0.1875q0.109375 0.6875 0.359375 0.953125q0.265625 0.265625 0.703125 0.265625q0.5625 0 0.9375 -0.390625q0.390625 -0.390625 0.390625 -1.0q0 -0.515625 -0.3125 -0.828125q-0.296875 -0.3125 -0.8125 -0.3125q-0.125 0 -0.265625 0.03125l0.28125 -1.40625q0.09375 0.015625 0.171875 0.015625q0.671875 0 1.015625 -0.34375q0.359375 -0.34375 0.359375 -0.921875q0 -0.421875 -0.234375 -0.65625q-0.234375 -0.25 -0.625 -0.25q-0.359375 0 -0.65625 0.265625q-0.28125 0.265625 -0.453125 0.921875l-1.53125 -0.3125q0.296875 -0.953125 0.8125 -1.453125q0.796875 -0.765625 1.953125 -0.765625q1.171875 0 1.78125 0.65625q0.625 0.65625 0.625 1.578125q0 0.671875 -0.390625 1.21875q-0.390625 0.53125 -1.078125 0.78125q0.546875 0.28125 0.796875 0.75q0.265625 0.453125 0.265625 0.953125q0 0.953125 -0.671875 1.75q-0.921875 1.078125 -2.375 1.078125q-1.09375 0 -1.796875 -0.625q-0.703125 -0.625 -0.859375 -1.765625z" fill-rule="nonzero"/><path fill="#000000" d="m729.028 94.01914l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m695.7624 102.189766l28.02539 0l0 1.0800018l-28.02539 0l0 -1.0800018z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m258.3517 113.8189l81.543304 -32.62992" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m258.3517 113.8189l75.97275 -30.40084" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m334.93808 84.95158l3.5996704 -3.2194824l-4.826935 0.15245819z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m258.3517 113.8189l81.543304 72.37795" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m258.3517 113.8189l77.05597 68.395" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m334.31122 183.4492l4.490448 1.7771912l-2.2975159 -4.2478027z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m352.57217 6.0367455l140.56693 0l0 33.51181l-140.56693 0z" fill-rule="evenodd"/><path fill="#000000" d="m361.54092 32.956745l5.125 -13.359377l1.90625 0l5.46875 13.359377l-2.015625 0l-1.546875 -4.046877l-5.59375 0l-1.46875 4.046877l-1.875 0zm3.859375 -5.484377l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm9.802948 5.484377l0 -13.359377l1.640625 0l0 13.359377l-1.640625 0zm10.816711 -3.109377l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.312502q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.2968769q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm9.125702 5.765627l0 -9.671877l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.062502l-1.625 0zm9.806427 -1.4687519l0.234375 1.4531269q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.4687519 -0.203125 -1.9375019l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm7.2884827 1.4687519l0 -13.359377l1.765625 0l0 13.359377l-1.765625 0zm4.683319 0l0 -9.671877l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953127l-1.640625 0l0 -5.890627q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281252l-1.640625 0zm9.719452 -2.890627l1.625 -0.25q0.125 0.96875 0.75 1.5q0.625 0.515625 1.75 0.515625q1.125 0 1.671875 -0.453125q0.546875 -0.46875 0.546875 -1.09375q0 -0.546875 -0.484375 -0.875q-0.328125 -0.21875 -1.671875 -0.546875q-1.8125 -0.46875 -2.515625 -0.796875q-0.6875 -0.328125 -1.046875 -0.90625q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.640625 0.296875 -1.1875q0.296875 -0.5625 0.8125 -0.921875q0.375 -0.28125 1.03125 -0.46875q0.671875 -0.203125 1.421875 -0.203125q1.140625 0 2.0 0.328125q0.859375 0.328125 1.265625 0.890625q0.421875 0.5625 0.578125 1.5l-1.609375 0.21875q-0.109375 -0.75 -0.640625 -1.171875q-0.515625 -0.421875 -1.46875 -0.421875q-1.140625 0 -1.625 0.375q-0.46875 0.375 -0.46875 0.875q0 0.3125 0.1875 0.578125q0.203125 0.265625 0.640625 0.4375q0.234375 0.09375 1.4375 0.421875q1.75 0.453125 2.4375 0.75q0.6875 0.296875 1.078125 0.859375q0.390625 0.5625 0.390625 1.40625q0 0.828125 -0.484375 1.546875q-0.46875 0.7187519 -1.375 1.1250019q-0.90625 0.390625 -2.046875 0.390625q-1.875 0 -2.875 -0.78125q-0.984375 -0.7812519 -1.25 -2.328127zm13.5625 1.421875l0.234375 1.4531269q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.4687519 -0.203125 -1.9375019l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm7.917694 0.28125q-0.921875 0.7656269 -1.765625 1.0937519q-0.828125 0.3125 -1.796875 0.3125q-1.59375 0 -2.453125 -0.78125q-0.859375 -0.7812519 -0.859375 -1.9843769q0 -0.71875 0.328125 -1.296875q0.328125 -0.59375 0.84375 -0.9375q0.53125 -0.359375 1.1875 -0.546875q0.46875 -0.125 1.453125 -0.25q1.984375 -0.234375 2.921875 -0.5625q0.015625 -0.34375 0.015625 -0.421875q0 -1.0 -0.46875 -1.421875q-0.625 -0.546875 -1.875 -0.546875q-1.15625 0 -1.703125 0.40625q-0.546875 0.40625 -0.8125 1.421875l-1.609375 -0.21875q0.21875 -1.015625 0.71875 -1.640625q0.5 -0.640625 1.453125 -0.984375q0.953125 -0.34375 2.1875 -0.34375q1.25 0 2.015625 0.296875q0.78125 0.28125 1.140625 0.734375q0.375 0.4375 0.515625 1.109375q0.078125 0.421875 0.078125 1.515625l0 2.1875q0 2.28125 0.109375 2.890625q0.109375 0.5937519 0.40625 1.1562519l-1.703125 0q-0.265625 -0.515625 -0.328125 -1.1875019zm-0.140625 -3.671875q-0.890625 0.375 -2.671875 0.625q-1.015625 0.140625 -1.4375 0.328125q-0.421875 0.1875 -0.65625 0.53125q-0.21875 0.34375 -0.21875 0.78125q0 0.65625 0.5 1.09375q0.5 0.4375 1.453125 0.4375q0.9375 0 1.671875 -0.40625q0.75 -0.421875 1.09375 -1.140625q0.265625 -0.5625 0.265625 -1.640625l0 -0.609375zm4.203827 4.859377l0 -9.671877l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953127l-1.640625 0l0 -5.890627q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281252l-1.640625 0zm16.688232 -3.546877l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609377q-1.078125 0.9375 -2.671875 0.9375q-1.984375 0 -3.1875 -1.2968769q-1.203125 -1.296875 -1.203125 -3.71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0.96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm9.640625 0.4375l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.312502q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.2968769q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625z" fill-rule="nonzero"/><path fill="#ffffff" d="m339.90027 52.889763l139.37009 0l0 56.598427l-139.37009 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m339.90027 52.889763l139.37009 0l0 56.598427l-139.37009 0z" fill-rule="evenodd"/><path fill="#000000" d="m369.50748 82.893974l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm15.291229 0.921875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm7.0056458 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.1667175 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm8.062012 0l4.0625 -5.46875l-3.578125 -5.015625l1.65625 0l1.890625 2.6875q0.59375 0.828125 0.84375 1.28125q0.359375 -0.5625 0.84375 -1.1875l2.109375 -2.78125l1.5 0l-3.6875 4.9375l3.984375 5.546875l-1.71875 0l-2.640625 -3.75q-0.21875 -0.3125 -0.46875 -0.6875q-0.34375 0.5625 -0.5 0.78125l-2.625 3.65625l-1.671875 0zm15.166229 0l-1.28125 0l0 -8.203125q-0.46875 0.4375 -1.21875 0.890625q-0.75 0.4375 -1.359375 0.65625l0 -1.25q1.078125 -0.5 1.890625 -1.21875q0.8125 -0.734375 1.140625 -1.40625l0.828125 0l0 10.53125zm10.072968 -1.234375l0 1.234375l-6.9375 0q-0.015625 -0.46875 0.15625 -0.890625q0.265625 -0.71875 0.84375 -1.40625q0.59375 -0.6875 1.6875 -1.578125q1.71875 -1.40625 2.3125 -2.21875q0.609375 -0.828125 0.609375 -1.5625q0 -0.765625 -0.5625 -1.28125q-0.546875 -0.53125 -1.421875 -0.53125q-0.921875 0 -1.484375 0.5625q-0.5625 0.5625 -0.5625 1.546875l-1.328125 -0.140625q0.140625 -1.484375 1.015625 -2.25q0.890625 -0.78125 2.390625 -0.78125q1.515625 0 2.390625 0.84375q0.875 0.828125 0.875 2.0625q0 0.640625 -0.265625 1.25q-0.25 0.59375 -0.84375 1.265625q-0.59375 0.671875 -1.984375 1.859375q-1.15625 0.96875 -1.484375 1.3125q-0.328125 0.34375 -0.546875 0.703125l5.140625 0zm1.385498 -1.53125l1.296875 -0.171875q0.21875 1.09375 0.75 1.578125q0.53125 0.484375 1.296875 0.484375q0.90625 0 1.53125 -0.625q0.640625 -0.640625 0.640625 -1.5625q0 -0.890625 -0.59375 -1.46875q-0.578125 -0.578125 -1.46875 -0.578125q-0.359375 0 -0.90625 0.140625l0.140625 -1.125q0.125 0.015625 0.203125 0.015625q0.828125 0 1.484375 -0.421875q0.65625 -0.4375 0.65625 -1.34375q0 -0.703125 -0.484375 -1.15625q-0.46875 -0.46875 -1.234375 -0.46875q-0.75 0 -1.25 0.46875q-0.5 0.46875 -0.640625 1.421875l-1.28125 -0.234375q0.234375 -1.296875 1.0625 -2.0q0.84375 -0.71875 2.09375 -0.71875q0.859375 0 1.578125 0.375q0.71875 0.359375 1.09375 1.0q0.390625 0.640625 0.390625 1.34375q0 0.6875 -0.375 1.25q-0.359375 0.546875 -1.0625 0.875q0.921875 0.21875 1.4375 0.90625q0.515625 0.671875 0.515625 1.6875q0 1.375 -1.0 2.328125q-1.0 0.953125 -2.53125 0.953125q-1.390625 0 -2.296875 -0.8125q-0.90625 -0.828125 -1.046875 -2.140625z" fill-rule="nonzero"/><path fill="#ffffff" d="m339.89764 157.89842l140.56693 0l0 56.59842l-140.56693 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m339.89764 157.89842l140.56693 0l0 56.59842l-140.56693 0z" fill-rule="evenodd"/><path fill="#000000" d="m370.23578 187.90263l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm15.291229 0.921875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.1667175 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm7.0056458 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm11.828278 0l0 -4.4375l-4.046875 -6.046875l1.6875 0l2.0625 3.15625q0.578125 0.890625 1.0625 1.78125q0.484375 -0.828125 1.15625 -1.859375l2.03125 -3.078125l1.609375 0l-4.1875 6.046875l0 4.4375l-1.375 0zm10.416229 0l0 -2.515625l-4.546875 0l0 -1.171875l4.78125 -6.796875l1.0625 0l0 6.796875l1.40625 0l0 1.171875l-1.40625 0l0 2.515625l-1.296875 0zm0 -3.6875l0 -4.734375l-3.28125 4.734375l3.28125 0zm4.0260925 0.9375l1.34375 -0.109375q0.15625 0.984375 0.703125 1.484375q0.546875 0.5 1.328125 0.5q0.921875 0 1.5625 -0.703125q0.65625 -0.703125 0.65625 -1.859375q0 -1.109375 -0.625 -1.734375q-0.625 -0.640625 -1.625 -0.640625q-0.625 0 -1.125 0.28125q-0.5 0.28125 -0.78125 0.734375l-1.203125 -0.15625l1.015625 -5.390625l5.21875 0l0 1.21875l-4.1875 0l-0.578125 2.828125q0.953125 -0.65625 1.984375 -0.65625q1.375 0 2.3125 0.953125q0.953125 0.953125 0.953125 2.4375q0 1.4375 -0.828125 2.46875q-1.015625 1.265625 -2.75 1.265625q-1.4375 0 -2.34375 -0.796875q-0.90625 -0.796875 -1.03125 -2.125zm14.838623 -5.171875l-1.28125 0.09375q-0.171875 -0.75 -0.484375 -1.09375q-0.53125 -0.546875 -1.296875 -0.546875q-0.609375 0 -1.078125 0.34375q-0.609375 0.4375 -0.96875 1.296875q-0.34375 0.84375 -0.359375 2.421875q0.46875 -0.71875 1.140625 -1.0625q0.671875 -0.34375 1.40625 -0.34375q1.296875 0 2.203125 0.953125q0.90625 0.953125 0.90625 2.453125q0 0.984375 -0.4375 1.84375q-0.421875 0.84375 -1.171875 1.296875q-0.734375 0.4375 -1.6875 0.4375q-1.609375 0 -2.625 -1.171875q-1.015625 -1.1875 -1.015625 -3.90625q0 -3.046875 1.125 -4.421875q0.984375 -1.203125 2.640625 -1.203125q1.234375 0 2.03125 0.703125q0.796875 0.6875 0.953125 1.90625zm-5.265625 4.515625q0 0.671875 0.28125 1.28125q0.28125 0.609375 0.78125 0.9375q0.515625 0.3125 1.078125 0.3125q0.8125 0 1.390625 -0.65625q0.59375 -0.671875 0.59375 -1.796875q0 -1.09375 -0.578125 -1.71875q-0.578125 -0.625 -1.453125 -0.625q-0.875 0 -1.484375 0.625q-0.609375 0.625 -0.609375 1.640625z" fill-rule="nonzero"/><path fill="#ffffff" d="m339.90027 261.96118l140.56693 0l0 56.59842l-140.56693 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m339.90027 261.96118l140.56693 0l0 56.59842l-140.56693 0z" fill-rule="evenodd"/><path fill="#000000" d="m370.5174 291.9654l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm15.291229 0.921875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm7.0056458 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.1667175 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm8.296387 0l0 -1.28125l5.375 -6.734375q0.5625 -0.703125 1.078125 -1.234375l-5.84375 0l0 -1.234375l7.515625 0l0 1.234375l-5.890625 7.28125l-0.640625 0.734375l6.703125 0l0 1.234375l-8.296875 0zm9.343231 -9.125l0 -1.234375l6.796875 0l0 1.0q-1.0 1.078125 -1.984375 2.84375q-0.984375 1.765625 -1.515625 3.625q-0.390625 1.328125 -0.5 2.890625l-1.328125 0q0.03125 -1.234375 0.484375 -2.984375q0.46875 -1.765625 1.34375 -3.390625q0.875 -1.625 1.84375 -2.75l-5.140625 0zm10.057373 3.4375q-0.796875 -0.296875 -1.1875 -0.84375q-0.390625 -0.546875 -0.390625 -1.296875q0 -1.140625 0.8125 -1.921875q0.828125 -0.78125 2.203125 -0.78125q1.375 0 2.203125 0.796875q0.84375 0.796875 0.84375 1.9375q0 0.734375 -0.390625 1.28125q-0.375 0.53125 -1.15625 0.828125q0.96875 0.3125 1.46875 1.015625q0.5 0.703125 0.5 1.671875q0 1.34375 -0.953125 2.265625q-0.9375 0.90625 -2.5 0.90625q-1.546875 0 -2.5 -0.90625q-0.953125 -0.921875 -0.953125 -2.296875q0 -1.03125 0.515625 -1.71875q0.515625 -0.6875 1.484375 -0.9375zm-0.265625 -2.1875q0 0.75 0.484375 1.21875q0.484375 0.46875 1.25 0.46875q0.734375 0 1.203125 -0.46875q0.484375 -0.46875 0.484375 -1.140625q0 -0.71875 -0.5 -1.1875q-0.484375 -0.484375 -1.203125 -0.484375q-0.734375 0 -1.234375 0.46875q-0.484375 0.46875 -0.484375 1.125zm-0.40625 4.84375q0 0.5625 0.25 1.078125q0.265625 0.5 0.78125 0.796875q0.515625 0.28125 1.109375 0.28125q0.921875 0 1.515625 -0.59375q0.609375 -0.59375 0.609375 -1.515625q0 -0.9375 -0.625 -1.53125q-0.609375 -0.609375 -1.546875 -0.609375q-0.90625 0 -1.5 0.609375q-0.59375 0.59375 -0.59375 1.484375zm7.0260925 0.609375l1.25 -0.125q0.15625 0.875 0.59375 1.28125q0.4375 0.390625 1.140625 0.390625q0.59375 0 1.03125 -0.265625q0.453125 -0.28125 0.734375 -0.734375q0.296875 -0.453125 0.484375 -1.21875q0.1875 -0.78125 0.1875 -1.59375q0 -0.078125 0 -0.25q-0.390625 0.609375 -1.0625 1.0q-0.65625 0.375 -1.4375 0.375q-1.3125 0 -2.21875 -0.9375q-0.890625 -0.953125 -0.890625 -2.484375q0 -1.609375 0.9375 -2.578125q0.9375 -0.96875 2.359375 -0.96875q1.03125 0 1.875 0.546875q0.84375 0.546875 1.28125 1.578125q0.4375 1.015625 0.4375 2.953125q0 2.0 -0.4375 3.203125q-0.421875 1.1875 -1.296875 1.8125q-0.859375 0.609375 -2.015625 0.609375q-1.234375 0 -2.015625 -0.671875q-0.78125 -0.6875 -0.9375 -1.921875zm5.28125 -4.640625q0 -1.109375 -0.59375 -1.75q-0.59375 -0.65625 -1.421875 -0.65625q-0.859375 0 -1.5 0.703125q-0.625 0.703125 -0.625 1.8125q0 1.0 0.59375 1.640625q0.609375 0.625 1.5 0.625q0.890625 0 1.46875 -0.625q0.578125 -0.640625 0.578125 -1.75z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m258.3517 113.8189l81.543304 176.44095" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m258.3517 113.8189l79.026184 170.99445" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m335.87854 285.50632l3.4031677 3.4264832l-0.40444946 -4.812378z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m661.33215 6.0367227l140.5669 0l0 33.51181l-140.5669 0z" fill-rule="evenodd"/><path fill="#000000" d="m670.3009 32.956722l5.125 -13.359377l1.90625 0l5.46875 13.359377l-2.015625 0l-1.546875 -4.046877l-5.59375 0l-1.46875 4.046877l-1.875 0zm3.859375 -5.484377l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm16.162292 1.9375l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609377q-1.078125 0.9375 -2.671875 0.9375q-1.984375 0 -3.1875 -1.2968769q-1.203125 -1.296875 -1.203125 -3.71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0.96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm6.59375 2.078125l0.234375 1.4531269q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.4687519 -0.203125 -1.9375019l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm1.6052246 -10.0l0 -1.890625l1.640625 0l0 1.890625l-1.640625 0zm0 11.468752l0 -9.671877l1.640625 0l0 9.671877l-1.640625 0zm3.5354004 -4.843752q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.6562519q-1.0625 0.59375 -2.328125 0.59375q-2.03125 0 -3.28125 -1.2968769q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm9.297607 4.843752l0 -9.671877l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953127l-1.640625 0l0 -5.890627q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281252l-1.640625 0z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m637.4121 157.89842l139.37012 0l0 56.59842l-139.37012 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m637.4121 157.89842l139.37012 0l0 56.59842l-139.37012 0z" fill-rule="evenodd"/><path fill="#000000" d="m647.36523 178.41843l0 -8.59375l6.203125 0l0 1.015625l-5.0625 0l0 2.625l4.75 0l0 1.015625l-4.75 0l0 2.921875l5.265625 0l0 1.015625l-6.40625 0zm7.8476562 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm14.058594 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.7050781 -4.25l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6347656 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m653.4746 189.4028l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923828 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm0.6464844 -2.171875q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.970703 3.109375l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm4.2929688 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm6.3867188 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm9.949219 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm5.453125 -2.390625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm7.0996094 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm1.546875 -0.828125q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125z" fill-rule="nonzero"/><path fill="#000000" d="m651.00586 199.41843l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0z" fill-rule="nonzero"/><path fill="#010000" fill-opacity="0.0" d="m695.75977 195.55515l8.003906 0l0 13.798828l-8.003906 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m700.57227 206.41843l-1.765625 0l0.703125 -3.34375l-2.375 -5.25l1.890625 0l1.0625 2.421875q0.375 0.875 0.5 1.265625q0.21875 -0.375 0.515625 -0.8125l0.8125 -1.125l1.203125 -1.75l2.0625 0l-3.9375 5.40625l-0.671875 3.1875z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m703.7637 195.55515l20.021484 0l0 13.798828l-20.021484 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m705.18555 201.48093l0.3125 -1.5q2.1875 -0.953125 3.375 -2.203125l1.015625 0l-1.8125 8.640625l-1.6875 0l1.25 -5.953125q-0.53125 0.34375 -1.21875 0.625q-0.6875 0.28125 -1.234375 0.390625zm8.423828 -0.96875l-1.640625 -0.25q0.1875 -1.203125 0.921875 -1.828125q0.734375 -0.640625 1.828125 -0.640625q1.203125 0 1.875 0.65625q0.6875 0.640625 0.6875 1.640625q0 0.515625 -0.21875 1.03125q-0.203125 0.515625 -0.671875 1.078125q-0.34375 0.390625 -1.140625 1.125q-0.78125 0.71875 -1.046875 1.0q-0.25 0.265625 -0.484375 0.5625l2.984375 0l-0.3125 1.53125l-5.21875 0q0.078125 -0.640625 0.359375 -1.203125q0.296875 -0.578125 0.71875 -1.0625q0.4375 -0.5 1.59375 -1.59375q0.875 -0.828125 1.09375 -1.078125q0.421875 -0.453125 0.5625 -0.78125q0.15625 -0.34375 0.15625 -0.609375q0 -0.4375 -0.25 -0.703125q-0.25 -0.28125 -0.65625 -0.28125q-0.390625 0 -0.6875 0.3125q-0.296875 0.296875 -0.453125 1.09375zm4.111328 3.65625l1.609375 -0.1875q0.109375 0.6875 0.359375 0.953125q0.265625 0.265625 0.703125 0.265625q0.5625 0 0.9375 -0.390625q0.390625 -0.390625 0.390625 -1.0q0 -0.515625 -0.3125 -0.828125q-0.296875 -0.3125 -0.8125 -0.3125q-0.125 0 -0.265625 0.03125l0.28125 -1.40625q0.09375 0.015625 0.171875 0.015625q0.671875 0 1.015625 -0.34375q0.359375 -0.34375 0.359375 -0.921875q0 -0.421875 -0.234375 -0.65625q-0.234375 -0.25 -0.625 -0.25q-0.359375 0 -0.65625 0.265625q-0.28125 0.265625 -0.453125 0.921875l-1.53125 -0.3125q0.296875 -0.953125 0.8125 -1.453125q0.796875 -0.765625 1.953125 -0.765625q1.171875 0 1.78125 0.65625q0.625 0.65625 0.625 1.578125q0 0.671875 -0.390625 1.21875q-0.390625 0.53125 -1.078125 0.78125q0.546875 0.28125 0.796875 0.75q0.265625 0.453125 0.265625 0.953125q0 0.953125 -0.671875 1.75q-0.921875 1.078125 -2.375 1.078125q-1.09375 0 -1.796875 -0.625q-0.703125 -0.625 -0.859375 -1.765625z" fill-rule="nonzero"/><path fill="#000000" d="m729.0254 199.0278l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m695.75977 207.19843l28.02539 0l0 1.0800018l-28.02539 0l0 -1.0800018z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m637.41473 261.96167l139.37012 0l0 56.59842l-139.37012 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m637.41473 261.96167l139.37012 0l0 56.59842l-139.37012 0z" fill-rule="evenodd"/><path fill="#000000" d="m647.36786 282.4817l0 -8.59375l6.203125 0l0 1.015625l-5.0625 0l0 2.625l4.75 0l0 1.015625l-4.75 0l0 2.921875l5.265625 0l0 1.015625l-6.40625 0zm7.8476562 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm14.058594 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.7050781 -4.25l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6347656 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m653.47723 293.46606l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923828 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm0.6464844 -2.171875q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.970703 3.109375l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm4.2929688 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm6.3867188 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm9.949219 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm5.453125 -2.390625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm7.0996094 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm1.546875 -0.828125q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125z" fill-rule="nonzero"/><path fill="#000000" d="m651.0085 303.4817l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0z" fill-rule="nonzero"/><path fill="#010000" fill-opacity="0.0" d="m695.7624 299.6184l7.330078 0l0 13.798828l-7.330078 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m696.05927 310.4817l0.28125 -1.359375l4.953125 -5.796875l-4.0 0l0.296875 -1.4375l6.1875 0l-0.25 1.34375l-5.0 5.84375q0.796875 -0.015625 0.921875 -0.015625q0.5625 0 1.546875 -0.015625l2.03125 0l-0.296875 1.4375l-6.671875 0z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m703.09247 299.6184l20.021484 0l0 13.798828l-20.021484 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m704.51434 305.5442l0.3125 -1.5q2.1875 -0.953125 3.375 -2.203125l1.015625 0l-1.8125 8.640625l-1.6875 0l1.25 -5.953125q-0.53125 0.34375 -1.21875 0.625q-0.6875 0.28125 -1.234375 0.390625zm8.423828 -0.96875l-1.640625 -0.25q0.1875 -1.203125 0.921875 -1.828125q0.734375 -0.640625 1.828125 -0.640625q1.203125 0 1.875 0.65625q0.6875 0.640625 0.6875 1.640625q0 0.515625 -0.21875 1.03125q-0.203125 0.515625 -0.671875 1.078125q-0.34375 0.390625 -1.140625 1.125q-0.78125 0.71875 -1.046875 1.0q-0.25 0.265625 -0.484375 0.5625l2.984375 0l-0.3125 1.53125l-5.21875 0q0.078125 -0.640625 0.359375 -1.203125q0.296875 -0.578125 0.71875 -1.0625q0.4375 -0.5 1.59375 -1.59375q0.875 -0.828125 1.09375 -1.078125q0.421875 -0.453125 0.5625 -0.78125q0.15625 -0.34375 0.15625 -0.609375q0 -0.4375 -0.25 -0.703125q-0.25 -0.28125 -0.65625 -0.28125q-0.390625 0 -0.6875 0.3125q-0.296875 0.296875 -0.453125 1.09375zm4.111328 3.65625l1.609375 -0.1875q0.109375 0.6875 0.359375 0.953125q0.265625 0.265625 0.703125 0.265625q0.5625 0 0.9375 -0.390625q0.390625 -0.390625 0.390625 -1.0q0 -0.515625 -0.3125 -0.828125q-0.296875 -0.3125 -0.8125 -0.3125q-0.125 0 -0.265625 0.03125l0.28125 -1.40625q0.09375 0.015625 0.171875 0.015625q0.671875 0 1.015625 -0.34375q0.359375 -0.34375 0.359375 -0.921875q0 -0.421875 -0.234375 -0.65625q-0.234375 -0.25 -0.625 -0.25q-0.359375 0 -0.65625 0.265625q-0.28125 0.265625 -0.453125 0.921875l-1.53125 -0.3125q0.296875 -0.953125 0.8125 -1.453125q0.796875 -0.765625 1.953125 -0.765625q1.171875 0 1.78125 0.65625q0.625 0.65625 0.625 1.578125q0 0.671875 -0.390625 1.21875q-0.390625 0.53125 -1.078125 0.78125q0.546875 0.28125 0.796875 0.75q0.265625 0.453125 0.265625 0.953125q0 0.953125 -0.671875 1.75q-0.921875 1.078125 -2.375 1.078125q-1.09375 0 -1.796875 -0.625q-0.703125 -0.625 -0.859375 -1.765625z" fill-rule="nonzero"/><path fill="#000000" d="m728.3542 303.09106l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m695.7624 311.2617l27.351562 0l0 1.0799866l-27.351562 0l0 -1.0799866z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m480.46457 186.19763l156.94485 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m480.46457 186.19763l150.94485 0" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m631.4094 187.84937l4.538147 -1.6517334l-4.538147 -1.6517334z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m479.27036 81.18898l158.1417 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m479.27036 81.18898l152.1417 0" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m631.41205 82.840706l4.538147 -1.6517258l-4.538147 -1.6517334z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m480.4672 290.2604l156.94485 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m480.4672 290.2604l150.94485 0" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m631.41205 291.91214l4.538147 -1.6517334l-4.538147 -1.6517334z" fill-rule="evenodd"/><path fill="#ffffff" d="m829.90027 157.89764l139.37006 0l0 56.59842l-139.37006 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m829.90027 157.89764l139.37006 0l0 56.59842l-139.37006 0z" fill-rule="evenodd"/><path fill="#000000" d="m862.00665 182.27686l0 -7.59375l1.15625 0l0 1.0625q0.34375 -0.5625 0.9375 -0.890625q0.609375 -0.34375 1.359375 -0.34375q0.84375 0 1.375 0.34375q0.546875 0.34375 0.765625 0.984375q0.90625 -1.328125 2.359375 -1.328125q1.125 0 1.734375 0.625q0.609375 0.625 0.609375 1.921875l0 5.21875l-1.28125 0l0 -4.78125q0 -0.78125 -0.125 -1.109375q-0.125 -0.34375 -0.453125 -0.546875q-0.328125 -0.21875 -0.78125 -0.21875q-0.796875 0 -1.328125 0.53125q-0.53125 0.53125 -0.53125 1.703125l0 4.421875l-1.28125 0l0 -4.9375q0 -0.859375 -0.3125 -1.28125q-0.3125 -0.4375 -1.03125 -0.4375q-0.546875 0 -1.015625 0.296875q-0.453125 0.28125 -0.671875 0.828125q-0.203125 0.546875 -0.203125 1.59375l0 3.9375l-1.28125 0zm12.14624 2.921875l-0.140625 -1.203125q0.421875 0.109375 0.734375 0.109375q0.4375 0 0.6875 -0.140625q0.265625 -0.140625 0.421875 -0.40625q0.125 -0.1875 0.390625 -0.953125q0.046875 -0.109375 0.125 -0.3125l-2.890625 -7.609375l1.390625 0l1.578125 4.390625q0.3125 0.84375 0.546875 1.765625q0.234375 -0.890625 0.53125 -1.734375l1.625 -4.421875l1.296875 0l-2.890625 7.71875q-0.46875 1.25 -0.734375 1.734375q-0.34375 0.625 -0.78125 0.921875q-0.4375 0.296875 -1.0625 0.296875q-0.375 0 -0.828125 -0.15625zm6.661499 -2.921875l0 -1.46875l1.46875 0l0 1.46875l-1.46875 0zm8.66571 -2.78125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm1.890625 -1.015625q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm16.582458 1.34375l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.182373 4.53125l0 -7.59375l1.15625 0l0 1.0625q0.34375 -0.5625 0.9375 -0.890625q0.609375 -0.34375 1.359375 -0.34375q0.84375 0 1.375 0.34375q0.546875 0.34375 0.765625 0.984375q0.90625 -1.328125 2.359375 -1.328125q1.125 0 1.734375 0.625q0.609375 0.625 0.609375 1.921875l0 5.21875l-1.28125 0l0 -4.78125q0 -0.78125 -0.125 -1.109375q-0.125 -0.34375 -0.453125 -0.546875q-0.328125 -0.21875 -0.78125 -0.21875q-0.796875 0 -1.328125 0.53125q-0.53125 0.53125 -0.53125 1.703125l0 4.421875l-1.28125 0l0 -4.9375q0 -0.859375 -0.3125 -1.28125q-0.3125 -0.4375 -1.03125 -0.4375q-0.546875 0 -1.015625 0.296875q-0.453125 0.28125 -0.671875 0.828125q-0.203125 0.546875 -0.203125 1.59375l0 3.9375l-1.28125 0zm17.161865 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm3.307373 -5.203125l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm3.2249146 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0z" fill-rule="nonzero"/><path fill="#000000" d="m876.8267 198.01123l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm13.046875 -0.1875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm7.005615 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm5.21875 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm8.20929 -2.78125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm7.578125 0.328125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m776.78485 81.18898l53.102356 105.00787" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m776.78485 81.18898l50.394714 99.653564" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m825.70557 181.58794l3.5219116 3.3043365l-0.5739136 -4.7951202z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m776.7822 186.19763l53.102356 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m776.7822 186.19763l47.102356 0" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m823.8846 187.84937l4.538086 -1.6517334l-4.538086 -1.6517334z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m776.78485 290.2609l53.102356 -104.063" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m776.78485 290.2609l50.375183 -98.71861" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m828.6313 192.29305l0.59143066 -4.792984l-3.5339355 3.2914581z" fill-rule="evenodd"/><path fill="#ffffff" d="m25.338583 342.32285l233.00789 0l0 105.00787l-233.00789 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.338583 342.32285l233.00789 0l0 105.00787l-233.00789 0z" fill-rule="evenodd"/><path fill="#000000" d="m46.104206 365.5072l1.890625 0.46875q-0.59375 2.34375 -2.140625 3.578125q-1.546875 1.21875 -3.796875 1.21875q-2.3125 0 -3.765625 -0.9375q-1.4375 -0.953125 -2.203125 -2.734375q-0.75 -1.796875 -0.75 -3.84375q0 -2.234375 0.84375 -3.890625q0.859375 -1.671875 2.4375 -2.53125q1.578125 -0.875 3.46875 -0.875q2.15625 0 3.609375 1.09375q1.46875 1.09375 2.046875 3.078125l-1.859375 0.4375q-0.5 -1.5625 -1.453125 -2.265625q-0.9375 -0.71875 -2.375 -0.71875q-1.65625 0 -2.765625 0.796875q-1.109375 0.78125 -1.5625 2.125q-0.4375 1.328125 -0.4375 2.734375q0 1.828125 0.53125 3.203125q0.53125 1.359375 1.65625 2.03125q1.125 0.671875 2.421875 0.671875q1.59375 0 2.703125 -0.921875q1.109375 -0.921875 1.5 -2.71875zm3.3496094 -0.171875q0 -2.875 1.59375 -4.265625q1.34375 -1.15625 3.265625 -1.15625q2.140625 0 3.484375 1.40625q1.359375 1.40625 1.359375 3.875q0 2.0 -0.59375 3.15625q-0.59375 1.140625 -1.75 1.78125q-1.140625 0.625 -2.5 0.625q-2.1875 0 -3.53125 -1.390625q-1.328125 -1.40625 -1.328125 -4.03125zm1.796875 0q0 2.0 0.859375 2.984375q0.875 0.984375 2.203125 0.984375q1.3125 0 2.171875 -0.984375q0.875 -1.0 0.875 -3.046875q0 -1.921875 -0.875 -2.90625q-0.875 -1.0 -2.171875 -1.0q-1.328125 0 -2.203125 1.0q-0.859375 0.984375 -0.859375 2.96875zm9.966797 5.1875l0 -10.375l1.59375 0l0 1.484375q1.140625 -1.71875 3.2968788 -1.71875q0.9375 0 1.71875 0.34375q0.796875 0.328125 1.1875 0.875q0.390625 0.546875 0.546875 1.296875q0.09375 0.5 0.09375 1.71875l0 6.375l-1.765625 0l0 -6.3125q0 -1.078125 -0.203125 -1.609375q-0.203125 -0.53125 -0.734375 -0.84375q-0.515625 -0.3125 -1.21875 -0.3125q-1.125 0 -1.9375038 0.71875q-0.8125 0.703125 -0.8125 2.6875l0 5.671875l-1.765625 0zm11.123051 0l0 -10.375l1.59375 0l0 1.484375q1.140625 -1.71875 3.296875 -1.71875q0.9375 0 1.71875 0.34375q0.796875 0.328125 1.1875 0.875q0.390625 0.546875 0.546875 1.296875q0.09375 0.5 0.09375 1.71875l0 6.375l-1.765625 0l0 -6.3125q0 -1.078125 -0.203125 -1.609375q-0.203125 -0.53125 -0.734375 -0.84375q-0.515625 -0.3125 -1.21875 -0.3125q-1.125 0 -1.9375 0.71875q-0.8125 0.703125 -0.8125 2.6875l0 5.671875l-1.765625 0zm18.232422 -3.34375l1.8125 0.234375q-0.421875 1.578125 -1.59375 2.46875q-1.15625 0.875 -2.96875 0.875q-2.265625 0 -3.609375 -1.390625q-1.328125 -1.40625 -1.328125 -3.9375q0 -2.625 1.34375 -4.0625q1.34375 -1.453125 3.5 -1.453125q2.078125 0 3.390625 1.421875q1.328125 1.40625 1.328125 3.984375q0 0.15625 -0.015625 0.46875l-7.734375 0q0.09375 1.703125 0.96875 2.609375q0.875 0.90625 2.171875 0.90625q0.96875 0 1.640625 -0.5q0.6875 -0.515625 1.09375 -1.625zm-5.78125 -2.84375l5.796875 0q-0.109375 -1.296875 -0.65625 -1.953125q-0.84375 -1.015625 -2.1875 -1.015625q-1.203125 0 -2.03125 0.8125q-0.828125 0.796875 -0.921875 2.15625zm16.576172 2.390625l1.71875 0.21875q-0.28125 1.796875 -1.453125 2.8125q-1.15625 1.0 -2.859375 1.0q-2.125 0 -3.421875 -1.390625q-1.296875 -1.390625 -1.296875 -3.984375q0 -1.6875 0.546875 -2.9375q0.5625 -1.265625 1.703125 -1.890625q1.140625 -0.640625 2.484375 -0.640625q1.6875 0 2.75 0.859375q1.078125 0.859375 1.390625 2.421875l-1.71875 0.265625q-0.234375 -1.046875 -0.859375 -1.5625q-0.625 -0.53125 -1.5 -0.53125q-1.328125 0 -2.15625 0.953125q-0.828125 0.953125 -0.828125 3.0q0 2.09375 0.796875 3.046875q0.796875 0.9375 2.09375 0.9375q1.03125 0 1.71875 -0.625q0.703125 -0.640625 0.890625 -1.953125zm7.0625 2.21875l0.25 1.5625q-0.734375 0.15625 -1.328125 0.15625q-0.953125 0 -1.484375 -0.296875q-0.515625 -0.3125 -0.734375 -0.796875q-0.21875 -0.5 -0.21875 -2.078125l0 -5.96875l-1.28125 0l0 -1.375l1.28125 0l0 -2.5625l1.75 -1.0625l0 3.625l1.765625 0l0 1.375l-1.765625 0l0 6.0625q0 0.75 0.09375 0.96875q0.09375 0.203125 0.296875 0.34375q0.21875 0.125 0.609375 0.125q0.28125 0 0.765625 -0.078125zm1.0722656 -3.609375q0 -2.875 1.59375 -4.265625q1.34375 -1.15625 3.265625 -1.15625q2.140625 0 3.484375 1.40625q1.359375 1.40625 1.359375 3.875q0 2.0 -0.59375 3.15625q-0.59375 1.140625 -1.75 1.78125q-1.140625 0.625 -2.5 0.625q-2.1875 0 -3.53125 -1.390625q-1.328125 -1.40625 -1.328125 -4.03125zm1.796875 0q0 2.0 0.859375 2.984375q0.875 0.984375 2.203125 0.984375q1.3125 0 2.171875 -0.984375q0.875 -1.0 0.875 -3.046875q0 -1.921875 -0.875 -2.90625q-0.875 -1.0 -2.171875 -1.0q-1.328125 0 -2.203125 1.0q-0.859375 0.984375 -0.859375 2.96875zm9.951172 5.1875l0 -10.375l1.578125 0l0 1.578125q0.609375 -1.109375 1.125 -1.453125q0.515625 -0.359375 1.125 -0.359375q0.890625 0 1.8125 0.5625l-0.609375 1.640625q-0.640625 -0.390625 -1.28125 -0.390625q-0.578125 0 -1.046875 0.359375q-0.453125 0.34375 -0.65625 0.953125q-0.28125 0.9375 -0.28125 2.046875l0 5.4375l-1.765625 0zm11.560539 -4.296875l0 -1.765625l5.390625 0l0 1.765625l-5.390625 0zm12.888672 4.296875l0 -10.375l1.578125 0l0 1.453125q0.484375 -0.75 1.296875 -1.21875q0.8125 -0.46875 1.84375 -0.46875q1.15625 0 1.890625 0.484375q0.734375 0.46875 1.046875 1.328125q1.234375 -1.8125 3.203125 -1.8125q1.546875 0 2.375 0.859375q0.828125 0.859375 0.828125 2.625l0 7.125l-1.75 0l0 -6.53125q0 -1.0625 -0.171875 -1.515625q-0.171875 -0.46875 -0.625 -0.75q-0.4375 -0.28125 -1.046875 -0.28125q-1.09375 0 -1.828125 0.734375q-0.71875 0.71875 -0.71875 2.3125l0 6.03125l-1.75 0l0 -6.734375q0 -1.171875 -0.4375 -1.75q-0.421875 -0.59375 -1.40625 -0.59375q-0.734375 0 -1.375 0.390625q-0.625 0.390625 -0.90625 1.140625q-0.28125 0.75 -0.28125 2.171875l0 5.375l-1.765625 0zm16.582031 4.0l-0.1875 -1.65625q0.578125 0.15625 1.0 0.15625q0.59375 0 0.9375 -0.203125q0.359375 -0.1875 0.578125 -0.53125q0.171875 -0.265625 0.546875 -1.3125q0.046875 -0.15625 0.15625 -0.4375l-3.9375 -10.390625l1.890625 0l2.15625 6.015625q0.421875 1.140625 0.75 2.390625q0.3125 -1.203125 0.71875 -2.359375l2.21875 -6.046875l1.765625 0l-3.953125 10.546875q-0.625 1.71875 -0.984375 2.359375q-0.46875 0.875 -1.078125 1.265625q-0.59375 0.40625 -1.4375 0.40625q-0.515625 0 -1.140625 -0.203125zm9.09375 -4.0l0 -2.0l2.0 0l0 2.0l-2.0 0zm11.837891 -3.796875l1.71875 0.21875q-0.28125 1.796875 -1.453125 2.8125q-1.15625 1.0 -2.859375 1.0q-2.125 0 -3.421875 -1.390625q-1.296875 -1.390625 -1.296875 -3.984375q0 -1.6875 0.546875 -2.9375q0.5625 -1.265625 1.703125 -1.890625q1.140625 -0.640625 2.484375 -0.640625q1.6875 0 2.75 0.859375q1.078125 0.859375 1.390625 2.421875l-1.71875 0.265625q-0.234375 -1.046875 -0.859375 -1.5625q-0.625 -0.53125 -1.5 -0.53125q-1.328125 0 -2.15625 0.953125q-0.828125 0.953125 -0.828125 3.0q0 2.09375 0.796875 3.046875q0.796875 0.9375 2.09375 0.9375q1.03125 0 1.71875 -0.625q0.703125 -0.640625 0.890625 -1.953125zm2.578125 -1.390625q0 -2.875 1.59375 -4.265625q1.34375 -1.15625 3.265625 -1.15625q2.140625 0 3.484375 1.40625q1.359375 1.40625 1.359375 3.875q0 2.0 -0.59375 3.15625q-0.59375 1.140625 -1.75 1.78125q-1.140625 0.625 -2.5 0.625q-2.1875 0 -3.53125 -1.390625q-1.328125 -1.40625 -1.328125 -4.03125zm1.796875 0q0 2.0 0.859375 2.984375q0.875 0.984375 2.203125 0.984375q1.3125 0 2.171875 -0.984375q0.875 -1.0 0.875 -3.046875q0 -1.921875 -0.875 -2.90625q-0.875 -1.0 -2.171875 -1.0q-1.328125 0 -2.203125 1.0q-0.859375 0.984375 -0.859375 2.96875z" fill-rule="nonzero"/><path fill="#000000" d="m53.94483 392.84283l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.5156212 -0.25 1.0624962 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.0937462 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm6.8613243 -2.359375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.3281288 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875038 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.6718788 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5000038 0 -0.9062538 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.533207 0.34375l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm8.375 1.265625l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm2.1933594 -4.15625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm-1.046875 5.015625l0.25 -1.203125l1.203125 0l-0.265625 1.203125l-1.1875 0z" fill-rule="nonzero"/><path fill="#000000" d="m130.7292 392.84283l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm5.796875 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm7.3339844 -2.265625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.90625 -0.09375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125z" fill-rule="nonzero"/><path fill="#000000" d="m53.41358 409.23346l1.796875 -8.609375l0.984375 0l-0.1875 0.859375q0.546875 -0.5625 0.96875 -0.78125q0.4374962 -0.21875 0.9218712 -0.21875q0.890625 0 1.46875 0.65625q0.59375 0.640625 0.59375 1.84375q0 0.96875 -0.328125 1.765625q-0.3125 0.796875 -0.78125 1.296875q-0.46875 0.484375 -0.953125 0.71875q-0.484375 0.21875 -0.9843712 0.21875q-1.125 0 -1.734375 -1.140625l-0.703125 3.390625l-1.0625 0zm2.09375 -4.9375q0 0.703125 0.109375 0.96875q0.140625 0.375 0.46875 0.609375q0.34375 0.234375 0.78125 0.234375q0.9218712 0 1.4843712 -1.015625q0.5625 -1.03125 0.5625 -2.109375q0 -0.78125 -0.390625 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.4062462 0 -0.7499962 0.21875q-0.34375 0.21875 -0.640625 0.640625q-0.296875 0.421875 -0.5 1.046875q-0.1875 0.609375 -0.1875 1.0625zm5.2988243 0.1875q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.3281288 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875038 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.6718788 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5000038 0 -0.9062538 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439457 2.46875l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm6.4648438 -0.859375l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm2.1933594 -4.15625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm-1.046875 5.015625l0.25 -1.203125l1.203125 0l-0.265625 1.203125l-1.1875 0z" fill-rule="nonzero"/><path fill="#000000" d="m131.18233 404.3897l1.078125 -0.109375q-0.015625 0.234375 -0.015625 0.28125q0 0.375 0.1875 0.78125q0.203125 0.390625 0.53125 0.59375q0.34375 0.203125 0.71875 0.203125q0.5 0 1.0 -0.328125q0.515625 -0.34375 0.828125 -0.984375q0.3125 -0.65625 0.3125 -1.296875q0 -0.71875 -0.421875 -1.15625q-0.421875 -0.4375 -1.109375 -0.4375q-0.46875 0 -0.890625 0.234375q-0.40625 0.21875 -0.765625 0.65625l-0.921875 -0.0625l1.296875 -4.390625l4.203125 0l-0.21875 0.96875l-3.25 0l-0.65625 2.1875q0.375 -0.265625 0.75 -0.390625q0.390625 -0.140625 0.796875 -0.140625q0.984375 0 1.609375 0.65625q0.640625 0.640625 0.640625 1.78125q0 0.984375 -0.4375 1.828125q-0.4375 0.828125 -1.203125 1.28125q-0.75 0.4375 -1.625 0.4375q-0.75 0 -1.3125 -0.328125q-0.5625 -0.328125 -0.859375 -0.90625q-0.28125 -0.59375 -0.28125 -1.1875q0 -0.0625 0.015625 -0.171875zm8.533203 -2.265625q-0.46875 -0.265625 -0.71875 -0.671875q-0.234375 -0.421875 -0.234375 -0.890625q0 -0.78125 0.5625 -1.453125q0.75 -0.890625 2.03125 -0.890625q1.125 0 1.78125 0.609375q0.65625 0.609375 0.65625 1.484375q0 0.625 -0.359375 1.125q-0.34375 0.5 -1.09375 0.8125q0.515625 0.3125 0.734375 0.671875q0.28125 0.484375 0.28125 1.109375q0 1.15625 -0.8125 2.0625q-0.8125 0.90625 -2.171875 0.90625q-1.09375 0 -1.78125 -0.671875q-0.6875 -0.671875 -0.6875 -1.671875q0 -0.9375 0.484375 -1.609375q0.5 -0.6875 1.328125 -0.921875zm0.09375 -1.515625q0 0.546875 0.34375 0.890625q0.359375 0.328125 0.984375 0.328125q0.71875 0 1.171875 -0.4375q0.453125 -0.453125 0.453125 -1.09375q0 -0.546875 -0.359375 -0.890625q-0.34375 -0.34375 -0.96875 -0.34375q-0.46875 0 -0.859375 0.21875q-0.390625 0.21875 -0.578125 0.59375q-0.1875 0.375 -0.1875 0.734375zm-0.828125 4.140625q0 0.359375 0.171875 0.6875q0.171875 0.328125 0.515625 0.515625q0.359375 0.1875 0.8125 0.1875q0.859375 0 1.390625 -0.796875q0.421875 -0.640625 0.421875 -1.375q0 -0.59375 -0.390625 -0.96875q-0.390625 -0.390625 -1.015625 -0.390625q-0.796875 0 -1.359375 0.59375q-0.546875 0.59375 -0.546875 1.546875zm6.439453 2.09375q0.140625 -0.875 0.609375 -2.09375q0.546875 -1.46875 1.421875 -2.90625q0.890625 -1.453125 1.96875 -2.5l-4.28125 0l0.203125 -0.96875l5.484375 0l-0.203125 0.96875q-1.390625 1.21875 -2.4375 3.15625q-1.203125 2.234375 -1.65625 4.34375l-1.109375 0z" fill-rule="nonzero"/><path fill="#000000" d="m58.179203 419.71783q-1.1093712 1.265625 -2.2812462 1.265625q-0.71875 0 -1.171875 -0.40625q-0.4375 -0.421875 -0.4375 -1.015625q0 -0.390625 0.203125 -1.34375l0.75 -3.59375l1.0625 0l-0.84375 3.96875q-0.09375 0.5 -0.09375 0.78125q0 0.34375 0.203125 0.546875q0.21875 0.1875 0.640625 0.1875q0.4375 0 0.859375 -0.203125q0.4374962 -0.21875 0.7343712 -0.59375q0.3125 -0.375 0.5 -0.875q0.140625 -0.3125 0.296875 -1.125l0.5625 -2.6875l1.0625 0l-1.296875 6.21875l-0.984375 0l0.234375 -1.125zm2.5332031 -1.0l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.7031288 0 1.0468788 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625038 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.1718788 0 1.7812538 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625038 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.2187538 0.53125 1.5312538 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.3750038 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm10.468754 0.015625l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.189453 3.65625l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm3.9960938 0l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0zm10.861328 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm2.0800781 3.15625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.574219 -2.109375l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm6.517578 -1.359375l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm-1.046875 5.015625l0.25 -1.203125l1.203125 0l-0.265625 1.203125l-1.1875 0z" fill-rule="nonzero"/><path fill="#000000" d="m130.7292 420.84283l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm10.640625 -3.53125q-1.109375 1.265625 -2.28125 1.265625q-0.71875 0 -1.171875 -0.40625q-0.4375 -0.421875 -0.4375 -1.015625q0 -0.390625 0.203125 -1.34375l0.75 -3.59375l1.0625 0l-0.84375 3.96875q-0.09375 0.5 -0.09375 0.78125q0 0.34375 0.203125 0.546875q0.21875 0.1875 0.640625 0.1875q0.4375 0 0.859375 -0.203125q0.4375 -0.21875 0.734375 -0.59375q0.3125 -0.375 0.5 -0.875q0.140625 -0.3125 0.296875 -1.125l0.5625 -2.6875l1.0625 0l-1.296875 6.21875l-0.984375 0l0.234375 -1.125zm2.5332031 -1.0l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm10.46875 0.015625l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.189453 3.65625l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0z" fill-rule="nonzero"/><path fill="#000000" d="m53.41358 437.23346l1.796875 -8.609375l0.984375 0l-0.1875 0.859375q0.546875 -0.5625 0.96875 -0.78125q0.4374962 -0.21875 0.9218712 -0.21875q0.890625 0 1.46875 0.65625q0.59375 0.640625 0.59375 1.84375q0 0.96875 -0.328125 1.765625q-0.3125 0.796875 -0.78125 1.296875q-0.46875 0.484375 -0.953125 0.71875q-0.484375 0.21875 -0.9843712 0.21875q-1.125 0 -1.734375 -1.140625l-0.703125 3.390625l-1.0625 0zm2.09375 -4.9375q0 0.703125 0.109375 0.96875q0.140625 0.375 0.46875 0.609375q0.34375 0.234375 0.78125 0.234375q0.9218712 0 1.4843712 -1.015625q0.5625 -1.03125 0.5625 -2.109375q0 -0.78125 -0.390625 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.4062462 0 -0.7499962 0.21875q-0.34375 0.21875 -0.640625 0.640625q-0.296875 0.421875 -0.5 1.046875q-0.1875 0.609375 -0.1875 1.0625zm9.298828 1.765625q-0.546875 0.484375 -1.0625038 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0000038 -0.046875 1.4218788 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.6718788 0 -1.0937538 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.7968788 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.8437538 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.4843788 -0.28125 0.7656288 -0.78125q0.28125 -0.5 0.4375 -1.421875zm2.1894531 1.03125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm6.0 0l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm7.078125 2.125l-0.640625 -6.21875l1.015625 0l0.28125 2.796875l0.109375 1.5625q0.015625 0.171875 0.03125 0.703125q0.1875 -0.59375 0.296875 -0.90625q0.125 -0.328125 0.40625 -0.921875l1.46875 -3.234375l1.15625 0l0.203125 3.109375q0.046875 0.703125 0.0625 1.890625q0.296875 -0.78125 0.953125 -2.1875l1.34375 -2.8125l1.0625 0l-3.046875 6.21875l-1.09375 0l-0.234375 -3.65625q-0.03125 -0.328125 -0.046875 -1.3125q-0.234375 0.625 -0.5625 1.34375l-1.640625 3.625l-1.125 0zm7.6816406 -2.359375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm8.183594 -0.90625q-0.90625 1.046875 -1.890625 1.046875q-0.890625 0 -1.484375 -0.65625q-0.578125 -0.65625 -0.578125 -1.890625q0 -1.140625 0.453125 -2.078125q0.46875 -0.9375 1.171875 -1.40625q0.703125 -0.46875 1.40625 -0.46875q1.15625 0 1.75 1.109375l0.703125 -3.34375l1.046875 0l-1.78125 8.59375l-0.984375 0l0.1875 -0.90625zm-2.90625 -1.703125q0 0.65625 0.125 1.03125q0.140625 0.375 0.4375 0.625q0.3125 0.25 0.75 0.25q0.71875 0 1.3125 -0.75q0.78125 -1.0 0.78125 -2.46875q0 -0.734375 -0.390625 -1.140625q-0.390625 -0.421875 -0.96875 -0.421875q-0.375 0 -0.703125 0.171875q-0.3125 0.15625 -0.625 0.5625q-0.296875 0.40625 -0.515625 1.046875q-0.203125 0.625 -0.203125 1.09375zm6.720703 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm-1.046875 5.015625l0.25 -1.203125l1.203125 0l-0.265625 1.203125l-1.1875 0z" fill-rule="nonzero"/><path fill="#000000" d="m135.02608 432.5772l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.71875 2.265625l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm10.861328 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm2.0957031 3.15625l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0zm6.611328 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm9.970703 0.640625l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.173828 3.65625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.574219 -2.109375l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625z" fill-rule="nonzero"/><path fill="#666666" d="m152.55838 267.70898l0 0c0 -1.800354 1.3678284 -3.2598267 3.0551147 -3.2598267l0 0c0.8102722 0 1.5873566 0.34344482 2.1602936 0.95477295c0.5729523 0.61135864 0.89482117 1.4404907 0.89482117 2.3050537l0 0c0 1.8003845 -1.3678131 3.2598572 -3.0551147 3.2598572l0 0c-1.6872864 0 -3.0551147 -1.4594727 -3.0551147 -3.2598572z" fill-rule="evenodd"/><path fill="#666666" d="m639.8609 292.85303l0 0c0 -1.8003845 1.3677979 -3.2598572 3.0551147 -3.2598572l0 0c0.8102417 0 1.5873413 0.34344482 2.1602783 0.95480347c0.572937 0.6113281 0.8948364 1.4404907 0.8948364 2.3050537l0 0c0 1.800354 -1.3677979 3.2598267 -3.0551147 3.2598267l0 0c-1.6873169 0 -3.0551147 -1.4594727 -3.0551147 -3.2598267z" fill-rule="evenodd"/><path fill="#666666" d="m639.8609 189.4567l0 0c0 -1.8003693 1.3677979 -3.259842 3.0551147 -3.259842l0 0c0.8102417 0 1.5873413 0.34344482 2.1602783 0.9547882c0.572937 0.6113281 0.8948364 1.4404907 0.8948364 2.3050537l0 0c0 1.800354 -1.3677979 3.259842 -3.0551147 3.259842l0 0c-1.6873169 0 -3.0551147 -1.4594879 -3.0551147 -3.259842z" fill-rule="evenodd"/><path fill="#666666" d="m639.8609 83.031494l0 0c0 -1.8003616 1.3677979 -3.259842 3.0551147 -3.259842l0 0c0.8102417 0 1.5873413 0.34344482 2.1602783 0.9547882c0.572937 0.61133575 0.8948364 1.4404907 0.8948364 2.3050537l0 0c0 1.8003616 -1.3677979 3.259842 -3.0551147 3.259842l0 0c-1.6873169 0 -3.0551147 -1.4594803 -3.0551147 -3.259842z" fill-rule="evenodd"/><path fill="#999999" d="m244.2231 392.55905l0 0c0 -1.800354 1.3678284 -3.2598267 3.0551147 -3.2598267l0 0c0.8102722 0 1.5873566 0.34344482 2.1602936 0.95477295c0.5729523 0.6113281 0.89482117 1.4404907 0.89482117 2.3050537l0 0c0 1.800354 -1.3678131 3.2598572 -3.0551147 3.2598572l0 0c-1.6872864 0 -3.0551147 -1.4595032 -3.0551147 -3.2598572z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m158.66861 267.70898c44.299225 0 88.598434 60.787415 88.598434 121.57483" fill-rule="evenodd"/><path stroke="#b7b7b7" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="4.0,3.0" d="m158.66861 267.70898c44.299225 0 88.598434 60.787415 88.598434 121.57483" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m250.33333 392.55905c196.29921 0 392.59845 -48.22046 392.59845 -96.44095" fill-rule="evenodd"/><path stroke="#b7b7b7" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="4.0,3.0" d="m250.33333 392.55905c196.29921 0 392.59845 -48.22046 392.59845 -96.44095" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m250.33333 392.55905c195.2126 0 390.42523 -100.39371 390.42523 -200.7874" fill-rule="evenodd"/><path stroke="#b7b7b7" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="4.0,3.0" d="m250.33333 392.55905c195.2126 0 390.42523 -100.39371 390.42523 -200.7874" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m250.33333 392.55905c196.29921 0 392.59845 -153.13385 392.59845 -306.2677" fill-rule="evenodd"/><path stroke="#b7b7b7" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="4.0,3.0" d="m250.33333 392.55905c196.29921 0 392.59845 -153.13385 392.59845 -306.2677" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m844.39624 4.631458l111.590515 0l0 33.51181l-111.590515 0z" fill-rule="evenodd"/><path fill="#000000" d="m855.1306 31.551456l0 -13.359375l1.765625 0l0 13.359375l-1.765625 0zm4.6832886 0l0 -9.671875l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953125l-1.640625 0l0 -5.890625q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.28125l-1.640625 0zm13.953857 -1.46875l0.234375 1.453125q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm8.230164 -1.640625l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.296875q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm8.828857 6.5625l1.59375 0.234375q0.109375 0.75 0.5625 1.078125q0.609375 0.453125 1.671875 0.453125q1.140625 0 1.75 -0.453125q0.625 -0.453125 0.84375 -1.265625q0.125 -0.5 0.109375 -2.109375q-1.0625 1.265625 -2.671875 1.265625q-2.0 0 -3.09375 -1.4375q-1.09375 -1.4375 -1.09375 -3.453125q0 -1.390625 0.5 -2.5625q0.515625 -1.171875 1.453125 -1.796875q0.953125 -0.640625 2.25 -0.640625q1.703125 0 2.8125 1.375l0 -1.15625l1.515625 0l0 8.359375q0 2.265625 -0.46875 3.203125q-0.453125 0.9375 -1.453125 1.484375q-0.984375 0.546875 -2.453125 0.546875q-1.71875 0 -2.796875 -0.78125q-1.0625 -0.765625 -1.03125 -2.34375zm1.359375 -5.8125q0 1.90625 0.75 2.78125q0.765625 0.875 1.90625 0.875q1.125 0 1.890625 -0.859375q0.765625 -0.875 0.765625 -2.734375q0 -1.78125 -0.796875 -2.671875q-0.78125 -0.90625 -1.890625 -0.90625q-1.09375 0 -1.859375 0.890625q-0.765625 0.875 -0.765625 2.625zm9.313232 5.015625l0 -9.671875l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.0625l-1.625 0zm12.5407715 -1.1875q-0.921875 0.765625 -1.765625 1.09375q-0.828125 0.3125 -1.796875 0.3125q-1.59375 0 -2.453125 -0.78125q-0.859375 -0.78125 -0.859375 -1.984375q0 -0.71875 0.328125 -1.296875q0.328125 -0.59375 0.84375 -0.9375q0.53125 -0.359375 1.1875 -0.546875q0.46875 -0.125 1.453125 -0.25q1.984375 -0.234375 2.921875 -0.5625q0.015625 -0.34375 0.015625 -0.421875q0 -1.0 -0.46875 -1.421875q-0.625 -0.546875 -1.875 -0.546875q-1.15625 0 -1.703125 0.40625q-0.546875 0.40625 -0.8125 1.421875l-1.609375 -0.21875q0.21875 -1.015625 0.71875 -1.640625q0.5 -0.640625 1.453125 -0.984375q0.953125 -0.34375 2.1875 -0.34375q1.25 0 2.015625 0.296875q0.78125 0.28125 1.140625 0.734375q0.375 0.4375 0.515625 1.109375q0.078125 0.421875 0.078125 1.515625l0 2.1875q0 2.28125 0.109375 2.890625q0.109375 0.59375 0.40625 1.15625l-1.703125 0q-0.265625 -0.515625 -0.328125 -1.1875zm-0.140625 -3.671875q-0.890625 0.375 -2.671875 0.625q-1.015625 0.140625 -1.4375 0.328125q-0.421875 0.1875 -0.65625 0.53125q-0.21875 0.34375 -0.21875 0.78125q0 0.65625 0.5 1.09375q0.5 0.4375 1.453125 0.4375q0.9375 0 1.671875 -0.40625q0.75 -0.421875 1.09375 -1.140625q0.265625 -0.5625 0.265625 -1.640625l0 -0.609375zm7.7819824 3.390625l0.234375 1.453125q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm1.6051636 -10.0l0 -1.890625l1.640625 0l0 1.890625l-1.640625 0zm0 11.46875l0 -9.671875l1.640625 0l0 9.671875l-1.640625 0zm3.5354614 -4.84375q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.59375 -2.328125 0.59375q-2.03125 0 -3.28125 -1.296875q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm9.297607 4.84375l0 -9.671875l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953125l-1.640625 0l0 -5.890625q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.28125l-1.640625 0z" fill-rule="nonzero"/></g></svg> \ No newline at end of file diff --git a/docs/user/alerting/images/alert-flyout-action-details.png b/docs/user/alerting/images/alert-flyout-action-details.png new file mode 100644 index 0000000000000..06287ed1837d2 Binary files /dev/null and b/docs/user/alerting/images/alert-flyout-action-details.png differ diff --git a/docs/user/alerting/images/alert-flyout-action-type-selection.png b/docs/user/alerting/images/alert-flyout-action-type-selection.png new file mode 100644 index 0000000000000..e4448ca5f3fcd Binary files /dev/null and b/docs/user/alerting/images/alert-flyout-action-type-selection.png differ diff --git a/docs/user/alerting/images/alert-flyout-action-variables.png b/docs/user/alerting/images/alert-flyout-action-variables.png new file mode 100644 index 0000000000000..73fc1fe9dea63 Binary files /dev/null and b/docs/user/alerting/images/alert-flyout-action-variables.png differ diff --git a/docs/user/alerting/images/alert-flyout-add-action.png b/docs/user/alerting/images/alert-flyout-add-action.png new file mode 100644 index 0000000000000..159dbd5c1e449 Binary files /dev/null and b/docs/user/alerting/images/alert-flyout-add-action.png differ diff --git a/docs/user/alerting/images/alert-flyout-alert-conditions.png b/docs/user/alerting/images/alert-flyout-alert-conditions.png new file mode 100644 index 0000000000000..f3e8f42ff0f37 Binary files /dev/null and b/docs/user/alerting/images/alert-flyout-alert-conditions.png differ diff --git a/docs/user/alerting/images/alert-flyout-alert-type-selection.png b/docs/user/alerting/images/alert-flyout-alert-type-selection.png new file mode 100644 index 0000000000000..a0a25dc5f1bbc Binary files /dev/null and b/docs/user/alerting/images/alert-flyout-alert-type-selection.png differ diff --git a/docs/user/alerting/images/alert-flyout-general-details.png b/docs/user/alerting/images/alert-flyout-general-details.png new file mode 100644 index 0000000000000..db56c16c1c308 Binary files /dev/null and b/docs/user/alerting/images/alert-flyout-general-details.png differ diff --git a/docs/user/alerting/images/alert-flyout-sections.png b/docs/user/alerting/images/alert-flyout-sections.png new file mode 100644 index 0000000000000..8fa4bfffe39a3 Binary files /dev/null and b/docs/user/alerting/images/alert-flyout-sections.png differ diff --git a/docs/user/alerting/images/alert-instances.svg b/docs/user/alerting/images/alert-instances.svg new file mode 100644 index 0000000000000..b7b0bd4996053 --- /dev/null +++ b/docs/user/alerting/images/alert-instances.svg @@ -0,0 +1 @@ +<svg version="1.1" viewBox="0.0 0.0 722.8110236220473 331.005249343832" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l722.81104 0l0 331.00525l-722.81104 0l0 -331.00525z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l722.81104 0l0 331.00525l-722.81104 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m13.120735 52.889763l268.7874 0l0 266.61417l-268.7874 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m13.120735 52.889763l268.7874 0l0 266.61417l-268.7874 0z" fill-rule="evenodd"/><path fill="#000000" d="m22.089485 79.80976l5.125 -13.359375l1.90625 0l5.46875 13.359375l-2.015625 0l-1.546875 -4.046875l-5.59375 0l-1.46875 4.046875l-1.875 0zm3.859375 -5.484375l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm9.802948 5.484375l0 -13.359375l1.640625 0l0 13.359375l-1.640625 0zm10.816696 -3.109375l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.296875q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm9.125717 5.765625l0 -9.671875l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.0625l-1.625 0zm9.806427 -1.46875l0.234375 1.453125q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 97.062996l233.00789 0l0 33.511803l-233.00789 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 97.062996l233.00789 0l0 33.511803l-233.00789 0z" fill-rule="evenodd"/><path fill="#000000" d="m43.750084 117.40737l1.515625 0.375q-0.46875 1.875 -1.71875 2.859375q-1.234375 0.984375 -3.015625 0.984375q-1.859375 0 -3.015625 -0.75q-1.15625 -0.765625 -1.765625 -2.1875q-0.609375 -1.4375 -0.609375 -3.078125q0 -1.796875 0.6875 -3.125q0.6875 -1.328125 1.9375 -2.015625q1.265625 -0.703125 2.78125 -0.703125q1.71875 0 2.890625 0.875q1.171875 0.875 1.640625 2.46875l-1.5 0.34375q-0.390625 -1.25 -1.15625 -1.8125q-0.75 -0.578125 -1.90625 -0.578125q-1.3125 0 -2.203125 0.640625q-0.890625 0.625 -1.25 1.703125q-0.359375 1.0625 -0.359375 2.1875q0 1.46875 0.421875 2.5625q0.4375 1.078125 1.328125 1.625q0.90625 0.53125 1.953125 0.53125q1.265625 0 2.140625 -0.734375q0.890625 -0.734375 1.203125 -2.171875zm2.6796875 -0.140625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0zm14.2734375 0l0 -1.046875q-0.78125 1.234375 -2.3125 1.234375q-1.0 0 -1.828125 -0.546875q-0.828125 -0.546875 -1.296875 -1.53125q-0.453125 -0.984375 -0.453125 -2.25q0 -1.25 0.40625 -2.25q0.421875 -1.015625 1.25 -1.546875q0.828125 -0.546875 1.859375 -0.546875q0.75 0 1.328125 0.3125q0.59375 0.3125 0.953125 0.828125l0 -4.109375l1.40625 0l0 11.453125l-1.3125 0zm-4.4375 -4.140625q0 1.59375 0.671875 2.390625q0.671875 0.78125 1.578125 0.78125q0.921875 0 1.5625 -0.75q0.65625 -0.765625 0.65625 -2.3125q0 -1.703125 -0.65625 -2.5q-0.65625 -0.796875 -1.625 -0.796875q-0.9375 0 -1.5625 0.765625q-0.625 0.765625 -0.625 2.421875zm7.9609375 -5.703125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm6.6171875 -1.265625l0.203125 1.25q-0.59375 0.125 -1.0625 0.125q-0.765625 0 -1.1875 -0.234375q-0.421875 -0.25 -0.59375 -0.640625q-0.171875 -0.40625 -0.171875 -1.671875l0 -4.765625l-1.03125 0l0 -1.09375l1.03125 0l0 -2.0625l1.40625 -0.84375l0 2.90625l1.40625 0l0 1.09375l-1.40625 0l0 4.84375q0 0.609375 0.0625 0.78125q0.078125 0.171875 0.25 0.28125q0.171875 0.09375 0.484375 0.09375q0.234375 0 0.609375 -0.0625zm1.3828125 -8.578125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm3.0234375 -4.15625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0z" fill-rule="nonzero"/><path fill="#000000" d="m136.46883 120.39175q-0.734375 0.625 -1.421875 0.921875q-0.671875 0.296875 -1.4375 0.296875q-1.15625 0 -1.859375 -0.671875q-0.703125 -0.6875 -0.703125 -1.734375q0 -0.703125 0.3125 -1.234375q0.328125 -0.53125 0.84375 -0.859375q0.53125 -0.328125 1.28125 -0.46875q0.484375 -0.09375 1.8125 -0.140625q1.328125 -0.0625 1.90625 -0.28125q0.171875 -0.578125 0.171875 -0.96875q0 -0.484375 -0.359375 -0.765625q-0.5 -0.390625 -1.4375 -0.390625q-0.890625 0 -1.46875 0.390625q-0.5625 0.390625 -0.8125 1.125l-1.4375 -0.125q0.4375 -1.234375 1.390625 -1.890625q0.953125 -0.65625 2.390625 -0.65625q1.546875 0 2.4375 0.734375q0.6875 0.546875 0.6875 1.421875q0 0.65625 -0.1875 1.53125l-0.46875 2.0625q-0.21875 0.984375 -0.21875 1.609375q0 0.390625 0.171875 1.125l-1.421875 0q-0.125 -0.40625 -0.171875 -1.03125zm0.53125 -3.171875q-0.296875 0.109375 -0.640625 0.171875q-0.34375 0.0625 -1.140625 0.140625q-1.234375 0.109375 -1.75 0.28125q-0.5 0.15625 -0.765625 0.53125q-0.25 0.359375 -0.25 0.8125q0 0.59375 0.40625 0.984375q0.421875 0.375 1.171875 0.375q0.703125 0 1.34375 -0.375q0.65625 -0.375 1.03125 -1.03125q0.375 -0.671875 0.59375 -1.890625zm4.8671875 4.203125l-1.359375 -8.296875l1.375 0l0.71875 4.5625q0.109375 0.75 0.28125 2.453125q0.40625 -0.890625 1.046875 -2.03125l2.765625 -4.984375l1.5 0l-4.75 8.296875l-1.578125 0zm5.828125 0.75l1.4375 0.140625q-0.015625 0.484375 0.109375 0.71875q0.140625 0.25 0.421875 0.375q0.390625 0.171875 1.0 0.171875q1.28125 0 1.84375 -0.65625q0.359375 -0.453125 0.671875 -1.953125l0.140625 -0.671875q-1.09375 1.125 -2.34375 1.125q-1.25 0 -2.109375 -0.921875q-0.84375 -0.9375 -0.84375 -2.625q0 -1.40625 0.671875 -2.578125q0.671875 -1.171875 1.59375 -1.765625q0.921875 -0.59375 1.890625 -0.59375q1.640625 0 2.515625 1.546875l0.28125 -1.359375l1.296875 0l-1.671875 8.0q-0.265625 1.328125 -0.71875 2.0625q-0.4375 0.75 -1.234375 1.15625q-0.796875 0.40625 -1.828125 0.40625q-1.0 0 -1.734375 -0.265625q-0.71875 -0.25 -1.078125 -0.75q-0.34375 -0.5 -0.34375 -1.140625q0 -0.1875 0.03125 -0.421875zm1.765625 -4.421875q0 0.84375 0.171875 1.28125q0.234375 0.59375 0.65625 0.921875q0.4375 0.3125 0.96875 0.3125q0.703125 0 1.390625 -0.484375q0.6875 -0.5 1.109375 -1.515625q0.421875 -1.03125 0.421875 -1.953125q0 -1.03125 -0.5625 -1.625q-0.5625 -0.609375 -1.40625 -0.609375q-0.515625 0 -1.0 0.28125q-0.484375 0.265625 -0.90625 0.84375q-0.40625 0.5625 -0.625 1.359375q-0.21875 0.78125 -0.21875 1.1875zm20.78125 -0.359375l1.515625 0.203125q-0.71875 1.984375 -2.09375 3.015625q-1.375 1.015625 -3.09375 1.015625q-2.078125 0 -3.3125 -1.28125q-1.21875 -1.28125 -1.21875 -3.65625q0 -3.09375 1.859375 -5.109375q1.65625 -1.8125 4.125 -1.8125q1.8125 0 2.9375 0.984375q1.140625 0.96875 1.3125 2.625l-1.421875 0.125q-0.234375 -1.234375 -0.9375 -1.84375q-0.703125 -0.625 -1.828125 -0.625q-2.09375 0 -3.390625 1.875q-1.125 1.609375 -1.125 3.8125q0 1.765625 0.859375 2.703125q0.875 0.921875 2.265625 0.921875q1.1875 0 2.140625 -0.765625q0.96875 -0.78125 1.40625 -2.1875zm2.5859375 4.03125l2.40625 -11.453125l4.78125 0q1.234375 0 1.859375 0.28125q0.625 0.28125 1.03125 0.984375q0.40625 0.6875 0.40625 1.546875q0 0.703125 -0.296875 1.4375q-0.28125 0.734375 -0.734375 1.21875q-0.4375 0.46875 -0.890625 0.71875q-0.453125 0.234375 -0.96875 0.359375q-1.09375 0.25 -2.21875 0.25l-2.875 0l-0.96875 4.65625l-1.53125 0zm2.78125 -5.953125l2.515625 0q1.46875 0 2.15625 -0.3125q0.6875 -0.3125 1.09375 -0.953125q0.421875 -0.65625 0.421875 -1.390625q0 -0.5625 -0.21875 -0.90625q-0.21875 -0.359375 -0.625 -0.53125q-0.390625 -0.171875 -1.515625 -0.171875l-2.9375 0l-0.890625 4.265625zm10.375 -5.5l1.53125 0l-1.46875 7.03125q-0.171875 0.875 -0.171875 1.296875q0 0.921875 0.71875 1.484375q0.734375 0.5625 1.828125 0.5625q0.875 0 1.625 -0.390625q0.765625 -0.40625 1.203125 -1.1875q0.4375 -0.78125 0.78125 -2.5l1.328125 -6.296875l1.53125 0l-1.40625 6.71875q-0.359375 1.71875 -0.953125 2.71875q-0.578125 1.0 -1.625 1.609375q-1.03125 0.609375 -2.40625 0.609375q-1.296875 0 -2.25 -0.421875q-0.9375 -0.4375 -1.421875 -1.1875q-0.46875 -0.765625 -0.46875 -1.734375q0 -0.59375 0.328125 -2.0625l1.296875 -6.25zm21.859375 6.4375l-7.59375 3.25l0 -1.40625l6.015625 -2.484375l-6.015625 -2.46875l0 -1.40625l7.59375 3.203125l0 1.3125zm5.8984375 1.25q0 -1.453125 0.421875 -2.953125q0.4375 -1.5 0.96875 -2.390625q0.53125 -0.90625 1.09375 -1.40625q0.578125 -0.515625 1.125 -0.75q0.5625 -0.234375 1.234375 -0.234375q1.3125 0 2.1875 0.984375q0.875 0.96875 0.875 2.8125q0 1.890625 -0.609375 3.75q-0.71875 2.203125 -2.0 3.3125q-0.984375 0.84375 -2.234375 0.84375q-1.296875 0 -2.1875 -1.015625q-0.875 -1.015625 -0.875 -2.953125zm1.359375 0.359375q0 1.0625 0.359375 1.65625q0.46875 0.8125 1.40625 0.8125q0.828125 0 1.5 -0.734375q0.96875 -1.046875 1.453125 -3.0625q0.484375 -2.03125 0.484375 -3.3125q0 -1.234375 -0.46875 -1.78125q-0.46875 -0.546875 -1.25 -0.546875q-0.5625 0 -1.046875 0.296875q-0.484375 0.28125 -0.9375 0.984375q-0.625 1.0 -1.09375 2.96875q-0.40625 1.6875 -0.40625 2.71875zm7.3203125 3.40625l0.34375 -1.609375l1.59375 0l-0.34375 1.609375l-1.59375 0zm4.6015625 -2.625l1.328125 -0.140625q0.140625 0.96875 0.546875 1.375q0.421875 0.40625 0.984375 0.40625q0.8125 0 1.515625 -0.703125q1.015625 -1.03125 1.5 -2.96875q-0.703125 0.53125 -1.265625 0.765625q-0.546875 0.21875 -1.125 0.21875q-1.046875 0 -1.875 -0.703125q-1.09375 -0.90625 -1.09375 -2.625q0 -1.9375 1.265625 -3.3125q1.09375 -1.1875 2.671875 -1.1875q1.421875 0 2.359375 1.046875q0.9375 1.046875 0.9375 2.96875q0 1.875 -0.625 3.6875q-0.75 2.140625 -2.015625 3.15625q-1.03125 0.84375 -2.3125 0.84375q-1.1875 0 -1.953125 -0.734375q-0.75 -0.75 -0.84375 -2.09375zm1.890625 -4.53125q0 1.078125 0.5625 1.71875q0.578125 0.640625 1.375 0.640625q0.578125 0 1.15625 -0.390625q0.578125 -0.40625 0.984375 -1.203125q0.421875 -0.796875 0.421875 -1.625q0 -0.65625 -0.28125 -1.21875q-0.265625 -0.5625 -0.734375 -0.84375q-0.46875 -0.296875 -0.953125 -0.296875q-0.46875 0 -0.921875 0.25q-0.453125 0.25 -0.84375 0.734375q-0.390625 0.484375 -0.578125 1.125q-0.1875 0.625 -0.1875 1.109375z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 144.07611l233.00789 0l0 39.3071l-233.00789 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 144.07611l233.00789 0l0 39.3071l-233.00789 0z" fill-rule="evenodd"/><path fill="#000000" d="m35.062584 164.74861l1.4375 -0.125q0.09375 0.859375 0.46875 1.421875q0.375 0.546875 1.15625 0.890625q0.78125 0.328125 1.75 0.328125q0.875 0 1.53125 -0.25q0.671875 -0.265625 0.984375 -0.703125q0.328125 -0.453125 0.328125 -0.984375q0 -0.546875 -0.3125 -0.9375q-0.3125 -0.40625 -1.03125 -0.6875q-0.453125 -0.171875 -2.03125 -0.546875q-1.578125 -0.390625 -2.21875 -0.71875q-0.8125 -0.4375 -1.21875 -1.0625q-0.40625 -0.640625 -0.40625 -1.4375q0 -0.859375 0.484375 -1.609375q0.5 -0.765625 1.4375 -1.15625q0.953125 -0.390625 2.109375 -0.390625q1.28125 0 2.25 0.421875q0.96875 0.40625 1.484375 1.203125q0.53125 0.796875 0.578125 1.796875l-1.453125 0.109375q-0.125 -1.078125 -0.796875 -1.625q-0.671875 -0.5625 -2.0 -0.5625q-1.375 0 -2.0 0.5q-0.625 0.5 -0.625 1.21875q0 0.609375 0.4375 1.015625q0.4375 0.390625 2.28125 0.8125q1.859375 0.421875 2.546875 0.734375q1.0 0.453125 1.46875 1.171875q0.484375 0.703125 0.484375 1.625q0 0.90625 -0.53125 1.71875q-0.515625 0.8125 -1.5 1.265625q-0.984375 0.453125 -2.203125 0.453125q-1.5625 0 -2.609375 -0.453125q-1.046875 -0.46875 -1.65625 -1.375q-0.59375 -0.90625 -0.625 -2.0625zm16.421875 0.640625l1.390625 0.1875q-0.234375 1.421875 -1.171875 2.234375q-0.921875 0.8125 -2.28125 0.8125q-1.703125 0 -2.75 -1.109375q-1.03125 -1.125 -1.03125 -3.203125q0 -1.34375 0.4375 -2.34375q0.453125 -1.015625 1.359375 -1.515625q0.921875 -0.5 1.984375 -0.5q1.359375 0 2.21875 0.6875q0.859375 0.671875 1.09375 1.9375l-1.359375 0.203125q-0.203125 -0.828125 -0.703125 -1.25q-0.484375 -0.421875 -1.1875 -0.421875q-1.0625 0 -1.734375 0.765625q-0.65625 0.75 -0.65625 2.40625q0 1.671875 0.640625 2.4375q0.640625 0.75 1.671875 0.75q0.828125 0 1.375 -0.5q0.5625 -0.515625 0.703125 -1.578125zm2.59375 3.046875l0 -11.453125l1.40625 0l0 4.109375q0.984375 -1.140625 2.484375 -1.140625q0.921875 0 1.59375 0.359375q0.6875 0.359375 0.96875 1.0q0.296875 0.640625 0.296875 1.859375l0 5.265625l-1.40625 0l0 -5.265625q0 -1.046875 -0.453125 -1.53125q-0.453125 -0.484375 -1.296875 -0.484375q-0.625 0 -1.171875 0.328125q-0.546875 0.328125 -0.78125 0.890625q-0.234375 0.546875 -0.234375 1.515625l0 4.546875l-1.40625 0zm14.5703125 -2.671875l1.453125 0.171875q-0.34375 1.28125 -1.28125 1.984375q-0.921875 0.703125 -2.359375 0.703125q-1.828125 0 -2.890625 -1.125q-1.0625 -1.125 -1.0625 -3.140625q0 -2.09375 1.078125 -3.25q1.078125 -1.15625 2.796875 -1.15625q1.65625 0 2.703125 1.140625q1.0625 1.125 1.0625 3.171875q0 0.125 0 0.375l-6.1875 0q0.078125 1.375 0.765625 2.109375q0.703125 0.71875 1.734375 0.71875q0.78125 0 1.328125 -0.40625q0.546875 -0.40625 0.859375 -1.296875zm-4.609375 -2.28125l4.625 0q-0.09375 -1.046875 -0.53125 -1.5625q-0.671875 -0.8125 -1.734375 -0.8125q-0.96875 0 -1.640625 0.65625q-0.65625 0.640625 -0.71875 1.71875zm13.2109375 4.953125l0 -1.046875q-0.78125 1.234375 -2.3125 1.234375q-1.0 0 -1.828125 -0.546875q-0.828125 -0.546875 -1.296875 -1.53125q-0.453125 -0.984375 -0.453125 -2.25q0 -1.25 0.40625 -2.25q0.421875 -1.015625 1.25 -1.546875q0.828125 -0.546875 1.859375 -0.546875q0.75 0 1.328125 0.3125q0.59375 0.3125 0.953125 0.828125l0 -4.109375l1.40625 0l0 11.453125l-1.3125 0zm-4.4375 -4.140625q0 1.59375 0.671875 2.390625q0.671875 0.78125 1.578125 0.78125q0.921875 0 1.5625 -0.75q0.65625 -0.765625 0.65625 -2.3125q0 -1.703125 -0.65625 -2.5q-0.65625 -0.796875 -1.625 -0.796875q-0.9375 0 -1.5625 0.765625q-0.625 0.765625 -0.625 2.421875zm13.3984375 4.140625l0 -1.21875q-0.96875 1.40625 -2.640625 1.40625q-0.734375 0 -1.375 -0.28125q-0.625 -0.28125 -0.9375 -0.703125q-0.3125 -0.4375 -0.4375 -1.046875q-0.078125 -0.421875 -0.078125 -1.3125l0 -5.140625l1.40625 0l0 4.59375q0 1.109375 0.078125 1.484375q0.140625 0.5625 0.5625 0.875q0.4375 0.3125 1.0625 0.3125q0.640625 0 1.1875 -0.3125q0.5625 -0.328125 0.78125 -0.890625q0.234375 -0.5625 0.234375 -1.625l0 -4.4375l1.40625 0l0 8.296875l-1.25 0zm3.4296875 0l0 -11.453125l1.40625 0l0 11.453125l-1.40625 0zm9.2578125 -2.671875l1.453125 0.171875q-0.34375 1.28125 -1.28125 1.984375q-0.921875 0.703125 -2.359375 0.703125q-1.828125 0 -2.890625 -1.125q-1.0625 -1.125 -1.0625 -3.140625q0 -2.09375 1.078125 -3.25q1.078125 -1.15625 2.796875 -1.15625q1.65625 0 2.703125 1.140625q1.0625 1.125 1.0625 3.171875q0 0.125 0 0.375l-6.1875 0q0.078125 1.375 0.765625 2.109375q0.703125 0.71875 1.734375 0.71875q0.78125 0 1.328125 -0.40625q0.546875 -0.40625 0.859375 -1.296875zm-4.609375 -2.28125l4.625 0q-0.09375 -1.046875 -0.53125 -1.5625q-0.671875 -0.8125 -1.734375 -0.8125q-0.96875 0 -1.640625 0.65625q-0.65625 0.640625 -0.71875 1.71875z" fill-rule="nonzero"/><path fill="#000000" d="m136.96883 165.60799l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm9.0078125 4.875l-1.359375 -8.296875l1.375 0l0.71875 4.5625q0.109375 0.75 0.28125 2.453125q0.40625 -0.890625 1.046875 -2.03125l2.765625 -4.984375l1.5 0l-4.75 8.296875l-1.578125 0zm12.0 -2.828125l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm6.9140625 4.875l1.734375 -8.296875l1.25 0l-0.34375 1.6875q0.640625 -0.953125 1.25 -1.40625q0.609375 -0.46875 1.25 -0.46875q0.421875 0 1.046875 0.296875l-0.578125 1.3125q-0.375 -0.265625 -0.8125 -0.265625q-0.734375 0 -1.515625 0.828125q-0.78125 0.828125 -1.234375 2.984375l-0.703125 3.328125l-1.34375 0zm4.796875 3.203125l0.09375 -1.328125q0.4375 0.125 0.84375 0.125q0.4375 0 0.703125 -0.1875q0.34375 -0.265625 0.75 -0.96875l0.453125 -0.8125l-1.390625 -8.328125l1.390625 0l0.625 4.1875q0.1875 1.25 0.328125 2.484375l3.703125 -6.671875l1.484375 0l-5.296875 9.40625q-0.765625 1.375 -1.359375 1.8125q-0.59375 0.453125 -1.359375 0.453125q-0.5 0 -0.96875 -0.171875zm12.9765625 -3.203125l1.734375 -8.296875l1.40625 0l-0.28125 1.359375q0.78125 -0.875 1.40625 -1.203125q0.625 -0.34375 1.34375 -0.34375q0.78125 0 1.296875 0.40625q0.515625 0.40625 0.6875 1.140625q0.625 -0.78125 1.328125 -1.15625q0.703125 -0.390625 1.46875 -0.390625q1.046875 0 1.5625 0.5q0.515625 0.484375 0.515625 1.375q0 0.375 -0.171875 1.265625l-1.125 5.34375l-1.40625 0l1.140625 -5.484375q0.140625 -0.671875 0.140625 -0.96875q0 -0.40625 -0.265625 -0.640625q-0.25 -0.234375 -0.71875 -0.234375q-0.625 0 -1.28125 0.390625q-0.65625 0.375 -1.03125 1.0q-0.359375 0.625 -0.625 1.90625l-0.84375 4.03125l-1.421875 0l1.171875 -5.609375q0.125 -0.5625 0.125 -0.8125q0 -0.40625 -0.25 -0.65625q-0.25 -0.25 -0.65625 -0.25q-0.609375 0 -1.265625 0.390625q-0.65625 0.375 -1.078125 1.0625q-0.40625 0.6875 -0.671875 1.953125l-0.828125 3.921875l-1.40625 0zm15.34375 -9.859375l0.328125 -1.59375l1.40625 0l-0.328125 1.59375l-1.40625 0zm-2.0625 9.859375l1.734375 -8.296875l1.40625 0l-1.734375 8.296875l-1.40625 0zm3.6015625 0l1.734375 -8.296875l1.28125 0l-0.296875 1.4375q0.828125 -0.84375 1.546875 -1.234375q0.734375 -0.390625 1.5 -0.390625q1.0 0 1.578125 0.546875q0.578125 0.546875 0.578125 1.453125q0 0.46875 -0.203125 1.453125l-1.0625 5.03125l-1.40625 0l1.09375 -5.265625q0.171875 -0.765625 0.171875 -1.125q0 -0.421875 -0.296875 -0.671875q-0.28125 -0.265625 -0.8125 -0.265625q-1.09375 0 -1.9375 0.78125q-0.84375 0.78125 -1.25 2.6875l-0.796875 3.859375l-1.421875 0zm14.5703125 -1.5q-1.5 1.6875 -3.0625 1.6875q-0.953125 0 -1.546875 -0.546875q-0.59375 -0.5625 -0.59375 -1.359375q0 -0.515625 0.265625 -1.796875l1.0 -4.78125l1.421875 0l-1.109375 5.296875q-0.140625 0.671875 -0.140625 1.03125q0 0.46875 0.28125 0.734375q0.28125 0.265625 0.84375 0.265625q0.59375 0 1.15625 -0.28125q0.5625 -0.296875 0.96875 -0.78125q0.421875 -0.5 0.671875 -1.171875q0.171875 -0.4375 0.40625 -1.515625l0.75 -3.578125l1.40625 0l-1.734375 8.296875l-1.296875 0l0.3125 -1.5zm6.5390625 0.34375l-0.234375 1.171875q-0.515625 0.125 -0.984375 0.125q-0.84375 0 -1.34375 -0.421875q-0.375 -0.3125 -0.375 -0.84375q0 -0.28125 0.203125 -1.265625l1.0 -4.8125l-1.109375 0l0.21875 -1.09375l1.125 0l0.421875 -2.046875l1.625 -0.96875l-0.640625 3.015625l1.390625 0l-0.234375 1.09375l-1.375 0l-0.96875 4.578125q-0.171875 0.875 -0.171875 1.046875q0 0.25 0.140625 0.390625q0.140625 0.125 0.46875 0.125q0.46875 0 0.84375 -0.09375zm7.2265625 -1.671875l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 192.92389l233.00789 0l0 112.12598l-233.00789 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 192.92389l233.00789 0l0 112.12598l-233.00789 0z" fill-rule="evenodd"/><path fill="#000000" d="m34.312584 217.28389l4.40625 -11.453125l1.640625 0l4.6875 11.453125l-1.734375 0l-1.328125 -3.46875l-4.796875 0l-1.25 3.46875l-1.625 0zm3.3125 -4.703125l3.890625 0l-1.203125 -3.171875q-0.546875 -1.453125 -0.8125 -2.375q-0.21875 1.09375 -0.609375 2.1875l-1.265625 3.359375zm13.859375 1.65625l1.390625 0.1875q-0.234375 1.421875 -1.171875 2.234375q-0.921875 0.8125 -2.28125 0.8125q-1.703125 0 -2.75 -1.109375q-1.03125 -1.125 -1.03125 -3.203125q0 -1.34375 0.4375 -2.34375q0.453125 -1.015625 1.359375 -1.515625q0.921875 -0.5 1.984375 -0.5q1.359375 0 2.21875 0.6875q0.859375 0.671875 1.09375 1.9375l-1.359375 0.203125q-0.203125 -0.828125 -0.703125 -1.25q-0.484375 -0.421875 -1.1875 -0.421875q-1.0625 0 -1.734375 0.765625q-0.65625 0.75 -0.65625 2.40625q0 1.671875 0.640625 2.4375q0.640625 0.75 1.671875 0.75q0.828125 0 1.375 -0.5q0.5625 -0.515625 0.703125 -1.578125zm5.65625 1.78125l0.203125 1.25q-0.59375 0.125 -1.0625 0.125q-0.765625 0 -1.1875 -0.234375q-0.421875 -0.25 -0.59375 -0.640625q-0.171875 -0.40625 -0.171875 -1.671875l0 -4.765625l-1.03125 0l0 -1.09375l1.03125 0l0 -2.0625l1.40625 -0.84375l0 2.90625l1.40625 0l0 1.09375l-1.40625 0l0 4.84375q0 0.609375 0.0625 0.78125q0.078125 0.171875 0.25 0.28125q0.171875 0.09375 0.484375 0.09375q0.234375 0 0.609375 -0.0625zm1.3828125 -8.578125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm3.0234375 -4.15625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0zm8.3359375 -2.484375l1.390625 -0.21875q0.109375 0.84375 0.640625 1.296875q0.546875 0.4375 1.5 0.4375q0.96875 0 1.4375 -0.390625q0.46875 -0.40625 0.46875 -0.9375q0 -0.46875 -0.40625 -0.75q-0.296875 -0.1875 -1.4375 -0.46875q-1.546875 -0.390625 -2.15625 -0.671875q-0.59375 -0.296875 -0.90625 -0.796875q-0.296875 -0.5 -0.296875 -1.109375q0 -0.5625 0.25 -1.03125q0.25 -0.46875 0.6875 -0.78125q0.328125 -0.25 0.890625 -0.40625q0.578125 -0.171875 1.21875 -0.171875q0.984375 0 1.71875 0.28125q0.734375 0.28125 1.078125 0.765625q0.359375 0.46875 0.5 1.28125l-1.375 0.1875q-0.09375 -0.640625 -0.546875 -1.0q-0.453125 -0.359375 -1.265625 -0.359375q-0.96875 0 -1.390625 0.328125q-0.40625 0.3125 -0.40625 0.734375q0 0.28125 0.171875 0.5q0.171875 0.21875 0.53125 0.375q0.21875 0.078125 1.25 0.359375q1.484375 0.390625 2.078125 0.65625q0.59375 0.25 0.921875 0.734375q0.34375 0.484375 0.34375 1.203125q0 0.703125 -0.421875 1.328125q-0.40625 0.609375 -1.1875 0.953125q-0.765625 0.34375 -1.734375 0.34375q-1.625 0 -2.46875 -0.671875q-0.84375 -0.671875 -1.078125 -2.0z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m45.49346 236.11156l195.77954 0l0 53.48033l-195.77954 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m45.49346 236.11156l195.77954 0l0 53.48033l-195.77954 0z" fill-rule="evenodd"/><path fill="#000000" d="m55.446587 256.63156l0 -8.59375l6.203125 0l0 1.015625l-5.0625 0l0 2.625l4.75 0l0 1.015625l-4.75 0l0 2.921875l5.265625 0l0 1.015625l-6.40625 0zm7.8476562 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm14.058594 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.7050781 -4.25l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6347656 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0zm10.09375 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm6.767578 0.84375l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm1.0371094 -6.4375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.2753906 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0z" fill-rule="nonzero"/><path fill="#000000" d="m59.08721 263.63156l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0z" fill-rule="nonzero"/><path fill="#010000" fill-opacity="0.0" d="m103.84112 259.76828l58.04882 0l0 13.798828l-58.04882 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m104.34112 268.2253l0.3125 -1.40625q0.421875 -0.015625 0.671875 -0.125q0.25 -0.109375 0.453125 -0.375q0.21875 -0.265625 0.34375 -0.609375q0.140625 -0.34375 0.296875 -1.09375q0.21875 -1.0625 0.4375 -1.515625q0.234375 -0.453125 0.546875 -0.703125q0.328125 -0.25 0.890625 -0.40625q0.359375 -0.09375 1.125 -0.09375l0.3125 0l-0.3125 1.390625q-0.65625 0 -0.875 0.09375q-0.21875 0.078125 -0.328125 0.265625q-0.078125 0.109375 -0.265625 0.859375q-0.328125 1.296875 -0.4375 1.578125q-0.203125 0.515625 -0.5 0.828125q-0.28125 0.3125 -0.796875 0.609375q0.34375 0.234375 0.5 0.5625q0.171875 0.3125 0.171875 0.84375q0 0.203125 -0.078125 0.625q-0.1875 1.15625 -0.25 1.359375q-0.046875 0.21875 -0.046875 0.359375q0 0.15625 0.078125 0.265625q0.09375 0.109375 0.25 0.15625q0.171875 0.0625 0.75 0.0625l-0.3125 1.40625l-0.3125 0q-0.796875 0 -1.203125 -0.140625q-0.390625 -0.125 -0.609375 -0.4375q-0.21875 -0.296875 -0.21875 -0.78125q0 -0.390625 0.203125 -1.328125q0.203125 -0.9375 0.203125 -1.25q0 -0.5 -0.21875 -0.734375q-0.21875 -0.234375 -0.78125 -0.265625zm4.669922 0l0.3125 -1.40625q0.421875 -0.015625 0.671875 -0.125q0.25 -0.109375 0.453125 -0.375q0.21875 -0.265625 0.34375 -0.609375q0.140625 -0.34375 0.296875 -1.09375q0.21875 -1.0625 0.4375 -1.515625q0.234375 -0.453125 0.546875 -0.703125q0.328125 -0.25 0.890625 -0.40625q0.359375 -0.09375 1.125 -0.09375l0.3125 0l-0.3125 1.390625q-0.65625 0 -0.875 0.09375q-0.21875 0.078125 -0.328125 0.265625q-0.078125 0.109375 -0.265625 0.859375q-0.328125 1.296875 -0.4375 1.578125q-0.203125 0.515625 -0.5 0.828125q-0.28125 0.3125 -0.796875 0.609375q0.34375 0.234375 0.5 0.5625q0.171875 0.3125 0.171875 0.84375q0 0.203125 -0.078125 0.625q-0.1875 1.15625 -0.25 1.359375q-0.046875 0.21875 -0.046875 0.359375q0 0.15625 0.078125 0.265625q0.09375 0.109375 0.25 0.15625q0.171875 0.0625 0.75 0.0625l-0.3125 1.40625l-0.3125 0q-0.796875 0 -1.203125 -0.140625q-0.390625 -0.125 -0.609375 -0.4375q-0.21875 -0.296875 -0.21875 -0.78125q0 -0.390625 0.203125 -1.328125q0.203125 -0.9375 0.203125 -1.25q0 -0.5 -0.21875 -0.734375q-0.21875 -0.234375 -0.78125 -0.265625zm4.435547 0.671875l1.625 -0.265625q0.21875 0.515625 0.53125 0.734375q0.328125 0.203125 0.890625 0.203125q0.578125 0 0.921875 -0.25q0.234375 -0.171875 0.234375 -0.421875q0 -0.171875 -0.125 -0.3125q-0.125 -0.125 -0.6875 -0.3125q-1.53125 -0.5 -1.890625 -0.796875q-0.5625 -0.453125 -0.5625 -1.203125q0 -0.734375 0.546875 -1.265625q0.765625 -0.75 2.28125 -0.75q1.203125 0 1.8125 0.453125q0.609375 0.4375 0.78125 1.1875l-1.5625 0.265625q-0.109375 -0.34375 -0.390625 -0.515625q-0.375 -0.21875 -0.90625 -0.21875q-0.53125 0 -0.765625 0.171875q-0.21875 0.171875 -0.21875 0.40625q0 0.234375 0.234375 0.375q0.140625 0.09375 0.9375 0.328125q1.234375 0.359375 1.65625 0.703125q0.578125 0.5 0.578125 1.171875q0 0.890625 -0.75 1.546875q-0.734375 0.640625 -2.09375 0.640625q-1.34375 0 -2.078125 -0.484375q-0.734375 -0.5 -1.0 -1.390625zm12.955078 -0.859375l-4.21875 0q-0.015625 0.109375 -0.015625 0.15625q0 0.625 0.359375 1.0q0.359375 0.375 0.859375 0.375q0.84375 0 1.3125 -0.859375l1.5 0.25q-0.4375 0.90625 -1.171875 1.359375q-0.734375 0.453125 -1.65625 0.453125q-1.25 0 -2.03125 -0.78125q-0.78125 -0.796875 -0.78125 -2.109375q0 -1.28125 0.703125 -2.265625q0.96875 -1.359375 2.765625 -1.359375q1.140625 0 1.8125 0.71875q0.671875 0.703125 0.671875 1.96875q0 0.609375 -0.109375 1.09375zm-1.421875 -1.015625q0 -0.125 0 -0.171875q0 -0.703125 -0.3125 -1.046875q-0.3125 -0.34375 -0.828125 -0.34375q-0.515625 0 -0.9375 0.390625q-0.40625 0.390625 -0.5625 1.171875l2.640625 0zm3.2363205 -2.609375l1.578125 0l-0.265625 1.203125q0.890625 -1.359375 1.90625 -1.359375q0.375 0 0.78125 0.1875l-0.640625 1.375q-0.234375 -0.078125 -0.484375 -0.078125q-0.4375 0 -0.890625 0.328125q-0.4375 0.328125 -0.703125 0.875q-0.25 0.546875 -0.484375 1.734375l-0.421875 1.953125l-1.6718674 0l1.2968674 -6.21875zm6.576172 6.21875l-1.453125 0l-1.25 -6.21875l1.65625 0l0.4375 2.703125q0.1875 1.109375 0.21875 1.46875q0.046875 -0.09375 0.40625 -0.75q0.359375 -0.671875 0.453125 -0.796875l1.546875 -2.625l1.8125 0l-3.828125 6.21875zm9.626953 -2.59375l-4.21875 0q-0.015625 0.109375 -0.015625 0.15625q0 0.625 0.359375 1.0q0.359375 0.375 0.859375 0.375q0.84375 0 1.3125 -0.859375l1.5 0.25q-0.4375 0.90625 -1.171875 1.359375q-0.734375 0.453125 -1.65625 0.453125q-1.25 0 -2.03125 -0.78125q-0.78125 -0.796875 -0.78125 -2.109375q0 -1.28125 0.703125 -2.265625q0.96875 -1.359375 2.765625 -1.359375q1.140625 0 1.8125 0.71875q0.671875 0.703125 0.671875 1.96875q0 0.609375 -0.109375 1.09375zm-1.421875 -1.015625q0 -0.125 0 -0.171875q0 -0.703125 -0.3125 -1.046875q-0.3125 -0.34375 -0.828125 -0.34375q-0.515625 0 -0.9375 0.390625q-0.40625 0.390625 -0.5625 1.171875l2.640625 0zm3.2363281 -2.609375l1.578125 0l-0.265625 1.203125q0.890625 -1.359375 1.90625 -1.359375q0.375 0 0.78125 0.1875l-0.640625 1.375q-0.234375 -0.078125 -0.484375 -0.078125q-0.4375 0 -0.890625 0.328125q-0.4375 0.328125 -0.703125 0.875q-0.25 0.546875 -0.484375 1.734375l-0.421875 1.953125l-1.671875 0l1.296875 -6.21875zm7.044922 3.8125q-0.53125 0.03125 -0.828125 0.21875q-0.296875 0.1875 -0.515625 0.609375q-0.21875 0.40625 -0.4375 1.4375q-0.203125 1.015625 -0.4375 1.453125q-0.21875 0.453125 -0.546875 0.703125q-0.3125 0.25 -0.875 0.40625q-0.375 0.109375 -1.125 0.109375l-0.3125 0l0.296875 -1.40625q0.640625 0 0.859375 -0.09375q0.234375 -0.09375 0.359375 -0.265625q0.078125 -0.125 0.25 -0.84375q0.328125 -1.296875 0.421875 -1.546875q0.21875 -0.515625 0.53125 -0.875q0.3125 -0.359375 0.796875 -0.609375q-0.390625 -0.28125 -0.546875 -0.578125q-0.140625 -0.296875 -0.140625 -0.828125q0 -0.203125 0.078125 -0.625q0.1875 -1.125 0.234375 -1.34375q0.0625 -0.234375 0.0625 -0.390625q0 -0.15625 -0.078125 -0.25q-0.078125 -0.109375 -0.25 -0.15625q-0.15625 -0.0625 -0.75 -0.0625l0.3125 -1.390625l0.3125 0q0.796875 0 1.1875 0.140625q0.40625 0.125 0.625 0.421875q0.21875 0.296875 0.21875 0.78125q0 0.390625 -0.203125 1.328125q-0.203125 0.9375 -0.203125 1.25q0 0.5 0.21875 0.734375q0.21875 0.234375 0.78125 0.265625l-0.296875 1.40625zm4.669922 0q-0.53125 0.03125 -0.828125 0.21875q-0.296875 0.1875 -0.515625 0.609375q-0.21875 0.40625 -0.4375 1.4375q-0.203125 1.015625 -0.4375 1.453125q-0.21875 0.453125 -0.546875 0.703125q-0.3125 0.25 -0.875 0.40625q-0.375 0.109375 -1.125 0.109375l-0.3125 0l0.296875 -1.40625q0.640625 0 0.859375 -0.09375q0.234375 -0.09375 0.359375 -0.265625q0.078125 -0.125 0.25 -0.84375q0.328125 -1.296875 0.421875 -1.546875q0.21875 -0.515625 0.53125 -0.875q0.3125 -0.359375 0.796875 -0.609375q-0.390625 -0.28125 -0.546875 -0.578125q-0.140625 -0.296875 -0.140625 -0.828125q0 -0.203125 0.078125 -0.625q0.1875 -1.125 0.234375 -1.34375q0.0625 -0.234375 0.0625 -0.390625q0 -0.15625 -0.078125 -0.25q-0.078125 -0.109375 -0.25 -0.15625q-0.15625 -0.0625 -0.75 -0.0625l0.3125 -1.390625l0.3125 0q0.796875 0 1.1875 0.140625q0.40625 0.125 0.625 0.421875q0.21875 0.296875 0.21875 0.78125q0 0.390625 -0.203125 1.328125q-0.203125 0.9375 -0.203125 1.25q0 0.5 0.21875 0.734375q0.21875 0.234375 0.78125 0.265625l-0.296875 1.40625z" fill-rule="nonzero"/><path fill="#000000" d="m163.79619 263.24094l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m103.84112 271.41156l58.04882 0l0 1.0799866l-58.04882 0l0 -1.0799866z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m557.41504 68.88976l139.37012 0l0 44.15748l-139.37012 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m557.41504 68.88976l139.37012 0l0 44.15748l-139.37012 0z" fill-rule="evenodd"/><path fill="#000000" d="m567.36816 89.40977l0 -8.59375l6.203125 0l0 1.015625l-5.0625 0l0 2.625l4.75 0l0 1.015625l-4.75 0l0 2.921875l5.265625 0l0 1.015625l-6.40625 0zm7.8476562 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm14.058594 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.7050781 -4.25l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6347656 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m571.0088 96.40977l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0z" fill-rule="nonzero"/><path fill="#010000" fill-opacity="0.0" d="m615.7627 92.546486l28.02539 0l0 13.798828l-28.02539 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m617.6846 103.40977l-2.28125 0l3.78125 -4.34375l-2.125 -4.25l1.84375 0l0.84375 1.609375q0.03125 0.09375 0.5625 1.1875q0.046875 0.0625 0.078125 0.171875q0.71875 -0.875 1.203125 -1.4375l1.359375 -1.53125l2.21875 0l-3.9375 4.53125l2.125 4.0625l-2.0 0l-0.640625 -1.296875q-0.5 -1.03125 -0.671875 -1.46875q-0.21875 0.34375 -1.171875 1.421875l-1.1875 1.34375zm7.5039062 -4.9375l0.3125 -1.5q2.1875 -0.953125 3.375 -2.203125l1.015625 0l-1.8125 8.640625l-1.6875 0l1.25 -5.953125q-0.53125 0.34375 -1.21875 0.625q-0.6875 0.28125 -1.234375 0.390625zm8.423828 -0.96875l-1.640625 -0.25q0.1875 -1.203125 0.921875 -1.828125q0.734375 -0.640625 1.828125 -0.640625q1.203125 0 1.875 0.65625q0.6875 0.640625 0.6875 1.640625q0 0.515625 -0.21875 1.03125q-0.203125 0.515625 -0.671875 1.078125q-0.34375 0.390625 -1.140625 1.125q-0.78125 0.71875 -1.046875 1.0q-0.25 0.265625 -0.484375 0.5625l2.984375 0l-0.3125 1.53125l-5.21875 0q0.078125 -0.640625 0.359375 -1.203125q0.296875 -0.578125 0.71875 -1.0625q0.4375 -0.5 1.59375 -1.59375q0.875 -0.828125 1.09375 -1.078125q0.421875 -0.453125 0.5625 -0.78125q0.15625 -0.34375 0.15625 -0.609375q0 -0.4375 -0.25 -0.703125q-0.25 -0.28125 -0.65625 -0.28125q-0.390625 0 -0.6875 0.3125q-0.296875 0.296875 -0.453125 1.09375zm4.111328 3.65625l1.609375 -0.1875q0.109375 0.6875 0.359375 0.953125q0.265625 0.265625 0.703125 0.265625q0.5625 0 0.9375 -0.390625q0.390625 -0.390625 0.390625 -1.0q0 -0.515625 -0.3125 -0.828125q-0.296875 -0.3125 -0.8125 -0.3125q-0.125 0 -0.265625 0.03125l0.28125 -1.40625q0.09375 0.015625 0.171875 0.015625q0.671875 0 1.015625 -0.34375q0.359375 -0.34375 0.359375 -0.921875q0 -0.421875 -0.234375 -0.65625q-0.234375 -0.25 -0.625 -0.25q-0.359375 0 -0.65625 0.265625q-0.28125 0.265625 -0.453125 0.921875l-1.53125 -0.3125q0.296875 -0.953125 0.8125 -1.453125q0.796875 -0.765625 1.953125 -0.765625q1.171875 0 1.78125 0.65625q0.625 0.65625 0.625 1.578125q0 0.671875 -0.390625 1.21875q-0.390625 0.53125 -1.078125 0.78125q0.546875 0.28125 0.796875 0.75q0.265625 0.453125 0.265625 0.953125q0 0.953125 -0.671875 1.75q-0.921875 1.078125 -2.375 1.078125q-1.09375 0 -1.796875 -0.625q-0.703125 -0.625 -0.859375 -1.765625z" fill-rule="nonzero"/><path fill="#000000" d="m649.0283 96.01914l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m615.7627 104.189766l28.02539 0l0 1.0800018l-28.02539 0l0 -1.0800018z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m258.3517 113.8189l81.543304 -22.834648" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m258.3517 113.8189l75.765564 -21.216705" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m334.56268 94.19274l3.924591 -2.8142776l-4.815399 -0.36681366z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m258.3517 113.8189l81.543304 69.35433" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m258.3517 113.8189l76.97284 65.46705" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m334.25443 180.54414l4.527008 1.6819458l-2.3867493 -4.1983337z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m352.57217 6.0367455l140.56693 0l0 33.51181l-140.56693 0z" fill-rule="evenodd"/><path fill="#000000" d="m361.54092 32.956745l5.125 -13.359377l1.90625 0l5.46875 13.359377l-2.015625 0l-1.546875 -4.046877l-5.59375 0l-1.46875 4.046877l-1.875 0zm3.859375 -5.484377l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm9.802948 5.484377l0 -13.359377l1.640625 0l0 13.359377l-1.640625 0zm10.816711 -3.109377l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.312502q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.2968769q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm9.125702 5.765627l0 -9.671877l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.062502l-1.625 0zm9.806427 -1.4687519l0.234375 1.4531269q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.4687519 -0.203125 -1.9375019l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm7.2884827 1.4687519l0 -13.359377l1.765625 0l0 13.359377l-1.765625 0zm4.683319 0l0 -9.671877l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953127l-1.640625 0l0 -5.890627q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281252l-1.640625 0zm9.719452 -2.890627l1.625 -0.25q0.125 0.96875 0.75 1.5q0.625 0.515625 1.75 0.515625q1.125 0 1.671875 -0.453125q0.546875 -0.46875 0.546875 -1.09375q0 -0.546875 -0.484375 -0.875q-0.328125 -0.21875 -1.671875 -0.546875q-1.8125 -0.46875 -2.515625 -0.796875q-0.6875 -0.328125 -1.046875 -0.90625q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.640625 0.296875 -1.1875q0.296875 -0.5625 0.8125 -0.921875q0.375 -0.28125 1.03125 -0.46875q0.671875 -0.203125 1.421875 -0.203125q1.140625 0 2.0 0.328125q0.859375 0.328125 1.265625 0.890625q0.421875 0.5625 0.578125 1.5l-1.609375 0.21875q-0.109375 -0.75 -0.640625 -1.171875q-0.515625 -0.421875 -1.46875 -0.421875q-1.140625 0 -1.625 0.375q-0.46875 0.375 -0.46875 0.875q0 0.3125 0.1875 0.578125q0.203125 0.265625 0.640625 0.4375q0.234375 0.09375 1.4375 0.421875q1.75 0.453125 2.4375 0.75q0.6875 0.296875 1.078125 0.859375q0.390625 0.5625 0.390625 1.40625q0 0.828125 -0.484375 1.546875q-0.46875 0.7187519 -1.375 1.1250019q-0.90625 0.390625 -2.046875 0.390625q-1.875 0 -2.875 -0.78125q-0.984375 -0.7812519 -1.25 -2.328127zm13.5625 1.421875l0.234375 1.4531269q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.4687519 -0.203125 -1.9375019l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm7.917694 0.28125q-0.921875 0.7656269 -1.765625 1.0937519q-0.828125 0.3125 -1.796875 0.3125q-1.59375 0 -2.453125 -0.78125q-0.859375 -0.7812519 -0.859375 -1.9843769q0 -0.71875 0.328125 -1.296875q0.328125 -0.59375 0.84375 -0.9375q0.53125 -0.359375 1.1875 -0.546875q0.46875 -0.125 1.453125 -0.25q1.984375 -0.234375 2.921875 -0.5625q0.015625 -0.34375 0.015625 -0.421875q0 -1.0 -0.46875 -1.421875q-0.625 -0.546875 -1.875 -0.546875q-1.15625 0 -1.703125 0.40625q-0.546875 0.40625 -0.8125 1.421875l-1.609375 -0.21875q0.21875 -1.015625 0.71875 -1.640625q0.5 -0.640625 1.453125 -0.984375q0.953125 -0.34375 2.1875 -0.34375q1.25 0 2.015625 0.296875q0.78125 0.28125 1.140625 0.734375q0.375 0.4375 0.515625 1.109375q0.078125 0.421875 0.078125 1.515625l0 2.1875q0 2.28125 0.109375 2.890625q0.109375 0.5937519 0.40625 1.1562519l-1.703125 0q-0.265625 -0.515625 -0.328125 -1.1875019zm-0.140625 -3.671875q-0.890625 0.375 -2.671875 0.625q-1.015625 0.140625 -1.4375 0.328125q-0.421875 0.1875 -0.65625 0.53125q-0.21875 0.34375 -0.21875 0.78125q0 0.65625 0.5 1.09375q0.5 0.4375 1.453125 0.4375q0.9375 0 1.671875 -0.40625q0.75 -0.421875 1.09375 -1.140625q0.265625 -0.5625 0.265625 -1.640625l0 -0.609375zm4.203827 4.859377l0 -9.671877l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953127l-1.640625 0l0 -5.890627q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281252l-1.640625 0zm16.688232 -3.546877l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609377q-1.078125 0.9375 -2.671875 0.9375q-1.984375 0 -3.1875 -1.2968769q-1.203125 -1.296875 -1.203125 -3.71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0.96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm9.640625 0.4375l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.312502q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.2968769q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625z" fill-rule="nonzero"/><path fill="#ffffff" d="m339.90027 68.88976l139.37009 0l0 44.15748l-139.37009 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m339.90027 68.88976l139.37009 0l0 44.15748l-139.37009 0z" fill-rule="evenodd"/><path fill="#000000" d="m369.50748 92.67351l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm15.291229 0.921875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm7.0056458 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.1667175 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm8.062012 0l4.0625 -5.46875l-3.578125 -5.015625l1.65625 0l1.890625 2.6875q0.59375 0.828125 0.84375 1.28125q0.359375 -0.5625 0.84375 -1.1875l2.109375 -2.78125l1.5 0l-3.6875 4.9375l3.984375 5.546875l-1.71875 0l-2.640625 -3.75q-0.21875 -0.3125 -0.46875 -0.6875q-0.34375 0.5625 -0.5 0.78125l-2.625 3.65625l-1.671875 0zm15.166229 0l-1.28125 0l0 -8.203125q-0.46875 0.4375 -1.21875 0.890625q-0.75 0.4375 -1.359375 0.65625l0 -1.25q1.078125 -0.5 1.890625 -1.21875q0.8125 -0.734375 1.140625 -1.40625l0.828125 0l0 10.53125zm10.072968 -1.234375l0 1.234375l-6.9375 0q-0.015625 -0.46875 0.15625 -0.890625q0.265625 -0.71875 0.84375 -1.40625q0.59375 -0.6875 1.6875 -1.578125q1.71875 -1.40625 2.3125 -2.21875q0.609375 -0.828125 0.609375 -1.5625q0 -0.765625 -0.5625 -1.28125q-0.546875 -0.53125 -1.421875 -0.53125q-0.921875 0 -1.484375 0.5625q-0.5625 0.5625 -0.5625 1.546875l-1.328125 -0.140625q0.140625 -1.484375 1.015625 -2.25q0.890625 -0.78125 2.390625 -0.78125q1.515625 0 2.390625 0.84375q0.875 0.828125 0.875 2.0625q0 0.640625 -0.265625 1.25q-0.25 0.59375 -0.84375 1.265625q-0.59375 0.671875 -1.984375 1.859375q-1.15625 0.96875 -1.484375 1.3125q-0.328125 0.34375 -0.546875 0.703125l5.140625 0zm1.385498 -1.53125l1.296875 -0.171875q0.21875 1.09375 0.75 1.578125q0.53125 0.484375 1.296875 0.484375q0.90625 0 1.53125 -0.625q0.640625 -0.640625 0.640625 -1.5625q0 -0.890625 -0.59375 -1.46875q-0.578125 -0.578125 -1.46875 -0.578125q-0.359375 0 -0.90625 0.140625l0.140625 -1.125q0.125 0.015625 0.203125 0.015625q0.828125 0 1.484375 -0.421875q0.65625 -0.4375 0.65625 -1.34375q0 -0.703125 -0.484375 -1.15625q-0.46875 -0.46875 -1.234375 -0.46875q-0.75 0 -1.25 0.46875q-0.5 0.46875 -0.640625 1.421875l-1.28125 -0.234375q0.234375 -1.296875 1.0625 -2.0q0.84375 -0.71875 2.09375 -0.71875q0.859375 0 1.578125 0.375q0.71875 0.359375 1.09375 1.0q0.390625 0.640625 0.390625 1.34375q0 0.6875 -0.375 1.25q-0.359375 0.546875 -1.0625 0.875q0.921875 0.21875 1.4375 0.90625q0.515625 0.671875 0.515625 1.6875q0 1.375 -1.0 2.328125q-1.0 0.953125 -2.53125 0.953125q-1.390625 0 -2.296875 -0.8125q-0.90625 -0.828125 -1.046875 -2.140625z" fill-rule="nonzero"/><path fill="#ffffff" d="m339.90027 161.08923l140.56693 0l0 44.157486l-140.56693 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m339.90027 161.08923l140.56693 0l0 44.157486l-140.56693 0z" fill-rule="evenodd"/><path fill="#000000" d="m370.2384 184.87299l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm15.291229 0.921875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.1667175 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm7.0056458 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm11.828278 0l0 -4.4375l-4.046875 -6.046875l1.6875 0l2.0625 3.15625q0.578125 0.890625 1.0625 1.78125q0.484375 -0.828125 1.15625 -1.859375l2.03125 -3.078125l1.609375 0l-4.1875 6.046875l0 4.4375l-1.375 0zm10.416229 0l0 -2.515625l-4.546875 0l0 -1.171875l4.78125 -6.796875l1.0625 0l0 6.796875l1.40625 0l0 1.171875l-1.40625 0l0 2.515625l-1.296875 0zm0 -3.6875l0 -4.734375l-3.28125 4.734375l3.28125 0zm4.0260925 0.9375l1.34375 -0.109375q0.15625 0.984375 0.703125 1.484375q0.546875 0.5 1.328125 0.5q0.921875 0 1.5625 -0.703125q0.65625 -0.703125 0.65625 -1.859375q0 -1.109375 -0.625 -1.734375q-0.625 -0.640625 -1.625 -0.640625q-0.625 0 -1.125 0.28125q-0.5 0.28125 -0.78125 0.734375l-1.203125 -0.15625l1.015625 -5.390625l5.21875 0l0 1.21875l-4.1875 0l-0.578125 2.828125q0.953125 -0.65625 1.984375 -0.65625q1.375 0 2.3125 0.953125q0.953125 0.953125 0.953125 2.4375q0 1.4375 -0.828125 2.46875q-1.015625 1.265625 -2.75 1.265625q-1.4375 0 -2.34375 -0.796875q-0.90625 -0.796875 -1.03125 -2.125zm14.838623 -5.171875l-1.28125 0.09375q-0.171875 -0.75 -0.484375 -1.09375q-0.53125 -0.546875 -1.296875 -0.546875q-0.609375 0 -1.078125 0.34375q-0.609375 0.4375 -0.96875 1.296875q-0.34375 0.84375 -0.359375 2.421875q0.46875 -0.71875 1.140625 -1.0625q0.671875 -0.34375 1.40625 -0.34375q1.296875 0 2.203125 0.953125q0.90625 0.953125 0.90625 2.453125q0 0.984375 -0.4375 1.84375q-0.421875 0.84375 -1.171875 1.296875q-0.734375 0.4375 -1.6875 0.4375q-1.609375 0 -2.625 -1.171875q-1.015625 -1.1875 -1.015625 -3.90625q0 -3.046875 1.125 -4.421875q0.984375 -1.203125 2.640625 -1.203125q1.234375 0 2.03125 0.703125q0.796875 0.6875 0.953125 1.90625zm-5.265625 4.515625q0 0.671875 0.28125 1.28125q0.28125 0.609375 0.78125 0.9375q0.515625 0.3125 1.078125 0.3125q0.8125 0 1.390625 -0.65625q0.59375 -0.671875 0.59375 -1.796875q0 -1.09375 -0.578125 -1.71875q-0.578125 -0.625 -1.453125 -0.625q-0.875 0 -1.484375 0.625q-0.609375 0.625 -0.609375 1.640625z" fill-rule="nonzero"/><path fill="#ffffff" d="m339.90027 260.88937l140.56693 0l0 44.15747l-140.56693 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m339.90027 260.88937l140.56693 0l0 44.15747l-140.56693 0z" fill-rule="evenodd"/><path fill="#000000" d="m370.5174 284.67313l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm15.291229 0.921875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm7.0056458 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.1667175 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm8.296387 0l0 -1.28125l5.375 -6.734375q0.5625 -0.703125 1.078125 -1.234375l-5.84375 0l0 -1.234375l7.515625 0l0 1.234375l-5.890625 7.28125l-0.640625 0.734375l6.703125 0l0 1.234375l-8.296875 0zm9.343231 -9.125l0 -1.234375l6.796875 0l0 1.0q-1.0 1.078125 -1.984375 2.84375q-0.984375 1.765625 -1.515625 3.625q-0.390625 1.328125 -0.5 2.890625l-1.328125 0q0.03125 -1.234375 0.484375 -2.984375q0.46875 -1.765625 1.34375 -3.390625q0.875 -1.625 1.84375 -2.75l-5.140625 0zm10.057373 3.4375q-0.796875 -0.296875 -1.1875 -0.84375q-0.390625 -0.546875 -0.390625 -1.296875q0 -1.140625 0.8125 -1.921875q0.828125 -0.78125 2.203125 -0.78125q1.375 0 2.203125 0.796875q0.84375 0.796875 0.84375 1.9375q0 0.734375 -0.390625 1.28125q-0.375 0.53125 -1.15625 0.828125q0.96875 0.3125 1.46875 1.015625q0.5 0.703125 0.5 1.671875q0 1.34375 -0.953125 2.265625q-0.9375 0.90625 -2.5 0.90625q-1.546875 0 -2.5 -0.90625q-0.953125 -0.921875 -0.953125 -2.296875q0 -1.03125 0.515625 -1.71875q0.515625 -0.6875 1.484375 -0.9375zm-0.265625 -2.1875q0 0.75 0.484375 1.21875q0.484375 0.46875 1.25 0.46875q0.734375 0 1.203125 -0.46875q0.484375 -0.46875 0.484375 -1.140625q0 -0.71875 -0.5 -1.1875q-0.484375 -0.484375 -1.203125 -0.484375q-0.734375 0 -1.234375 0.46875q-0.484375 0.46875 -0.484375 1.125zm-0.40625 4.84375q0 0.5625 0.25 1.078125q0.265625 0.5 0.78125 0.796875q0.515625 0.28125 1.109375 0.28125q0.921875 0 1.515625 -0.59375q0.609375 -0.59375 0.609375 -1.515625q0 -0.9375 -0.625 -1.53125q-0.609375 -0.609375 -1.546875 -0.609375q-0.90625 0 -1.5 0.609375q-0.59375 0.59375 -0.59375 1.484375zm7.0260925 0.609375l1.25 -0.125q0.15625 0.875 0.59375 1.28125q0.4375 0.390625 1.140625 0.390625q0.59375 0 1.03125 -0.265625q0.453125 -0.28125 0.734375 -0.734375q0.296875 -0.453125 0.484375 -1.21875q0.1875 -0.78125 0.1875 -1.59375q0 -0.078125 0 -0.25q-0.390625 0.609375 -1.0625 1.0q-0.65625 0.375 -1.4375 0.375q-1.3125 0 -2.21875 -0.9375q-0.890625 -0.953125 -0.890625 -2.484375q0 -1.609375 0.9375 -2.578125q0.9375 -0.96875 2.359375 -0.96875q1.03125 0 1.875 0.546875q0.84375 0.546875 1.28125 1.578125q0.4375 1.015625 0.4375 2.953125q0 2.0 -0.4375 3.203125q-0.421875 1.1875 -1.296875 1.8125q-0.859375 0.609375 -2.015625 0.609375q-1.234375 0 -2.015625 -0.671875q-0.78125 -0.6875 -0.9375 -1.921875zm5.28125 -4.640625q0 -1.109375 -0.59375 -1.75q-0.59375 -0.65625 -1.421875 -0.65625q-0.859375 0 -1.5 0.703125q-0.625 0.703125 -0.625 1.8125q0 1.0 0.59375 1.640625q0.609375 0.625 1.5 0.625q0.890625 0 1.46875 -0.625q0.578125 -0.640625 0.578125 -1.75z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m258.3517 113.8189l81.543304 169.13385" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m258.3517 113.8189l78.93759 163.72919" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m335.80145 278.2654l3.4586792 3.3704834l-0.4829712 -4.8051147z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m581.33215 2.6403973l140.5669 0l0 33.511814l-140.5669 0z" fill-rule="evenodd"/><path fill="#000000" d="m590.3009 29.560396l5.125 -13.359375l1.90625 0l5.46875 13.359375l-2.015625 0l-1.546875 -4.046875l-5.59375 0l-1.46875 4.046875l-1.875 0zm3.859375 -5.484375l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm16.162292 1.9375l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609375q-1.078125 0.9375 -2.671875 0.9375q-1.984375 0 -3.1875 -1.296875q-1.203125 -1.296875 -1.203125 -3.71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0.96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm6.59375 2.078125l0.234375 1.453125q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm1.6052246 -10.0l0 -1.890625l1.640625 0l0 1.890625l-1.640625 0zm0 11.46875l0 -9.671875l1.640625 0l0 9.671875l-1.640625 0zm3.5354004 -4.84375q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.59375 -2.328125 0.59375q-2.03125 0 -3.28125 -1.296875q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm9.297607 4.84375l0 -9.671875l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953125l-1.640625 0l0 -5.890625q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.28125l-1.640625 0z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m557.41504 161.08923l139.37012 0l0 44.157486l-139.37012 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m557.41504 161.08923l139.37012 0l0 44.157486l-139.37012 0z" fill-rule="evenodd"/><path fill="#000000" d="m567.36816 181.60924l0 -8.59375l6.203125 0l0 1.015625l-5.0625 0l0 2.625l4.75 0l0 1.015625l-4.75 0l0 2.921875l5.265625 0l0 1.015625l-6.40625 0zm7.8476562 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm14.058594 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.7050781 -4.25l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6347656 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m571.0088 188.60924l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0z" fill-rule="nonzero"/><path fill="#010000" fill-opacity="0.0" d="m615.7627 184.74596l8.003906 0l0 13.798828l-8.003906 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m620.5752 195.60924l-1.765625 0l0.703125 -3.34375l-2.375 -5.25l1.890625 0l1.0625 2.421875q0.375 0.875 0.5 1.265625q0.21875 -0.375 0.515625 -0.8125l0.8125 -1.125l1.203125 -1.75l2.0625 0l-3.9375 5.40625l-0.671875 3.1875z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m623.7666 184.74596l20.021484 0l0 13.798828l-20.021484 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m625.1885 190.67174l0.3125 -1.5q2.1875 -0.953125 3.375 -2.203125l1.015625 0l-1.8125 8.640625l-1.6875 0l1.25 -5.953125q-0.53125 0.34375 -1.21875 0.625q-0.6875 0.28125 -1.234375 0.390625zm8.423828 -0.96875l-1.640625 -0.25q0.1875 -1.203125 0.921875 -1.828125q0.734375 -0.640625 1.828125 -0.640625q1.203125 0 1.875 0.65625q0.6875 0.640625 0.6875 1.640625q0 0.515625 -0.21875 1.03125q-0.203125 0.515625 -0.671875 1.078125q-0.34375 0.390625 -1.140625 1.125q-0.78125 0.71875 -1.046875 1.0q-0.25 0.265625 -0.484375 0.5625l2.984375 0l-0.3125 1.53125l-5.21875 0q0.078125 -0.640625 0.359375 -1.203125q0.296875 -0.578125 0.71875 -1.0625q0.4375 -0.5 1.59375 -1.59375q0.875 -0.828125 1.09375 -1.078125q0.421875 -0.453125 0.5625 -0.78125q0.15625 -0.34375 0.15625 -0.609375q0 -0.4375 -0.25 -0.703125q-0.25 -0.28125 -0.65625 -0.28125q-0.390625 0 -0.6875 0.3125q-0.296875 0.296875 -0.453125 1.09375zm4.111328 3.65625l1.609375 -0.1875q0.109375 0.6875 0.359375 0.953125q0.265625 0.265625 0.703125 0.265625q0.5625 0 0.9375 -0.390625q0.390625 -0.390625 0.390625 -1.0q0 -0.515625 -0.3125 -0.828125q-0.296875 -0.3125 -0.8125 -0.3125q-0.125 0 -0.265625 0.03125l0.28125 -1.40625q0.09375 0.015625 0.171875 0.015625q0.671875 0 1.015625 -0.34375q0.359375 -0.34375 0.359375 -0.921875q0 -0.421875 -0.234375 -0.65625q-0.234375 -0.25 -0.625 -0.25q-0.359375 0 -0.65625 0.265625q-0.28125 0.265625 -0.453125 0.921875l-1.53125 -0.3125q0.296875 -0.953125 0.8125 -1.453125q0.796875 -0.765625 1.953125 -0.765625q1.171875 0 1.78125 0.65625q0.625 0.65625 0.625 1.578125q0 0.671875 -0.390625 1.21875q-0.390625 0.53125 -1.078125 0.78125q0.546875 0.28125 0.796875 0.75q0.265625 0.453125 0.265625 0.953125q0 0.953125 -0.671875 1.75q-0.921875 1.078125 -2.375 1.078125q-1.09375 0 -1.796875 -0.625q-0.703125 -0.625 -0.859375 -1.765625z" fill-rule="nonzero"/><path fill="#000000" d="m649.0283 188.21861l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m615.7627 196.38924l28.02539 0l0 1.0800018l-28.02539 0l0 -1.0800018z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m557.41504 260.88977l139.37012 0l0 44.15747l-139.37012 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m557.41504 260.88977l139.37012 0l0 44.15747l-139.37012 0z" fill-rule="evenodd"/><path fill="#000000" d="m567.36816 281.40976l0 -8.59375l6.203125 0l0 1.015625l-5.0625 0l0 2.625l4.75 0l0 1.015625l-4.75 0l0 2.921875l5.265625 0l0 1.015625l-6.40625 0zm7.8476562 0l0 -6.21875l0.9375 0l0 0.875q0.296875 -0.46875 0.78125 -0.734375q0.484375 -0.28125 1.109375 -0.28125q0.6875 0 1.125 0.28125q0.453125 0.28125 0.625 0.796875q0.75 -1.078125 1.921875 -1.078125q0.9375 0 1.421875 0.515625q0.5 0.5 0.5 1.578125l0 4.265625l-1.046875 0l0 -3.921875q0 -0.625 -0.109375 -0.90625q-0.09375 -0.28125 -0.359375 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.65625 0 -1.09375 0.4375q-0.421875 0.4375 -0.421875 1.40625l0 3.609375l-1.0625 0l0 -4.046875q0 -0.703125 -0.265625 -1.046875q-0.25 -0.359375 -0.828125 -0.359375q-0.453125 0 -0.828125 0.234375q-0.375 0.234375 -0.546875 0.6875q-0.171875 0.453125 -0.171875 1.296875l0 3.234375l-1.046875 0zm14.058594 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.7050781 -4.25l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6347656 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m571.0088 288.40976l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0z" fill-rule="nonzero"/><path fill="#010000" fill-opacity="0.0" d="m615.7627 284.54648l7.330078 0l0 13.798828l-7.330078 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m616.0596 295.40976l0.28125 -1.359375l4.953125 -5.796875l-4.0 0l0.296875 -1.4375l6.1875 0l-0.25 1.34375l-5.0 5.84375q0.796875 -0.015625 0.921875 -0.015625q0.5625 0 1.546875 -0.015625l2.03125 0l-0.296875 1.4375l-6.671875 0z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m623.0928 284.54648l20.021484 0l0 13.798828l-20.021484 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m624.51465 290.47226l0.3125 -1.5q2.1875 -0.953125 3.375 -2.203125l1.015625 0l-1.8125 8.640625l-1.6875 0l1.25 -5.953125q-0.53125 0.34375 -1.21875 0.625q-0.6875 0.28125 -1.234375 0.390625zm8.423828 -0.96875l-1.640625 -0.25q0.1875 -1.203125 0.921875 -1.828125q0.734375 -0.640625 1.828125 -0.640625q1.203125 0 1.875 0.65625q0.6875 0.640625 0.6875 1.640625q0 0.515625 -0.21875 1.03125q-0.203125 0.515625 -0.671875 1.078125q-0.34375 0.390625 -1.140625 1.125q-0.78125 0.71875 -1.046875 1.0q-0.25 0.265625 -0.484375 0.5625l2.984375 0l-0.3125 1.53125l-5.21875 0q0.078125 -0.640625 0.359375 -1.203125q0.296875 -0.578125 0.71875 -1.0625q0.4375 -0.5 1.59375 -1.59375q0.875 -0.828125 1.09375 -1.078125q0.421875 -0.453125 0.5625 -0.78125q0.15625 -0.34375 0.15625 -0.609375q0 -0.4375 -0.25 -0.703125q-0.25 -0.28125 -0.65625 -0.28125q-0.390625 0 -0.6875 0.3125q-0.296875 0.296875 -0.453125 1.09375zm4.111328 3.65625l1.609375 -0.1875q0.109375 0.6875 0.359375 0.953125q0.265625 0.265625 0.703125 0.265625q0.5625 0 0.9375 -0.390625q0.390625 -0.390625 0.390625 -1.0q0 -0.515625 -0.3125 -0.828125q-0.296875 -0.3125 -0.8125 -0.3125q-0.125 0 -0.265625 0.03125l0.28125 -1.40625q0.09375 0.015625 0.171875 0.015625q0.671875 0 1.015625 -0.34375q0.359375 -0.34375 0.359375 -0.921875q0 -0.421875 -0.234375 -0.65625q-0.234375 -0.25 -0.625 -0.25q-0.359375 0 -0.65625 0.265625q-0.28125 0.265625 -0.453125 0.921875l-1.53125 -0.3125q0.296875 -0.953125 0.8125 -1.453125q0.796875 -0.765625 1.953125 -0.765625q1.171875 0 1.78125 0.65625q0.625 0.65625 0.625 1.578125q0 0.671875 -0.390625 1.21875q-0.390625 0.53125 -1.078125 0.78125q0.546875 0.28125 0.796875 0.75q0.265625 0.453125 0.265625 0.953125q0 0.953125 -0.671875 1.75q-0.921875 1.078125 -2.375 1.078125q-1.09375 0 -1.796875 -0.625q-0.703125 -0.625 -0.859375 -1.765625z" fill-rule="nonzero"/><path fill="#000000" d="m648.3545 288.01913l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m615.7627 296.18976l27.351562 0l0 1.0800171l-27.351562 0l0 -1.0800171z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m480.4672 183.16798l76.944855 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m480.4672 183.16798l70.944855 0" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m551.41205 184.81972l4.538147 -1.6517334l-4.538147 -1.6517334z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m479.27036 90.968506l78.14169 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m479.27036 90.968506l72.14169 0" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m551.41205 92.62024l4.538147 -1.6517334l-4.538147 -1.6517334z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m480.4672 282.9681l76.944855 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m480.4672 282.9681l70.944855 0" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m551.41205 284.61984l4.538147 -1.6517334l-4.538147 -1.6517334z" fill-rule="evenodd"/></g></svg> \ No newline at end of file diff --git a/docs/user/alerting/images/alert-types-index-threshold-conditions.png b/docs/user/alerting/images/alert-types-index-threshold-conditions.png new file mode 100644 index 0000000000000..356732dfb9777 Binary files /dev/null and b/docs/user/alerting/images/alert-types-index-threshold-conditions.png differ diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-aggregation.png b/docs/user/alerting/images/alert-types-index-threshold-example-aggregation.png new file mode 100644 index 0000000000000..fc40da7436547 Binary files /dev/null and b/docs/user/alerting/images/alert-types-index-threshold-example-aggregation.png differ diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-comparison.png b/docs/user/alerting/images/alert-types-index-threshold-example-comparison.png new file mode 100644 index 0000000000000..5e7c65e1247d8 Binary files /dev/null and b/docs/user/alerting/images/alert-types-index-threshold-example-comparison.png differ diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-grouping.png b/docs/user/alerting/images/alert-types-index-threshold-example-grouping.png new file mode 100644 index 0000000000000..ea3a3849c8927 Binary files /dev/null and b/docs/user/alerting/images/alert-types-index-threshold-example-grouping.png differ diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-index.png b/docs/user/alerting/images/alert-types-index-threshold-example-index.png new file mode 100644 index 0000000000000..8f818f7001278 Binary files /dev/null and b/docs/user/alerting/images/alert-types-index-threshold-example-index.png differ diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-preview.png b/docs/user/alerting/images/alert-types-index-threshold-example-preview.png new file mode 100644 index 0000000000000..b5d9c38d99810 Binary files /dev/null and b/docs/user/alerting/images/alert-types-index-threshold-example-preview.png differ diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-threshold.png b/docs/user/alerting/images/alert-types-index-threshold-example-threshold.png new file mode 100644 index 0000000000000..9c51807b8d219 Binary files /dev/null and b/docs/user/alerting/images/alert-types-index-threshold-example-threshold.png differ diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-timefield.png b/docs/user/alerting/images/alert-types-index-threshold-example-timefield.png new file mode 100644 index 0000000000000..24e4e03f829ce Binary files /dev/null and b/docs/user/alerting/images/alert-types-index-threshold-example-timefield.png differ diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-window.png b/docs/user/alerting/images/alert-types-index-threshold-example-window.png new file mode 100644 index 0000000000000..5405415958485 Binary files /dev/null and b/docs/user/alerting/images/alert-types-index-threshold-example-window.png differ diff --git a/docs/user/alerting/images/alert-types-index-threshold-preview.png b/docs/user/alerting/images/alert-types-index-threshold-preview.png new file mode 100644 index 0000000000000..3709f162b612b Binary files /dev/null and b/docs/user/alerting/images/alert-types-index-threshold-preview.png differ diff --git a/docs/user/alerting/images/alert-types-index-threshold-select.png b/docs/user/alerting/images/alert-types-index-threshold-select.png new file mode 100644 index 0000000000000..0c2776e01b962 Binary files /dev/null and b/docs/user/alerting/images/alert-types-index-threshold-select.png differ diff --git a/docs/user/alerting/images/alerting-overview.png b/docs/user/alerting/images/alerting-overview.png new file mode 100644 index 0000000000000..383bc8c2ce015 Binary files /dev/null and b/docs/user/alerting/images/alerting-overview.png differ diff --git a/docs/user/alerting/images/what-is-an-action.svg b/docs/user/alerting/images/what-is-an-action.svg new file mode 100644 index 0000000000000..7675f717a6c84 --- /dev/null +++ b/docs/user/alerting/images/what-is-an-action.svg @@ -0,0 +1 @@ +<svg version="1.1" viewBox="0.0 0.0 985.5511811023622 337.08923884514434" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l985.5512 0l0 337.08923l-985.5512 0l0 -337.08923z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l985.5512 0l0 337.08923l-985.5512 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m13.120735 28.889763l451.9685 0l0 282.2677l-451.9685 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m13.120735 28.889763l451.9685 0l0 282.2677l-451.9685 0z" fill-rule="evenodd"/><path fill="#000000" d="m22.089485 55.80976l5.125 -13.359375l1.90625 0l5.46875 13.359375l-2.015625 0l-1.546875 -4.046875l-5.59375 0l-1.46875 4.046875l-1.875 0zm3.859375 -5.484375l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm9.802948 5.484375l0 -13.359375l1.640625 0l0 13.359375l-1.640625 0zm10.816696 -3.109375l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.296875q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm9.125717 5.765625l0 -9.671875l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.0625l-1.625 0zm9.806427 -1.46875l0.234375 1.453125q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 73.062996l418.2677 0l0 33.51181l-418.2677 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 73.062996l418.2677 0l0 33.51181l-418.2677 0z" fill-rule="evenodd"/><path fill="#000000" d="m43.750084 93.40737l1.515625 0.375q-0.46875 1.875 -1.71875 2.859375q-1.234375 0.984375 -3.015625 0.984375q-1.859375 0 -3.015625 -0.75q-1.15625 -0.765625 -1.765625 -2.1875q-0.609375 -1.4375 -0.609375 -3.078125q0 -1.796875 0.6875 -3.125q0.6875 -1.328125 1.9375 -2.015625q1.265625 -0.703125 2.78125 -0.703125q1.71875 0 2.890625 0.875q1.171875 0.875 1.640625 2.46875l-1.5 0.34375q-0.390625 -1.25 -1.15625 -1.8125q-0.75 -0.578125 -1.90625 -0.578125q-1.3125 0 -2.203125 0.640625q-0.890625 0.625 -1.25 1.703125q-0.359375 1.0625 -0.359375 2.1875q0 1.46875 0.421875 2.5625q0.4375 1.078125 1.328125 1.625q0.90625 0.53125 1.953125 0.53125q1.265625 0 2.140625 -0.734375q0.890625 -0.734375 1.203125 -2.171875zm2.6796875 -0.140625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0zm14.2734375 0l0 -1.046875q-0.78125 1.234375 -2.3125 1.234375q-1.0 0 -1.828125 -0.546875q-0.828125 -0.546875 -1.296875 -1.53125q-0.453125 -0.984375 -0.453125 -2.25q0 -1.25 0.40625 -2.25q0.421875 -1.015625 1.25 -1.546875q0.828125 -0.546875 1.859375 -0.546875q0.75 0 1.328125 0.3125q0.59375 0.3125 0.953125 0.828125l0 -4.109375l1.40625 0l0 11.453125l-1.3125 0zm-4.4375 -4.140625q0 1.59375 0.671875 2.390625q0.671875 0.78125 1.578125 0.78125q0.921875 0 1.5625 -0.75q0.65625 -0.765625 0.65625 -2.3125q0 -1.703125 -0.65625 -2.5q-0.65625 -0.796875 -1.625 -0.796875q-0.9375 0 -1.5625 0.765625q-0.625 0.765625 -0.625 2.421875zm7.9609375 -5.703125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm6.6171875 -1.265625l0.203125 1.25q-0.59375 0.125 -1.0625 0.125q-0.765625 0 -1.1875 -0.234375q-0.421875 -0.25 -0.59375 -0.640625q-0.171875 -0.40625 -0.171875 -1.671875l0 -4.765625l-1.03125 0l0 -1.09375l1.03125 0l0 -2.0625l1.40625 -0.84375l0 2.90625l1.40625 0l0 1.09375l-1.40625 0l0 4.84375q0 0.609375 0.0625 0.78125q0.078125 0.171875 0.25 0.28125q0.171875 0.09375 0.484375 0.09375q0.234375 0 0.609375 -0.0625zm1.3828125 -8.578125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm3.0234375 -4.15625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0z" fill-rule="nonzero"/><path fill="#000000" d="m131.0157 94.57925l1.40625 -0.078125q0 0.609375 0.1875 1.046875q0.1875 0.421875 0.6875 0.703125q0.515625 0.265625 1.1875 0.265625q0.9375 0 1.40625 -0.375q0.46875 -0.375 0.46875 -0.890625q0 -0.359375 -0.28125 -0.6875q-0.296875 -0.328125 -1.421875 -0.8125q-1.109375 -0.484375 -1.421875 -0.671875q-0.53125 -0.328125 -0.796875 -0.75q-0.265625 -0.4375 -0.265625 -1.0q0 -0.984375 0.78125 -1.6875q0.78125 -0.703125 2.1875 -0.703125q1.5625 0 2.375 0.71875q0.828125 0.71875 0.859375 1.90625l-1.390625 0.09375q-0.03125 -0.75 -0.53125 -1.1875q-0.5 -0.4375 -1.40625 -0.4375q-0.734375 0 -1.140625 0.34375q-0.40625 0.328125 -0.40625 0.71875q0 0.390625 0.34375 0.6875q0.234375 0.203125 1.21875 0.625q1.625 0.703125 2.046875 1.109375q0.671875 0.640625 0.671875 1.578125q0 0.609375 -0.390625 1.203125q-0.375 0.59375 -1.15625 0.953125q-0.765625 0.359375 -1.828125 0.359375q-1.4375 0 -2.4375 -0.71875q-1.015625 -0.703125 -0.953125 -2.3125zm13.953125 0.015625l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm6.9140625 4.875l1.734375 -8.296875l1.25 0l-0.34375 1.6875q0.640625 -0.953125 1.25 -1.40625q0.609375 -0.46875 1.25 -0.46875q0.421875 0 1.046875 0.296875l-0.578125 1.3125q-0.375 -0.265625 -0.8125 -0.265625q-0.734375 0 -1.515625 0.828125q-0.78125 0.828125 -1.234375 2.984375l-0.703125 3.328125l-1.34375 0zm7.421875 0l-1.359375 -8.296875l1.375 0l0.71875 4.5625q0.109375 0.75 0.28125 2.453125q0.40625 -0.890625 1.046875 -2.03125l2.765625 -4.984375l1.5 0l-4.75 8.296875l-1.578125 0zm12.0 -2.828125l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm6.9140625 4.875l1.734375 -8.296875l1.25 0l-0.34375 1.6875q0.640625 -0.953125 1.25 -1.40625q0.609375 -0.46875 1.25 -0.46875q0.421875 0 1.046875 0.296875l-0.578125 1.3125q-0.375 -0.265625 -0.8125 -0.265625q-0.734375 0 -1.515625 0.828125q-0.78125 0.828125 -1.234375 2.984375l-0.703125 3.328125l-1.34375 0zm15.3671875 -1.03125q-0.734375 0.625 -1.421875 0.921875q-0.671875 0.296875 -1.4375 0.296875q-1.15625 0 -1.859375 -0.671875q-0.703125 -0.6875 -0.703125 -1.734375q0 -0.703125 0.3125 -1.234375q0.328125 -0.53125 0.84375 -0.859375q0.53125 -0.328125 1.28125 -0.46875q0.484375 -0.09375 1.8125 -0.140625q1.328125 -0.0625 1.90625 -0.28125q0.171875 -0.578125 0.171875 -0.96875q0 -0.484375 -0.359375 -0.765625q-0.5 -0.390625 -1.4375 -0.390625q-0.890625 0 -1.46875 0.390625q-0.5625 0.390625 -0.8125 1.125l-1.4375 -0.125q0.4375 -1.234375 1.390625 -1.890625q0.953125 -0.65625 2.390625 -0.65625q1.546875 0 2.4375 0.734375q0.6875 0.546875 0.6875 1.421875q0 0.65625 -0.1875 1.53125l-0.46875 2.0625q-0.21875 0.984375 -0.21875 1.609375q0 0.390625 0.171875 1.125l-1.421875 0q-0.125 -0.40625 -0.171875 -1.03125zm0.53125 -3.171875q-0.296875 0.109375 -0.640625 0.171875q-0.34375 0.0625 -1.140625 0.140625q-1.234375 0.109375 -1.75 0.28125q-0.5 0.15625 -0.765625 0.53125q-0.25 0.359375 -0.25 0.8125q0 0.59375 0.40625 0.984375q0.421875 0.375 1.171875 0.375q0.703125 0 1.34375 -0.375q0.65625 -0.375 1.03125 -1.03125q0.375 -0.671875 0.59375 -1.890625zm4.8671875 4.203125l-1.359375 -8.296875l1.375 0l0.71875 4.5625q0.109375 0.75 0.28125 2.453125q0.40625 -0.890625 1.046875 -2.03125l2.765625 -4.984375l1.5 0l-4.75 8.296875l-1.578125 0zm5.828125 0.75l1.4375 0.140625q-0.015625 0.484375 0.109375 0.71875q0.140625 0.25 0.421875 0.375q0.390625 0.171875 1.0 0.171875q1.28125 0 1.84375 -0.65625q0.359375 -0.453125 0.671875 -1.953125l0.140625 -0.671875q-1.09375 1.125 -2.34375 1.125q-1.25 0 -2.109375 -0.921875q-0.84375 -0.9375 -0.84375 -2.625q0 -1.40625 0.671875 -2.578125q0.671875 -1.171875 1.59375 -1.765625q0.921875 -0.59375 1.890625 -0.59375q1.640625 0 2.515625 1.546875l0.28125 -1.359375l1.296875 0l-1.671875 8.0q-0.265625 1.328125 -0.71875 2.0625q-0.4375 0.75 -1.234375 1.15625q-0.796875 0.40625 -1.828125 0.40625q-1.0 0 -1.734375 -0.265625q-0.71875 -0.25 -1.078125 -0.75q-0.34375 -0.5 -0.34375 -1.140625q0 -0.1875 0.03125 -0.421875zm1.765625 -4.421875q0 0.84375 0.171875 1.28125q0.234375 0.59375 0.65625 0.921875q0.4375 0.3125 0.96875 0.3125q0.703125 0 1.390625 -0.484375q0.6875 -0.5 1.109375 -1.515625q0.421875 -1.03125 0.421875 -1.953125q0 -1.03125 -0.5625 -1.625q-0.5625 -0.609375 -1.40625 -0.609375q-0.515625 0 -1.0 0.28125q-0.484375 0.265625 -0.90625 0.84375q-0.40625 0.5625 -0.625 1.359375q-0.21875 0.78125 -0.21875 1.1875zm20.78125 -0.359375l1.515625 0.203125q-0.71875 1.984375 -2.09375 3.015625q-1.375 1.015625 -3.09375 1.015625q-2.078125 0 -3.3125 -1.28125q-1.21875 -1.28125 -1.21875 -3.65625q0 -3.09375 1.859375 -5.109375q1.65625 -1.8125 4.125 -1.8125q1.8125 0 2.9375 0.984375q1.140625 0.96875 1.3125 2.625l-1.421875 0.125q-0.234375 -1.234375 -0.9375 -1.84375q-0.703125 -0.625 -1.828125 -0.625q-2.09375 0 -3.390625 1.875q-1.125 1.609375 -1.125 3.8125q0 1.765625 0.859375 2.703125q0.875 0.921875 2.265625 0.921875q1.1875 0 2.140625 -0.765625q0.96875 -0.78125 1.40625 -2.1875zm2.5859375 4.03125l2.40625 -11.453125l4.78125 0q1.234375 0 1.859375 0.28125q0.625 0.28125 1.03125 0.984375q0.40625 0.6875 0.40625 1.546875q0 0.703125 -0.296875 1.4375q-0.28125 0.734375 -0.734375 1.21875q-0.4375 0.46875 -0.890625 0.71875q-0.453125 0.234375 -0.96875 0.359375q-1.09375 0.25 -2.21875 0.25l-2.875 0l-0.96875 4.65625l-1.53125 0zm2.78125 -5.953125l2.515625 0q1.46875 0 2.15625 -0.3125q0.6875 -0.3125 1.09375 -0.953125q0.421875 -0.65625 0.421875 -1.390625q0 -0.5625 -0.21875 -0.90625q-0.21875 -0.359375 -0.625 -0.53125q-0.390625 -0.171875 -1.515625 -0.171875l-2.9375 0l-0.890625 4.265625zm10.375 -5.5l1.53125 0l-1.46875 7.03125q-0.171875 0.875 -0.171875 1.296875q0 0.921875 0.71875 1.484375q0.734375 0.5625 1.828125 0.5625q0.875 0 1.625 -0.390625q0.765625 -0.40625 1.203125 -1.1875q0.4375 -0.78125 0.78125 -2.5l1.328125 -6.296875l1.53125 0l-1.40625 6.71875q-0.359375 1.71875 -0.953125 2.71875q-0.578125 1.0 -1.625 1.609375q-1.03125 0.609375 -2.40625 0.609375q-1.296875 0 -2.25 -0.421875q-0.9375 -0.4375 -1.421875 -1.1875q-0.46875 -0.765625 -0.46875 -1.734375q0 -0.59375 0.328125 -2.0625l1.296875 -6.25zm21.85939 6.4375l-7.5937653 3.25l0 -1.40625l6.015625 -2.484375l-6.015625 -2.46875l0 -1.40625l7.5937653 3.203125l0 1.3125zm5.8984375 1.25q0 -1.453125 0.421875 -2.953125q0.4375 -1.5 0.96875 -2.390625q0.53125 -0.90625 1.09375 -1.40625q0.578125 -0.515625 1.125 -0.75q0.5625 -0.234375 1.234375 -0.234375q1.3125 0 2.1875 0.984375q0.875 0.96875 0.875 2.8125q0 1.890625 -0.609375 3.75q-0.71875 2.203125 -2.0 3.3125q-0.984375 0.84375 -2.234375 0.84375q-1.296875 0 -2.1875 -1.015625q-0.875 -1.015625 -0.875 -2.953125zm1.359375 0.359375q0 1.0625 0.359375 1.65625q0.46875 0.8125 1.40625 0.8125q0.828125 0 1.5 -0.734375q0.96875 -1.046875 1.453125 -3.0625q0.484375 -2.03125 0.484375 -3.3125q0 -1.234375 -0.46875 -1.78125q-0.46875 -0.546875 -1.25 -0.546875q-0.5625 0 -1.046875 0.296875q-0.484375 0.28125 -0.9375 0.984375q-0.625 1.0 -1.09375 2.96875q-0.40625 1.6875 -0.40625 2.71875zm7.3203125 3.40625l0.34375 -1.609375l1.59375 0l-0.34375 1.609375l-1.59375 0zm4.6015625 -2.625l1.328125 -0.140625q0.140625 0.96875 0.546875 1.375q0.421875 0.40625 0.984375 0.40625q0.8125 0 1.515625 -0.703125q1.015625 -1.03125 1.5 -2.96875q-0.703125 0.53125 -1.265625 0.765625q-0.546875 0.21875 -1.125 0.21875q-1.046875 0 -1.875 -0.703125q-1.09375 -0.90625 -1.09375 -2.625q0 -1.9375 1.265625 -3.3125q1.09375 -1.1875 2.671875 -1.1875q1.421875 0 2.359375 1.046875q0.9375 1.046875 0.9375 2.96875q0 1.875 -0.625 3.6875q-0.75 2.140625 -2.015625 3.15625q-1.03125 0.84375 -2.3125 0.84375q-1.1875 0 -1.953125 -0.734375q-0.75 -0.75 -0.84375 -2.09375zm1.890625 -4.53125q0 1.078125 0.5625 1.71875q0.578125 0.640625 1.375 0.640625q0.578125 0 1.15625 -0.390625q0.578125 -0.40625 0.984375 -1.203125q0.421875 -0.796875 0.421875 -1.625q0 -0.65625 -0.28125 -1.21875q-0.265625 -0.5625 -0.734375 -0.84375q-0.46875 -0.296875 -0.953125 -0.296875q-0.46875 0 -0.921875 0.25q-0.453125 0.25 -0.84375 0.734375q-0.390625 0.484375 -0.578125 1.125q-0.1875 0.625 -0.1875 1.109375zm11.109375 7.15625l1.5 -7.203125l-1.265625 0l0.234375 -1.09375l1.265625 0l0.234375 -1.1875q0.1875 -0.875 0.390625 -1.265625q0.203125 -0.390625 0.640625 -0.640625q0.453125 -0.265625 1.234375 -0.265625q0.53125 0 1.5625 0.234375l-0.265625 1.234375q-0.71875 -0.1875 -1.203125 -0.1875q-0.40625 0 -0.625 0.203125q-0.21875 0.203125 -0.375 0.984375l-0.1875 0.890625l1.578125 0l-0.21875 1.09375l-1.578125 0l-1.515625 7.203125l-1.40625 0zm4.4921875 -3.15625q0 -2.421875 1.4375 -4.015625q1.171875 -1.3125 3.09375 -1.3125q1.5 0 2.40625 0.9375q0.921875 0.9375 0.921875 2.53125q0 1.421875 -0.578125 2.65625q-0.578125 1.234375 -1.640625 1.890625q-1.0625 0.65625 -2.25 0.65625q-0.96875 0 -1.765625 -0.40625q-0.78125 -0.421875 -1.203125 -1.171875q-0.421875 -0.765625 -0.421875 -1.765625zm1.421875 -0.140625q0 1.171875 0.5625 1.78125q0.5625 0.609375 1.421875 0.609375q0.453125 0 0.890625 -0.1875q0.453125 -0.1875 0.828125 -0.5625q0.390625 -0.375 0.65625 -0.84375q0.28125 -0.484375 0.4375 -1.046875q0.25 -0.765625 0.25 -1.484375q0 -1.125 -0.578125 -1.734375q-0.5625 -0.625 -1.421875 -0.625q-0.65625 0 -1.203125 0.3125q-0.546875 0.3125 -1.0 0.921875q-0.4375 0.609375 -0.640625 1.421875q-0.203125 0.8125 -0.203125 1.4375zm7.2265625 3.296875l1.734375 -8.296875l1.25 0l-0.34375 1.6875q0.640625 -0.953125 1.25 -1.40625q0.609375 -0.46875 1.25 -0.46875q0.421875 0 1.046875 0.296875l-0.578125 1.3125q-0.375 -0.265625 -0.8125 -0.265625q-0.734375 0 -1.515625 0.828125q-0.78125 0.828125 -1.234375 2.984375l-0.703125 3.328125l-1.34375 0zm9.6640625 0l2.390625 -11.453125l1.421875 0l-2.390625 11.453125l-1.421875 0zm9.2578125 -1.03125q-0.734375 0.625 -1.421875 0.921875q-0.671875 0.296875 -1.4375 0.296875q-1.15625 0 -1.859375 -0.671875q-0.703125 -0.6875 -0.703125 -1.734375q0 -0.703125 0.3125 -1.234375q0.328125 -0.53125 0.84375 -0.859375q0.53125 -0.328125 1.28125 -0.46875q0.484375 -0.09375 1.8125 -0.140625q1.328125 -0.0625 1.90625 -0.28125q0.171875 -0.578125 0.171875 -0.96875q0 -0.484375 -0.359375 -0.765625q-0.5 -0.390625 -1.4375 -0.390625q-0.890625 0 -1.46875 0.390625q-0.5625 0.390625 -0.8125 1.125l-1.4375 -0.125q0.4375 -1.234375 1.390625 -1.890625q0.953125 -0.65625 2.390625 -0.65625q1.546875 0 2.4375 0.734375q0.6875 0.546875 0.6875 1.421875q0 0.65625 -0.1875 1.53125l-0.46875 2.0625q-0.21875 0.984375 -0.21875 1.609375q0 0.390625 0.171875 1.125l-1.421875 0q-0.125 -0.40625 -0.171875 -1.03125zm0.53125 -3.171875q-0.296875 0.109375 -0.640625 0.171875q-0.34375 0.0625 -1.140625 0.140625q-1.234375 0.109375 -1.75 0.28125q-0.5 0.15625 -0.765625 0.53125q-0.25 0.359375 -0.25 0.8125q0 0.59375 0.40625 0.984375q0.421875 0.375 1.171875 0.375q0.703125 0 1.34375 -0.375q0.65625 -0.375 1.03125 -1.03125q0.375 -0.671875 0.59375 -1.890625zm2.9140625 1.359375l1.40625 -0.078125q0 0.609375 0.1875 1.046875q0.1875 0.421875 0.6875 0.703125q0.515625 0.265625 1.1875 0.265625q0.9375 0 1.40625 -0.375q0.46875 -0.375 0.46875 -0.890625q0 -0.359375 -0.28125 -0.6875q-0.296875 -0.328125 -1.421875 -0.8125q-1.109375 -0.484375 -1.421875 -0.671875q-0.53125 -0.328125 -0.796875 -0.75q-0.265625 -0.4375 -0.265625 -1.0q0 -0.984375 0.78125 -1.6875q0.78125 -0.703125 2.1875 -0.703125q1.5625 0 2.375 0.71875q0.828125 0.71875 0.859375 1.90625l-1.390625 0.09375q-0.03125 -0.75 -0.53125 -1.1875q-0.5 -0.4375 -1.40625 -0.4375q-0.734375 0 -1.140625 0.34375q-0.40625 0.328125 -0.40625 0.71875q0 0.390625 0.34375 0.6875q0.234375 0.203125 1.21875 0.625q1.625 0.703125 2.046875 1.109375q0.671875 0.640625 0.671875 1.578125q0 0.609375 -0.390625 1.203125q-0.375 0.59375 -1.15625 0.953125q-0.765625 0.359375 -1.828125 0.359375q-1.4375 0 -2.4375 -0.71875q-1.015625 -0.703125 -0.953125 -2.3125zm11.171875 1.6875l-0.234375 1.171875q-0.515625 0.125 -0.984375 0.125q-0.84375 0 -1.34375 -0.421875q-0.375 -0.3125 -0.375 -0.84375q0 -0.28125 0.203125 -1.265625l1.0 -4.8125l-1.109375 0l0.21875 -1.09375l1.125 0l0.421875 -2.046875l1.625 -0.96875l-0.640625 3.015625l1.390625 0l-0.234375 1.09375l-1.375 0l-0.96875 4.578125q-0.171875 0.875 -0.171875 1.046875q0 0.25 0.140625 0.390625q0.140625 0.125 0.46875 0.125q0.46875 0 0.84375 -0.09375zm5.984375 1.15625q0.21875 -1.03125 0.609375 -1.71875q0.390625 -0.6875 1.03125 -1.3125q0.640625 -0.625 2.46875 -2.078125q1.109375 -0.890625 1.515625 -1.3125q0.578125 -0.609375 0.84375 -1.1875q0.1875 -0.40625 0.1875 -0.875q0 -0.78125 -0.5625 -1.328125q-0.5625 -0.5625 -1.375 -0.5625q-0.796875 0 -1.40625 0.5625q-0.59375 0.5625 -0.859375 1.796875l-1.375 -0.203125q0.203125 -1.53125 1.171875 -2.40625q0.984375 -0.875 2.453125 -0.875q0.96875 0 1.765625 0.40625q0.796875 0.40625 1.1875 1.140625q0.40625 0.734375 0.40625 1.515625q0 1.140625 -0.8125 2.1875q-0.5 0.65625 -2.9375 2.59375q-1.046875 0.828125 -1.5625 1.359375q-0.515625 0.53125 -0.765625 1.0l5.125 0l-0.28125 1.296875l-6.828125 0zm12.9375 0l1.734375 -8.296875l1.40625 0l-0.28125 1.359375q0.78125 -0.875 1.40625 -1.203125q0.625 -0.34375 1.34375 -0.34375q0.78125 0 1.296875 0.40625q0.515625 0.40625 0.6875 1.140625q0.625 -0.78125 1.328125 -1.15625q0.703125 -0.390625 1.46875 -0.390625q1.046875 0 1.5625 0.5q0.515625 0.484375 0.515625 1.375q0 0.375 -0.171875 1.265625l-1.125 5.34375l-1.40625 0l1.140625 -5.484375q0.140625 -0.671875 0.140625 -0.96875q0 -0.40625 -0.265625 -0.640625q-0.25 -0.234375 -0.71875 -0.234375q-0.625 0 -1.28125 0.390625q-0.65625 0.375 -1.03125 1.0q-0.359375 0.625 -0.625 1.90625l-0.84375 4.03125l-1.421875 0l1.171875 -5.609375q0.125 -0.5625 0.125 -0.8125q0 -0.40625 -0.25 -0.65625q-0.25 -0.25 -0.65625 -0.25q-0.609375 0 -1.265625 0.390625q-0.65625 0.375 -1.078125 1.0625q-0.40625 0.6875 -0.671875 1.953125l-0.828125 3.921875l-1.40625 0zm15.34375 -9.859375l0.328125 -1.59375l1.40625 0l-0.328125 1.59375l-1.40625 0zm-2.0625 9.859375l1.734375 -8.296875l1.40625 0l-1.734375 8.296875l-1.40625 0zm3.6015625 0l1.734375 -8.296875l1.28125 0l-0.296875 1.4375q0.828125 -0.84375 1.546875 -1.234375q0.734375 -0.390625 1.5 -0.390625q1.0 0 1.578125 0.546875q0.578125 0.546875 0.578125 1.453125q0 0.46875 -0.203125 1.453125l-1.0625 5.03125l-1.40625 0l1.09375 -5.265625q0.171875 -0.765625 0.171875 -1.125q0 -0.421875 -0.296875 -0.671875q-0.28125 -0.265625 -0.8125 -0.265625q-1.09375 0 -1.9375 0.78125q-0.84375 0.78125 -1.25 2.6875l-0.796875 3.859375l-1.421875 0zm14.5703125 -1.5q-1.5 1.6875 -3.0625 1.6875q-0.953125 0 -1.546875 -0.546875q-0.59375 -0.5625 -0.59375 -1.359375q0 -0.515625 0.265625 -1.796875l1.0 -4.78125l1.421875 0l-1.109375 5.296875q-0.140625 0.671875 -0.140625 1.03125q0 0.46875 0.28125 0.734375q0.28125 0.265625 0.84375 0.265625q0.59375 0 1.15625 -0.28125q0.5625 -0.296875 0.96875 -0.78125q0.421875 -0.5 0.671875 -1.171875q0.171875 -0.4375 0.40625 -1.515625l0.75 -3.578125l1.40625 0l-1.734375 8.296875l-1.296875 0l0.3125 -1.5zm6.5390625 0.34375l-0.234375 1.171875q-0.515625 0.125 -0.984375 0.125q-0.84375 0 -1.34375 -0.421875q-0.375 -0.3125 -0.375 -0.84375q0 -0.28125 0.203125 -1.265625l1.0 -4.8125l-1.109375 0l0.21875 -1.09375l1.125 0l0.421875 -2.046875l1.625 -0.96875l-0.640625 3.015625l1.390625 0l-0.234375 1.09375l-1.375 0l-0.96875 4.578125q-0.171875 0.875 -0.171875 1.046875q0 0.25 0.140625 0.390625q0.140625 0.125 0.46875 0.125q0.46875 0 0.84375 -0.09375zm7.2265625 -1.671875l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm7.0546875 2.03125l1.40625 -0.078125q0 0.609375 0.1875 1.046875q0.1875 0.421875 0.6875 0.703125q0.515625 0.265625 1.1875 0.265625q0.9375 0 1.40625 -0.375q0.46875 -0.375 0.46875 -0.890625q0 -0.359375 -0.28125 -0.6875q-0.296875 -0.328125 -1.421875 -0.8125q-1.109375 -0.484375 -1.421875 -0.671875q-0.53125 -0.328125 -0.796875 -0.75q-0.265625 -0.4375 -0.265625 -1.0q0 -0.984375 0.78125 -1.6875q0.78125 -0.703125 2.1875 -0.703125q1.5625 0 2.375 0.71875q0.828125 0.71875 0.859375 1.90625l-1.390625 0.09375q-0.03125 -0.75 -0.53125 -1.1875q-0.5 -0.4375 -1.40625 -0.4375q-0.734375 0 -1.140625 0.34375q-0.40625 0.328125 -0.40625 0.71875q0 0.390625 0.34375 0.6875q0.234375 0.203125 1.21875 0.625q1.625 0.703125 2.046875 1.109375q0.671875 0.640625 0.671875 1.578125q0 0.609375 -0.390625 1.203125q-0.375 0.59375 -1.15625 0.953125q-0.765625 0.359375 -1.828125 0.359375q-1.4375 0 -2.4375 -0.71875q-1.015625 -0.703125 -0.953125 -2.3125z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 120.07612l418.2677 0l0 39.30709l-418.2677 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 120.07612l418.2677 0l0 39.30709l-418.2677 0z" fill-rule="evenodd"/><path fill="#000000" d="m35.062584 140.74861l1.4375 -0.125q0.09375 0.859375 0.46875 1.421875q0.375 0.546875 1.15625 0.890625q0.78125 0.328125 1.75 0.328125q0.875 0 1.53125 -0.25q0.671875 -0.265625 0.984375 -0.703125q0.328125 -0.453125 0.328125 -0.984375q0 -0.546875 -0.3125 -0.9375q-0.3125 -0.40625 -1.03125 -0.6875q-0.453125 -0.171875 -2.03125 -0.546875q-1.578125 -0.390625 -2.21875 -0.71875q-0.8125 -0.4375 -1.21875 -1.0625q-0.40625 -0.640625 -0.40625 -1.4375q0 -0.859375 0.484375 -1.609375q0.5 -0.765625 1.4375 -1.15625q0.953125 -0.390625 2.109375 -0.390625q1.28125 0 2.25 0.421875q0.96875 0.40625 1.484375 1.203125q0.53125 0.796875 0.578125 1.796875l-1.453125 0.109375q-0.125 -1.078125 -0.796875 -1.625q-0.671875 -0.5625 -2.0 -0.5625q-1.375 0 -2.0 0.5q-0.625 0.5 -0.625 1.21875q0 0.609375 0.4375 1.015625q0.4375 0.390625 2.28125 0.8125q1.859375 0.421875 2.546875 0.734375q1.0 0.453125 1.46875 1.171875q0.484375 0.703125 0.484375 1.625q0 0.90625 -0.53125 1.71875q-0.515625 0.8125 -1.5 1.265625q-0.984375 0.453125 -2.203125 0.453125q-1.5625 0 -2.609375 -0.453125q-1.046875 -0.46875 -1.65625 -1.375q-0.59375 -0.90625 -0.625 -2.0625zm16.421875 0.640625l1.390625 0.1875q-0.234375 1.421875 -1.171875 2.234375q-0.921875 0.8125 -2.28125 0.8125q-1.703125 0 -2.75 -1.109375q-1.03125 -1.125 -1.03125 -3.203125q0 -1.34375 0.4375 -2.34375q0.453125 -1.015625 1.359375 -1.515625q0.921875 -0.5 1.984375 -0.5q1.359375 0 2.21875 0.6875q0.859375 0.671875 1.09375 1.9375l-1.359375 0.203125q-0.203125 -0.828125 -0.703125 -1.25q-0.484375 -0.421875 -1.1875 -0.421875q-1.0625 0 -1.734375 0.765625q-0.65625 0.75 -0.65625 2.40625q0 1.671875 0.640625 2.4375q0.640625 0.75 1.671875 0.75q0.828125 0 1.375 -0.5q0.5625 -0.515625 0.703125 -1.578125zm2.59375 3.046875l0 -11.453125l1.40625 0l0 4.109375q0.984375 -1.140625 2.484375 -1.140625q0.921875 0 1.59375 0.359375q0.6875 0.359375 0.96875 1.0q0.296875 0.640625 0.296875 1.859375l0 5.265625l-1.40625 0l0 -5.265625q0 -1.046875 -0.453125 -1.53125q-0.453125 -0.484375 -1.296875 -0.484375q-0.625 0 -1.171875 0.328125q-0.546875 0.328125 -0.78125 0.890625q-0.234375 0.546875 -0.234375 1.515625l0 4.546875l-1.40625 0zm14.5703125 -2.671875l1.453125 0.171875q-0.34375 1.28125 -1.28125 1.984375q-0.921875 0.703125 -2.359375 0.703125q-1.828125 0 -2.890625 -1.125q-1.0625 -1.125 -1.0625 -3.140625q0 -2.09375 1.078125 -3.25q1.078125 -1.15625 2.796875 -1.15625q1.65625 0 2.703125 1.140625q1.0625 1.125 1.0625 3.171875q0 0.125 0 0.375l-6.1875 0q0.078125 1.375 0.765625 2.109375q0.703125 0.71875 1.734375 0.71875q0.78125 0 1.328125 -0.40625q0.546875 -0.40625 0.859375 -1.296875zm-4.609375 -2.28125l4.625 0q-0.09375 -1.046875 -0.53125 -1.5625q-0.671875 -0.8125 -1.734375 -0.8125q-0.96875 0 -1.640625 0.65625q-0.65625 0.640625 -0.71875 1.71875zm13.2109375 4.953125l0 -1.046875q-0.78125 1.234375 -2.3125 1.234375q-1.0 0 -1.828125 -0.546875q-0.828125 -0.546875 -1.296875 -1.53125q-0.453125 -0.984375 -0.453125 -2.25q0 -1.25 0.40625 -2.25q0.421875 -1.015625 1.25 -1.546875q0.828125 -0.546875 1.859375 -0.546875q0.75 0 1.328125 0.3125q0.59375 0.3125 0.953125 0.828125l0 -4.109375l1.40625 0l0 11.453125l-1.3125 0zm-4.4375 -4.140625q0 1.59375 0.671875 2.390625q0.671875 0.78125 1.578125 0.78125q0.921875 0 1.5625 -0.75q0.65625 -0.765625 0.65625 -2.3125q0 -1.703125 -0.65625 -2.5q-0.65625 -0.796875 -1.625 -0.796875q-0.9375 0 -1.5625 0.765625q-0.625 0.765625 -0.625 2.421875zm13.3984375 4.140625l0 -1.21875q-0.96875 1.40625 -2.640625 1.40625q-0.734375 0 -1.375 -0.28125q-0.625 -0.28125 -0.9375 -0.703125q-0.3125 -0.4375 -0.4375 -1.046875q-0.078125 -0.421875 -0.078125 -1.3125l0 -5.140625l1.40625 0l0 4.59375q0 1.109375 0.078125 1.484375q0.140625 0.5625 0.5625 0.875q0.4375 0.3125 1.0625 0.3125q0.640625 0 1.1875 -0.3125q0.5625 -0.328125 0.78125 -0.890625q0.234375 -0.5625 0.234375 -1.625l0 -4.4375l1.40625 0l0 8.296875l-1.25 0zm3.4296875 0l0 -11.453125l1.40625 0l0 11.453125l-1.40625 0zm9.2578125 -2.671875l1.453125 0.171875q-0.34375 1.28125 -1.28125 1.984375q-0.921875 0.703125 -2.359375 0.703125q-1.828125 0 -2.890625 -1.125q-1.0625 -1.125 -1.0625 -3.140625q0 -2.09375 1.078125 -3.25q1.078125 -1.15625 2.796875 -1.15625q1.65625 0 2.703125 1.140625q1.0625 1.125 1.0625 3.171875q0 0.125 0 0.375l-6.1875 0q0.078125 1.375 0.765625 2.109375q0.703125 0.71875 1.734375 0.71875q0.78125 0 1.328125 -0.40625q0.546875 -0.40625 0.859375 -1.296875zm-4.609375 -2.28125l4.625 0q-0.09375 -1.046875 -0.53125 -1.5625q-0.671875 -0.8125 -1.734375 -0.8125q-0.96875 0 -1.640625 0.65625q-0.65625 0.640625 -0.71875 1.71875z" fill-rule="nonzero"/><path fill="#000000" d="m136.96883 141.60799l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm9.0078125 4.875l-1.359375 -8.296875l1.375 0l0.71875 4.5625q0.109375 0.75 0.28125 2.453125q0.40625 -0.890625 1.046875 -2.03125l2.765625 -4.984375l1.5 0l-4.75 8.296875l-1.578125 0zm12.0 -2.828125l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm6.9140625 4.875l1.734375 -8.296875l1.25 0l-0.34375 1.6875q0.640625 -0.953125 1.25 -1.40625q0.609375 -0.46875 1.25 -0.46875q0.421875 0 1.046875 0.296875l-0.578125 1.3125q-0.375 -0.265625 -0.8125 -0.265625q-0.734375 0 -1.515625 0.828125q-0.78125 0.828125 -1.234375 2.984375l-0.703125 3.328125l-1.34375 0zm4.796875 3.203125l0.09375 -1.328125q0.4375 0.125 0.84375 0.125q0.4375 0 0.703125 -0.1875q0.34375 -0.265625 0.75 -0.96875l0.453125 -0.8125l-1.390625 -8.328125l1.390625 0l0.625 4.1875q0.1875 1.25 0.328125 2.484375l3.703125 -6.671875l1.484375 0l-5.296875 9.40625q-0.765625 1.375 -1.359375 1.8125q-0.59375 0.453125 -1.359375 0.453125q-0.5 0 -0.96875 -0.171875zm12.9765625 -3.203125l1.734375 -8.296875l1.40625 0l-0.28125 1.359375q0.78125 -0.875 1.40625 -1.203125q0.625 -0.34375 1.34375 -0.34375q0.78125 0 1.296875 0.40625q0.515625 0.40625 0.6875 1.140625q0.625 -0.78125 1.328125 -1.15625q0.703125 -0.390625 1.46875 -0.390625q1.046875 0 1.5625 0.5q0.515625 0.484375 0.515625 1.375q0 0.375 -0.171875 1.265625l-1.125 5.34375l-1.40625 0l1.140625 -5.484375q0.140625 -0.671875 0.140625 -0.96875q0 -0.40625 -0.265625 -0.640625q-0.25 -0.234375 -0.71875 -0.234375q-0.625 0 -1.28125 0.390625q-0.65625 0.375 -1.03125 1.0q-0.359375 0.625 -0.625 1.90625l-0.84375 4.03125l-1.421875 0l1.171875 -5.609375q0.125 -0.5625 0.125 -0.8125q0 -0.40625 -0.25 -0.65625q-0.25 -0.25 -0.65625 -0.25q-0.609375 0 -1.265625 0.390625q-0.65625 0.375 -1.078125 1.0625q-0.40625 0.6875 -0.671875 1.953125l-0.828125 3.921875l-1.40625 0zm15.34375 -9.859375l0.328125 -1.59375l1.40625 0l-0.328125 1.59375l-1.40625 0zm-2.0625 9.859375l1.734375 -8.296875l1.40625 0l-1.734375 8.296875l-1.40625 0zm3.6015625 0l1.734375 -8.296875l1.28125 0l-0.296875 1.4375q0.828125 -0.84375 1.546875 -1.234375q0.734375 -0.390625 1.5 -0.390625q1.0 0 1.578125 0.546875q0.578125 0.546875 0.578125 1.453125q0 0.46875 -0.203125 1.453125l-1.0625 5.03125l-1.40625 0l1.09375 -5.265625q0.171875 -0.765625 0.171875 -1.125q0 -0.421875 -0.296875 -0.671875q-0.28125 -0.265625 -0.8125 -0.265625q-1.09375 0 -1.9375 0.78125q-0.84375 0.78125 -1.25 2.6875l-0.796875 3.859375l-1.421875 0zm14.5703125 -1.5q-1.5 1.6875 -3.0625 1.6875q-0.953125 0 -1.546875 -0.546875q-0.59375 -0.5625 -0.59375 -1.359375q0 -0.515625 0.265625 -1.796875l1.0 -4.78125l1.421875 0l-1.109375 5.296875q-0.140625 0.671875 -0.140625 1.03125q0 0.46875 0.28125 0.734375q0.28125 0.265625 0.84375 0.265625q0.59375 0 1.15625 -0.28125q0.5625 -0.296875 0.96875 -0.78125q0.421875 -0.5 0.671875 -1.171875q0.171875 -0.4375 0.40625 -1.515625l0.75 -3.578125l1.40625 0l-1.734375 8.296875l-1.296875 0l0.3125 -1.5zm6.5390625 0.34375l-0.234375 1.171875q-0.515625 0.125 -0.984375 0.125q-0.84375 0 -1.34375 -0.421875q-0.375 -0.3125 -0.375 -0.84375q0 -0.28125 0.203125 -1.265625l1.0 -4.8125l-1.109375 0l0.21875 -1.09375l1.125 0l0.421875 -2.046875l1.625 -0.96875l-0.640625 3.015625l1.390625 0l-0.234375 1.09375l-1.375 0l-0.96875 4.578125q-0.171875 0.875 -0.171875 1.046875q0 0.25 0.140625 0.390625q0.140625 0.125 0.46875 0.125q0.46875 0 0.84375 -0.09375zm7.2265625 -1.671875l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 168.92389l418.2677 0l0 129.07086l-418.2677 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 168.92389l418.2677 0l0 129.07086l-418.2677 0z" fill-rule="evenodd"/><path fill="#000000" d="m34.312584 193.28389l4.40625 -11.453125l1.640625 0l4.6875 11.453125l-1.734375 0l-1.328125 -3.46875l-4.796875 0l-1.25 3.46875l-1.625 0zm3.3125 -4.703125l3.890625 0l-1.203125 -3.171875q-0.546875 -1.453125 -0.8125 -2.375q-0.21875 1.09375 -0.609375 2.1875l-1.265625 3.359375zm13.859375 1.65625l1.390625 0.1875q-0.234375 1.421875 -1.171875 2.234375q-0.921875 0.8125 -2.28125 0.8125q-1.703125 0 -2.75 -1.109375q-1.03125 -1.125 -1.03125 -3.203125q0 -1.34375 0.4375 -2.34375q0.453125 -1.015625 1.359375 -1.515625q0.921875 -0.5 1.984375 -0.5q1.359375 0 2.21875 0.6875q0.859375 0.671875 1.09375 1.9375l-1.359375 0.203125q-0.203125 -0.828125 -0.703125 -1.25q-0.484375 -0.421875 -1.1875 -0.421875q-1.0625 0 -1.734375 0.765625q-0.65625 0.75 -0.65625 2.40625q0 1.671875 0.640625 2.4375q0.640625 0.75 1.671875 0.75q0.828125 0 1.375 -0.5q0.5625 -0.515625 0.703125 -1.578125zm5.65625 1.78125l0.203125 1.25q-0.59375 0.125 -1.0625 0.125q-0.765625 0 -1.1875 -0.234375q-0.421875 -0.25 -0.59375 -0.640625q-0.171875 -0.40625 -0.171875 -1.671875l0 -4.765625l-1.03125 0l0 -1.09375l1.03125 0l0 -2.0625l1.40625 -0.84375l0 2.90625l1.40625 0l0 1.09375l-1.40625 0l0 4.84375q0 0.609375 0.0625 0.78125q0.078125 0.171875 0.25 0.28125q0.171875 0.09375 0.484375 0.09375q0.234375 0 0.609375 -0.0625zm1.3828125 -8.578125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm3.0234375 -4.15625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0zm8.3359375 -2.484375l1.390625 -0.21875q0.109375 0.84375 0.640625 1.296875q0.546875 0.4375 1.5 0.4375q0.96875 0 1.4375 -0.390625q0.46875 -0.40625 0.46875 -0.9375q0 -0.46875 -0.40625 -0.75q-0.296875 -0.1875 -1.4375 -0.46875q-1.546875 -0.390625 -2.15625 -0.671875q-0.59375 -0.296875 -0.90625 -0.796875q-0.296875 -0.5 -0.296875 -1.109375q0 -0.5625 0.25 -1.03125q0.25 -0.46875 0.6875 -0.78125q0.328125 -0.25 0.890625 -0.40625q0.578125 -0.171875 1.21875 -0.171875q0.984375 0 1.71875 0.28125q0.734375 0.28125 1.078125 0.765625q0.359375 0.46875 0.5 1.28125l-1.375 0.1875q-0.09375 -0.640625 -0.546875 -1.0q-0.453125 -0.359375 -1.265625 -0.359375q-0.96875 0 -1.390625 0.328125q-0.40625 0.3125 -0.40625 0.734375q0 0.28125 0.171875 0.5q0.171875 0.21875 0.53125 0.375q0.21875 0.078125 1.25 0.359375q1.484375 0.390625 2.078125 0.65625q0.59375 0.25 0.921875 0.734375q0.34375 0.484375 0.34375 1.203125q0 0.703125 -0.421875 1.328125q-0.40625 0.609375 -1.1875 0.953125q-0.765625 0.34375 -1.734375 0.34375q-1.625 0 -2.46875 -0.671875q-0.84375 -0.671875 -1.078125 -2.0z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m131.97372 179.3517l296.9449 0l0 106.92915l-296.9449 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m131.97372 179.3517l296.9449 0l0 106.92915l-296.9449 0z" fill-rule="evenodd"/><path fill="#000000" d="m144.0831 199.8717l0 -7.578125l-2.828125 0l0 -1.015625l6.8125 0l0 1.015625l-2.84375 0l0 7.578125l-1.140625 0zm4.3085938 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm6.046875 0l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm6.158203 -1.3125l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m241.94247 197.76233l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.173828 3.65625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.199219 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm3.5957031 -4.234375l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6191406 0l1.796875 -8.59375l1.0625 0l-1.796875 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m148.03622 215.85608l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923828 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm1.0371094 -6.4375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.2753906 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.955078 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m237.37997 218.8717l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm6.861328 -2.359375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.533203 0.34375l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm8.375 1.265625l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm3.9824219 2.4375l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm5.796875 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm7.3339844 -2.265625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.90625 -0.09375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125z" fill-rule="nonzero"/><path fill="#000000" d="m236.84872 235.26233l1.796875 -8.609375l0.984375 0l-0.1875 0.859375q0.546875 -0.5625 0.96875 -0.78125q0.4375 -0.21875 0.921875 -0.21875q0.890625 0 1.46875 0.65625q0.59375 0.640625 0.59375 1.84375q0 0.96875 -0.328125 1.765625q-0.3125 0.796875 -0.78125 1.296875q-0.46875 0.484375 -0.953125 0.71875q-0.484375 0.21875 -0.984375 0.21875q-1.125 0 -1.734375 -1.140625l-0.703125 3.390625l-1.0625 0zm2.09375 -4.9375q0 0.703125 0.109375 0.96875q0.140625 0.375 0.46875 0.609375q0.34375 0.234375 0.78125 0.234375q0.921875 0 1.484375 -1.015625q0.5625 -1.03125 0.5625 -2.109375q0 -0.78125 -0.390625 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.40625 0 -0.75 0.21875q-0.34375 0.21875 -0.640625 0.640625q-0.296875 0.421875 -0.5 1.046875q-0.1875 0.609375 -0.1875 1.0625zm5.298828 0.1875q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm6.4648438 -0.859375l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm4.435547 -0.015625l1.078125 -0.109375q-0.015625 0.234375 -0.015625 0.28125q0 0.375 0.1875 0.78125q0.203125 0.390625 0.53125 0.59375q0.34375 0.203125 0.71875 0.203125q0.5 0 1.0 -0.328125q0.515625 -0.34375 0.828125 -0.984375q0.3125 -0.65625 0.3125 -1.296875q0 -0.71875 -0.421875 -1.15625q-0.421875 -0.4375 -1.109375 -0.4375q-0.46875 0 -0.890625 0.234375q-0.40625 0.21875 -0.765625 0.65625l-0.921875 -0.0625l1.296875 -4.390625l4.203125 0l-0.21875 0.96875l-3.25 0l-0.65625 2.1875q0.375 -0.265625 0.75 -0.390625q0.390625 -0.140625 0.796875 -0.140625q0.984375 0 1.609375 0.65625q0.640625 0.640625 0.640625 1.78125q0 0.984375 -0.4375 1.828125q-0.4375 0.828125 -1.203125 1.28125q-0.75 0.4375 -1.625 0.4375q-0.75 0 -1.3125 -0.328125q-0.5625 -0.328125 -0.859375 -0.90625q-0.28125 -0.59375 -0.28125 -1.1875q0 -0.0625 0.015625 -0.171875zm8.533203 -2.265625q-0.46875 -0.265625 -0.71875 -0.671875q-0.234375 -0.421875 -0.234375 -0.890625q0 -0.78125 0.5625 -1.453125q0.75 -0.890625 2.03125 -0.890625q1.125 0 1.78125 0.609375q0.65625 0.609375 0.65625 1.484375q0 0.625 -0.359375 1.125q-0.34375 0.5 -1.09375 0.8125q0.515625 0.3125 0.734375 0.671875q0.28125 0.484375 0.28125 1.109375q0 1.15625 -0.8125 2.0625q-0.8125 0.90625 -2.171875 0.90625q-1.09375 0 -1.78125 -0.671875q-0.6875 -0.671875 -0.6875 -1.671875q0 -0.9375 0.484375 -1.609375q0.5 -0.6875 1.328125 -0.921875zm0.09375 -1.515625q0 0.546875 0.34375 0.890625q0.359375 0.328125 0.984375 0.328125q0.71875 0 1.171875 -0.4375q0.453125 -0.453125 0.453125 -1.09375q0 -0.546875 -0.359375 -0.890625q-0.34375 -0.34375 -0.96875 -0.34375q-0.46875 0 -0.859375 0.21875q-0.390625 0.21875 -0.578125 0.59375q-0.1875 0.375 -0.1875 0.734375zm-0.828125 4.140625q0 0.359375 0.171875 0.6875q0.171875 0.328125 0.515625 0.515625q0.359375 0.1875 0.8125 0.1875q0.859375 0 1.390625 -0.796875q0.421875 -0.640625 0.421875 -1.375q0 -0.59375 -0.390625 -0.96875q-0.390625 -0.390625 -1.015625 -0.390625q-0.796875 0 -1.359375 0.59375q-0.546875 0.59375 -0.546875 1.546875zm6.439453 2.09375q0.140625 -0.875 0.609375 -2.09375q0.546875 -1.46875 1.421875 -2.90625q0.890625 -1.453125 1.96875 -2.5l-4.28125 0l0.203125 -0.96875l5.484375 0l-0.203125 0.96875q-1.390625 1.21875 -2.4375 3.15625q-1.203125 2.234375 -1.65625 4.34375l-1.109375 0z" fill-rule="nonzero"/><path fill="#000000" d="m141.8956 252.8717l0 -8.59375l3.25 0q0.84375 0 1.296875 0.078125q0.640625 0.109375 1.0625 0.40625q0.4375 0.296875 0.6875 0.828125q0.265625 0.53125 0.265625 1.171875q0 1.09375 -0.703125 1.859375q-0.6875 0.75 -2.515625 0.75l-2.203125 0l0 3.5l-1.140625 0zm1.140625 -4.5l2.21875 0q1.109375 0 1.5625 -0.40625q0.46875 -0.421875 0.46875 -1.171875q0 -0.53125 -0.28125 -0.90625q-0.265625 -0.390625 -0.703125 -0.515625q-0.296875 -0.078125 -1.0625 -0.078125l-2.203125 0l0 3.078125zm6.7226562 4.5l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm3.6210938 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 5.5l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.861328 3.703125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm6.3085938 -0.9375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm1.0371094 -6.4375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm6.9160156 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z" fill-rule="nonzero"/><path fill="#000000" d="m237.47372 250.7467l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm10.140625 1.0q-1.109375 1.265625 -2.28125 1.265625q-0.71875 0 -1.171875 -0.40625q-0.4375 -0.421875 -0.4375 -1.015625q0 -0.390625 0.203125 -1.34375l0.75 -3.59375l1.0625 0l-0.84375 3.96875q-0.09375 0.5 -0.09375 0.78125q0 0.34375 0.203125 0.546875q0.21875 0.1875 0.640625 0.1875q0.4375 0 0.859375 -0.203125q0.4375 -0.21875 0.734375 -0.59375q0.3125 -0.375 0.5 -0.875q0.140625 -0.3125 0.296875 -1.125l0.5625 -2.6875l1.0625 0l-1.296875 6.21875l-0.984375 0l0.234375 -1.125zm2.4394531 1.125l1.78125 -8.59375l1.0625 0l-0.640625 3.078125q0.515625 -0.46875 0.921875 -0.65625q0.40625 -0.1875 0.859375 -0.1875q0.859375 0 1.4375 0.65625q0.59375 0.640625 0.59375 1.875q0 0.828125 -0.234375 1.515625q-0.234375 0.6875 -0.578125 1.15625q-0.34375 0.46875 -0.71875 0.75q-0.359375 0.265625 -0.75 0.40625q-0.390625 0.140625 -0.75 0.140625q-0.609375 0 -1.078125 -0.328125q-0.46875 -0.328125 -0.734375 -0.984375l-0.25 1.171875l-0.921875 0zm1.5625 -2.5625l0 0.203125q0 0.796875 0.375 1.21875q0.375 0.40625 0.953125 0.40625q0.546875 0 1.015625 -0.375q0.46875 -0.390625 0.765625 -1.21875q0.296875 -0.828125 0.296875 -1.53125q0 -0.78125 -0.375 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.5625 0 -1.046875 0.453125q-0.484375 0.4375 -0.8125 1.359375q-0.234375 0.6875 -0.234375 1.140625zm6.611328 -4.828125l0.25 -1.203125l1.046875 0l-0.25 1.203125l-1.046875 0zm-3.359375 9.734375l0.1875 -0.90625q0.359375 0.09375 0.640625 0.09375q0.34375 0 0.546875 -0.234375q0.203125 -0.21875 0.421875 -1.234375l1.296875 -6.28125l1.0625 0l-1.34375 6.46875q-0.25 1.21875 -0.578125 1.65625q-0.4375 0.578125 -1.234375 0.578125q-0.40625 0 -1.0 -0.140625zm9.087891 -4.453125l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm9.470703 1.390625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm4.1875 1.40625l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm8.185547 -4.5625l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm1.6835938 7.0l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm16.835938 -3.03125l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm7.9160156 1.234375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m237.37997 266.8717l1.78125 -8.59375l1.0625 0l-0.640625 3.078125q0.515625 -0.46875 0.921875 -0.65625q0.40625 -0.1875 0.859375 -0.1875q0.859375 0 1.4375 0.65625q0.59375 0.640625 0.59375 1.875q0 0.828125 -0.234375 1.515625q-0.234375 0.6875 -0.578125 1.15625q-0.34375 0.46875 -0.71875 0.75q-0.359375 0.265625 -0.75 0.40625q-0.390625 0.140625 -0.75 0.140625q-0.609375 0 -1.078125 -0.328125q-0.46875 -0.328125 -0.734375 -0.984375l-0.25 1.171875l-0.921875 0zm1.5625 -2.5625l0 0.203125q0 0.796875 0.375 1.21875q0.375 0.40625 0.953125 0.40625q0.546875 0 1.015625 -0.375q0.46875 -0.390625 0.765625 -1.21875q0.296875 -0.828125 0.296875 -1.53125q0 -0.78125 -0.375 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.5625 0 -1.046875 0.453125q-0.484375 0.4375 -0.8125 1.359375q-0.234375 0.6875 -0.234375 1.140625zm5.298828 0.203125q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm9.626953 1.5625q-0.90625 1.046875 -1.890625 1.046875q-0.890625 0 -1.484375 -0.65625q-0.578125 -0.65625 -0.578125 -1.890625q0 -1.140625 0.453125 -2.078125q0.46875 -0.9375 1.171875 -1.40625q0.703125 -0.46875 1.40625 -0.46875q1.15625 0 1.75 1.109375l0.703125 -3.34375l1.046875 0l-1.78125 8.59375l-0.984375 0l0.1875 -0.90625zm-2.90625 -1.703125q0 0.65625 0.125 1.03125q0.140625 0.375 0.4375 0.625q0.3125 0.25 0.75 0.25q0.71875 0 1.3125 -0.75q0.78125 -1.0 0.78125 -2.46875q0 -0.734375 -0.390625 -1.140625q-0.390625 -0.421875 -0.96875 -0.421875q-0.375 0 -0.703125 0.171875q-0.3125 0.15625 -0.625 0.5625q-0.296875 0.40625 -0.515625 1.046875q-0.203125 0.625 -0.203125 1.09375zm4.986328 5.015625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm16.083984 -7.453125l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm8.185547 -4.5625l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0zm10.4296875 -3.59375q0.453125 0 0.78125 -0.234375q0.34375 -0.234375 0.546875 -0.703125q0.171875 -0.34375 0.375 -1.3125q0.21875 -0.984375 0.34375 -1.34375q0.15625 -0.484375 0.375 -0.78125q0.21875 -0.3125 0.46875 -0.46875q0.25 -0.171875 0.625 -0.25q0.203125 -0.046875 0.671875 -0.046875l0.328125 0l-0.203125 0.921875l-0.171875 0q-0.5 0 -0.71875 0.125q-0.21875 0.109375 -0.34375 0.375q-0.109375 0.25 -0.3125 1.125q-0.234375 1.171875 -0.453125 1.6875q-0.203125 0.5 -0.53125 0.828125q-0.3125 0.3125 -0.90625 0.5625q0.421875 0.203125 0.625 0.53125q0.203125 0.328125 0.203125 0.875q0 0.453125 -0.265625 1.671875q-0.1875 0.859375 -0.1875 1.078125q0 0.21875 0.09375 0.34375q0.0625 0.109375 0.21875 0.15625q0.171875 0.0625 0.703125 0.0625l-0.1875 0.921875l-0.328125 0q-0.546875 0 -0.765625 -0.078125q-0.34375 -0.125 -0.546875 -0.40625q-0.1875 -0.265625 -0.1875 -0.734375q0 -0.390625 0.265625 -1.671875q0.203125 -0.96875 0.203125 -1.3125q0 -0.46875 -0.234375 -0.703125q-0.234375 -0.25 -0.6875 -0.25l0.203125 -0.96875zm4.0078125 0q0.453125 0 0.78125 -0.234375q0.34375 -0.234375 0.546875 -0.703125q0.171875 -0.34375 0.375 -1.3125q0.21875 -0.984375 0.34375 -1.34375q0.15625 -0.484375 0.375 -0.78125q0.21875 -0.3125 0.46875 -0.46875q0.25 -0.171875 0.625 -0.25q0.203125 -0.046875 0.671875 -0.046875l0.328125 0l-0.203125 0.921875l-0.171875 0q-0.5 0 -0.71875 0.125q-0.21875 0.109375 -0.34375 0.375q-0.109375 0.25 -0.3125 1.125q-0.234375 1.171875 -0.453125 1.6875q-0.203125 0.5 -0.53125 0.828125q-0.3125 0.3125 -0.90625 0.5625q0.421875 0.203125 0.625 0.53125q0.203125 0.328125 0.203125 0.875q0 0.453125 -0.265625 1.671875q-0.1875 0.859375 -0.1875 1.078125q0 0.21875 0.09375 0.34375q0.0625 0.109375 0.21875 0.15625q0.171875 0.0625 0.703125 0.0625l-0.1875 0.921875l-0.328125 0q-0.546875 0 -0.765625 -0.078125q-0.34375 -0.125 -0.546875 -0.40625q-0.1875 -0.265625 -0.1875 -0.734375q0 -0.390625 0.265625 -1.671875q0.203125 -0.96875 0.203125 -1.3125q0 -0.46875 -0.234375 -0.703125q-0.234375 -0.25 -0.6875 -0.25l0.203125 -0.96875zm3.6796875 1.46875l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm10.46875 0.015625l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.189453 3.65625l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm5.5585938 0l-1.015625 -6.21875l1.03125 0l0.53125 3.421875q0.09375 0.5625 0.21875 1.828125q0.296875 -0.65625 0.78125 -1.515625l2.078125 -3.734375l1.109375 0l-3.5625 6.21875l-1.171875 0zm9.0 -2.109375l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.189453 3.65625l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm7.2929688 -3.59375l-0.203125 0.96875q-0.4375 0 -0.78125 0.234375q-0.328125 0.234375 -0.515625 0.640625q-0.1875 0.40625 -0.40625 1.390625q-0.21875 0.984375 -0.3125 1.3125q-0.171875 0.5 -0.390625 0.796875q-0.21875 0.3125 -0.484375 0.46875q-0.25 0.171875 -0.609375 0.265625q-0.203125 0.046875 -0.6875 0.046875l-0.328125 0l0.203125 -0.921875l0.1875 0q0.5 0 0.71875 -0.125q0.21875 -0.125 0.359375 -0.421875q0.09375 -0.21875 0.28125 -1.09375q0.234375 -1.109375 0.40625 -1.546875q0.234375 -0.59375 0.59375 -0.953125q0.359375 -0.359375 0.890625 -0.578125q-0.453125 -0.234375 -0.640625 -0.546875q-0.171875 -0.3125 -0.171875 -0.859375q0 -0.453125 0.25 -1.671875q0.1875 -0.84375 0.1875 -1.078125q0 -0.296875 -0.15625 -0.421875q-0.15625 -0.125 -0.671875 -0.125l-0.1875 0l0.203125 -0.921875l0.328125 0q0.546875 0 0.765625 0.078125q0.34375 0.109375 0.53125 0.390625q0.203125 0.265625 0.203125 0.734375q0 0.390625 -0.203125 1.296875q-0.28125 1.359375 -0.28125 1.6875q0 0.46875 0.234375 0.71875q0.234375 0.234375 0.6875 0.234375zm4.0078125 0l-0.203125 0.96875q-0.4375 0 -0.78125 0.234375q-0.328125 0.234375 -0.515625 0.640625q-0.1875 0.40625 -0.40625 1.390625q-0.21875 0.984375 -0.3125 1.3125q-0.171875 0.5 -0.390625 0.796875q-0.21875 0.3125 -0.484375 0.46875q-0.25 0.171875 -0.609375 0.265625q-0.203125 0.046875 -0.6875 0.046875l-0.328125 0l0.203125 -0.921875l0.1875 0q0.5 0 0.71875 -0.125q0.21875 -0.125 0.359375 -0.421875q0.09375 -0.21875 0.28125 -1.09375q0.234375 -1.109375 0.40625 -1.546875q0.234375 -0.59375 0.59375 -0.953125q0.359375 -0.359375 0.890625 -0.578125q-0.453125 -0.234375 -0.640625 -0.546875q-0.171875 -0.3125 -0.171875 -0.859375q0 -0.453125 0.25 -1.671875q0.1875 -0.84375 0.1875 -1.078125q0 -0.296875 -0.15625 -0.421875q-0.15625 -0.125 -0.671875 -0.125l-0.1875 0l0.203125 -0.921875l0.328125 0q0.546875 0 0.765625 0.078125q0.34375 0.109375 0.53125 0.390625q0.203125 0.265625 0.203125 0.734375q0 0.390625 -0.203125 1.296875q-0.28125 1.359375 -0.28125 1.6875q0 0.46875 0.234375 0.71875q0.234375 0.234375 0.6875 0.234375zm5.544922 -3.796875l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m673.95276 96.70677l283.68506 0l0 106.92914l-283.68506 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m673.95276 96.70677l283.68506 0l0 106.92914l-283.68506 0z" fill-rule="evenodd"/><path fill="#000000" d="m686.06213 117.22677l0 -7.578125l-2.828125 0l0 -1.015625l6.8125 0l0 1.015625l-2.84375 0l0 7.578125l-1.140625 0zm4.3085938 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm6.046875 0l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm6.158203 -1.3125l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m783.9215 115.11739l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.173828 3.65625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.199219 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm3.5957031 -4.234375l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6191406 0l1.796875 -8.59375l1.0625 0l-1.796875 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m690.01526 133.21115l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.5156326 2.09375 -0.5156326q1.28125 0 2.15625 0.6562576q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923828 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.6250076l0 2.1718826l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm1.0371094 -6.4375l0 -1.2187576l1.0625 0l0 1.2187576l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.2753906 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.955078 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m779.359 136.22678l1.78125 -8.593758l1.0625 0l-0.6875 3.2968826q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm6.861328 -2.359375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.533203 0.34375l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm8.375 1.265625l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.7343826l-0.46875 2.2656326l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm3.9824219 2.4375l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm5.796875 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm7.3339844 -2.265625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.90625 -0.09375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125z" fill-rule="nonzero"/><path fill="#000000" d="m778.82776 152.6174l1.796875 -8.609375l0.984375 0l-0.1875 0.859375q0.546875 -0.5625 0.96875 -0.78125q0.4375 -0.21875 0.921875 -0.21875q0.890625 0 1.46875 0.65625q0.59375 0.640625 0.59375 1.84375q0 0.96875 -0.328125 1.765625q-0.3125 0.796875 -0.78125 1.296875q-0.46875 0.484375 -0.953125 0.71875q-0.484375 0.21875 -0.984375 0.21875q-1.125 0 -1.734375 -1.140625l-0.703125 3.390625l-1.0625 0zm2.09375 -4.9375q0 0.703125 0.109375 0.96875q0.140625 0.375 0.46875 0.609375q0.34375 0.234375 0.78125 0.234375q0.921875 0 1.484375 -1.015625q0.5625 -1.03125 0.5625 -2.109375q0 -0.78125 -0.390625 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.40625 0 -0.75 0.21875q-0.34375 0.21875 -0.640625 0.640625q-0.296875 0.421875 -0.5 1.046875q-0.1875 0.609375 -0.1875 1.0625zm5.298828 0.1875q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm6.4648438 -0.859375l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm4.435547 -0.015625l1.078125 -0.109375q-0.015625 0.234375 -0.015625 0.28125q0 0.375 0.1875 0.78125q0.203125 0.390625 0.53125 0.59375q0.34375 0.203125 0.71875 0.203125q0.5 0 1.0 -0.328125q0.515625 -0.34375 0.828125 -0.984375q0.3125 -0.65625 0.3125 -1.296875q0 -0.71875 -0.421875 -1.15625q-0.421875 -0.4375 -1.109375 -0.4375q-0.46875 0 -0.890625 0.234375q-0.40625 0.21875 -0.765625 0.65625l-0.921875 -0.0625l1.296875 -4.390625l4.203125 0l-0.21875 0.96875l-3.25 0l-0.65625 2.1875q0.375 -0.265625 0.75 -0.390625q0.390625 -0.140625 0.796875 -0.140625q0.984375 0 1.609375 0.65625q0.640625 0.640625 0.640625 1.78125q0 0.984375 -0.4375 1.828125q-0.4375 0.828125 -1.203125 1.28125q-0.75 0.4375 -1.625 0.4375q-0.75 0 -1.3125 -0.328125q-0.5625 -0.328125 -0.859375 -0.90625q-0.28125 -0.59375 -0.28125 -1.1875q0 -0.0625 0.015625 -0.171875zm8.533203 -2.265625q-0.46875 -0.265625 -0.71875 -0.671875q-0.234375 -0.421875 -0.234375 -0.890625q0 -0.78125 0.5625 -1.453125q0.75 -0.890625 2.03125 -0.890625q1.125 0 1.78125 0.609375q0.65625 0.609375 0.65625 1.484375q0 0.625 -0.359375 1.125q-0.34375 0.5 -1.09375 0.8125q0.515625 0.3125 0.734375 0.671875q0.28125 0.484375 0.28125 1.109375q0 1.15625 -0.8125 2.0625q-0.8125 0.90625 -2.171875 0.90625q-1.09375 0 -1.78125 -0.671875q-0.6875 -0.671875 -0.6875 -1.671875q0 -0.9375 0.484375 -1.609375q0.5 -0.6875 1.328125 -0.921875zm0.09375 -1.515625q0 0.546875 0.34375 0.890625q0.359375 0.328125 0.984375 0.328125q0.71875 0 1.171875 -0.4375q0.453125 -0.453125 0.453125 -1.09375q0 -0.546875 -0.359375 -0.890625q-0.34375 -0.34375 -0.96875 -0.34375q-0.46875 0 -0.859375 0.21875q-0.390625 0.21875 -0.578125 0.59375q-0.1875 0.375 -0.1875 0.734375zm-0.828125 4.140625q0 0.359375 0.171875 0.6875q0.171875 0.328125 0.515625 0.515625q0.359375 0.1875 0.8125 0.1875q0.859375 0 1.390625 -0.796875q0.421875 -0.640625 0.421875 -1.375q0 -0.59375 -0.390625 -0.96875q-0.390625 -0.390625 -1.015625 -0.390625q-0.796875 0 -1.359375 0.59375q-0.546875 0.59375 -0.546875 1.546875zm6.439453 2.09375q0.140625 -0.875 0.609375 -2.09375q0.546875 -1.46875 1.421875 -2.90625q0.890625 -1.453125 1.96875 -2.5l-4.28125 0l0.203125 -0.96875l5.484375 0l-0.203125 0.96875q-1.390625 1.21875 -2.4375 3.15625q-1.203125 2.234375 -1.65625 4.34375l-1.109375 0z" fill-rule="nonzero"/><path fill="#000000" d="m683.87463 170.22676l0 -8.59375l3.25 0q0.84375 0 1.296875 0.078125q0.640625 0.109375 1.0625 0.40625q0.4375 0.296875 0.6875 0.828125q0.265625 0.53125 0.265625 1.171875q0 1.09375 -0.703125 1.859375q-0.6875 0.75 -2.515625 0.75l-2.203125 0l0 3.5l-1.140625 0zm1.140625 -4.5l2.21875 0q1.109375 0 1.5625 -0.40625q0.46875 -0.421875 0.46875 -1.171875q0 -0.53125 -0.28125 -0.90625q-0.265625 -0.390625 -0.703125 -0.515625q-0.296875 -0.078125 -1.0625 -0.078125l-2.203125 0l0 3.078125zm6.7226562 4.5l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm3.6210938 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 5.5l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.861328 3.703125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm6.3085938 -0.9375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm1.0371094 -6.4375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm6.9160156 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z" fill-rule="nonzero"/><path fill="#000000" d="m779.45276 168.10176l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm10.140625 1.0q-1.109375 1.265625 -2.28125 1.265625q-0.71875 0 -1.171875 -0.40625q-0.4375 -0.421875 -0.4375 -1.015625q0 -0.390625 0.203125 -1.34375l0.75 -3.59375l1.0625 0l-0.84375 3.96875q-0.09375 0.5 -0.09375 0.78125q0 0.34375 0.203125 0.546875q0.21875 0.1875 0.640625 0.1875q0.4375 0 0.859375 -0.203125q0.4375 -0.21875 0.734375 -0.59375q0.3125 -0.375 0.5 -0.875q0.140625 -0.3125 0.296875 -1.125l0.5625 -2.6875l1.0625 0l-1.296875 6.21875l-0.984375 0l0.234375 -1.125zm2.4394531 1.125l1.78125 -8.59375l1.0625 0l-0.640625 3.078125q0.515625 -0.46875 0.921875 -0.65625q0.40625 -0.1875 0.859375 -0.1875q0.859375 0 1.4375 0.65625q0.59375 0.640625 0.59375 1.875q0 0.828125 -0.234375 1.515625q-0.234375 0.6875 -0.578125 1.15625q-0.34375 0.46875 -0.71875 0.75q-0.359375 0.265625 -0.75 0.40625q-0.390625 0.140625 -0.75 0.140625q-0.609375 0 -1.078125 -0.328125q-0.46875 -0.328125 -0.734375 -0.984375l-0.25 1.171875l-0.921875 0zm1.5625 -2.5625l0 0.203125q0 0.796875 0.375 1.21875q0.375 0.40625 0.953125 0.40625q0.546875 0 1.015625 -0.375q0.46875 -0.390625 0.765625 -1.21875q0.296875 -0.828125 0.296875 -1.53125q0 -0.78125 -0.375 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.5625 0 -1.046875 0.453125q-0.484375 0.4375 -0.8125 1.359375q-0.234375 0.6875 -0.234375 1.140625zm6.611328 -4.828125l0.25 -1.203125l1.046875 0l-0.25 1.203125l-1.046875 0zm-3.359375 9.734375l0.1875 -0.90625q0.359375 0.09375 0.640625 0.09375q0.34375 0 0.546875 -0.234375q0.203125 -0.21875 0.421875 -1.234375l1.296875 -6.28125l1.0625 0l-1.34375 6.46875q-0.25 1.21875 -0.578125 1.65625q-0.4375 0.578125 -1.234375 0.578125q-0.40625 0 -1.0 -0.140625zm9.087891 -4.453125l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm9.470703 1.390625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm4.1875 1.40625l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm8.185547 -4.5625l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm1.6835938 7.0l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm16.835938 -3.03125l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm7.9160156 1.234375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m779.359 184.22676l1.78125 -8.59375l1.0625 0l-0.640625 3.078125q0.515625 -0.46875 0.921875 -0.65625q0.40625 -0.1875 0.859375 -0.1875q0.859375 0 1.4375 0.65625q0.59375 0.640625 0.59375 1.875q0 0.828125 -0.234375 1.515625q-0.234375 0.6875 -0.578125 1.15625q-0.34375 0.46875 -0.71875 0.75q-0.359375 0.265625 -0.75 0.40625q-0.390625 0.140625 -0.75 0.140625q-0.609375 0 -1.078125 -0.328125q-0.46875 -0.328125 -0.734375 -0.984375l-0.25 1.171875l-0.921875 0zm1.5625 -2.5625l0 0.203125q0 0.796875 0.375 1.21875q0.375 0.40625 0.953125 0.40625q0.546875 0 1.015625 -0.375q0.46875 -0.390625 0.765625 -1.21875q0.296875 -0.828125 0.296875 -1.53125q0 -0.78125 -0.375 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.5625 0 -1.046875 0.453125q-0.484375 0.4375 -0.8125 1.359375q-0.234375 0.6875 -0.234375 1.140625zm5.298828 0.203125q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm9.626953 1.5625q-0.90625 1.046875 -1.890625 1.046875q-0.890625 0 -1.484375 -0.65625q-0.578125 -0.65625 -0.578125 -1.890625q0 -1.140625 0.453125 -2.078125q0.46875 -0.9375 1.171875 -1.40625q0.703125 -0.46875 1.40625 -0.46875q1.15625 0 1.75 1.109375l0.703125 -3.34375l1.046875 0l-1.78125 8.59375l-0.984375 0l0.1875 -0.90625zm-2.90625 -1.703125q0 0.65625 0.125 1.03125q0.140625 0.375 0.4375 0.625q0.3125 0.25 0.75 0.25q0.71875 0 1.3125 -0.75q0.78125 -1.0 0.78125 -2.46875q0 -0.734375 -0.390625 -1.140625q-0.390625 -0.421875 -0.96875 -0.421875q-0.375 0 -0.703125 0.171875q-0.3125 0.15625 -0.625 0.5625q-0.296875 0.40625 -0.515625 1.046875q-0.203125 0.625 -0.203125 1.09375zm4.986328 5.015625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm16.083984 -7.453125l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm8.185547 -4.5625l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m867.9977 173.36348l28.02539 0l0 13.798828l-28.02539 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m869.91956 184.22676l-2.28125 0l3.78125 -4.34375l-2.125 -4.25l1.84375 0l0.84375 1.609375q0.03125 0.09375 0.5625 1.1875q0.046875 0.0625 0.078125 0.171875q0.71875 -0.875 1.203125 -1.4375l1.359375 -1.53125l2.21875 0l-3.9375 4.53125l2.125 4.0625l-2.0 0l-0.640625 -1.296875q-0.5 -1.03125 -0.671875 -1.46875q-0.21875 0.34375 -1.171875 1.421875l-1.1875 1.34375zm7.5039062 -4.9375l0.3125 -1.5q2.1875 -0.953125 3.375 -2.203125l1.015625 0l-1.8125 8.640625l-1.6875 0l1.25 -5.953125q-0.53125 0.34375 -1.21875 0.625q-0.6875 0.28125 -1.234375 0.390625zm8.423828 -0.96875l-1.640625 -0.25q0.1875 -1.203125 0.921875 -1.828125q0.734375 -0.640625 1.828125 -0.640625q1.203125 0 1.875 0.65625q0.6875 0.640625 0.6875 1.640625q0 0.515625 -0.21875 1.03125q-0.203125 0.515625 -0.671875 1.078125q-0.34375 0.390625 -1.140625 1.125q-0.78125 0.71875 -1.046875 1.0q-0.25 0.265625 -0.484375 0.5625l2.984375 0l-0.3125 1.53125l-5.21875 0q0.078125 -0.640625 0.359375 -1.203125q0.296875 -0.578125 0.71875 -1.0625q0.4375 -0.5 1.59375 -1.59375q0.875 -0.828125 1.09375 -1.078125q0.421875 -0.453125 0.5625 -0.78125q0.15625 -0.34375 0.15625 -0.609375q0 -0.4375 -0.25 -0.703125q-0.25 -0.28125 -0.65625 -0.28125q-0.390625 0 -0.6875 0.3125q-0.296875 0.296875 -0.453125 1.09375zm4.111328 3.65625l1.609375 -0.1875q0.109375 0.6875 0.359375 0.953125q0.265625 0.265625 0.703125 0.265625q0.5625 0 0.9375 -0.390625q0.390625 -0.390625 0.390625 -1.0q0 -0.515625 -0.3125 -0.828125q-0.296875 -0.3125 -0.8125 -0.3125q-0.125 0 -0.265625 0.03125l0.28125 -1.40625q0.09375 0.015625 0.171875 0.015625q0.671875 0 1.015625 -0.34375q0.359375 -0.34375 0.359375 -0.921875q0 -0.421875 -0.234375 -0.65625q-0.234375 -0.25 -0.625 -0.25q-0.359375 0 -0.65625 0.265625q-0.28125 0.265625 -0.453125 0.921875l-1.53125 -0.3125q0.296875 -0.953125 0.8125 -1.453125q0.796875 -0.765625 1.953125 -0.765625q1.171875 0 1.78125 0.65625q0.625 0.65625 0.625 1.578125q0 0.671875 -0.390625 1.21875q-0.390625 0.53125 -1.078125 0.78125q0.546875 0.28125 0.796875 0.75q0.265625 0.453125 0.265625 0.953125q0 0.953125 -0.671875 1.75q-0.921875 1.078125 -2.375 1.078125q-1.09375 0 -1.796875 -0.625q-0.703125 -0.625 -0.859375 -1.765625z" fill-rule="nonzero"/><path fill="#000000" d="m901.2633 176.83614l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m867.9977 185.00676l28.02539 0l0 1.0800018l-28.02539 0l0 -1.0800018z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m673.95276 213.52304l283.68506 0l0 106.92914l-283.68506 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m673.95276 213.52304l283.68506 0l0 106.92914l-283.68506 0z" fill-rule="evenodd"/><path fill="#000000" d="m686.06213 234.04305l0 -7.578125l-2.828125 0l0 -1.015625l6.8125 0l0 1.015625l-2.84375 0l0 7.578125l-1.140625 0zm4.3085938 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm6.046875 0l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm6.158203 -1.3125l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m783.9215 231.93367l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.173828 3.65625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.199219 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm3.5957031 -4.234375l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6191406 0l1.796875 -8.59375l1.0625 0l-1.796875 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m690.01526 250.02742l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923828 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm1.0371094 -6.4375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.2753906 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.955078 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m779.359 253.04305l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm6.861328 -2.359375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.533203 0.34375l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm8.375 1.265625l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm3.9824219 2.4375l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm5.796875 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm7.3339844 -2.265625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.90625 -0.09375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125z" fill-rule="nonzero"/><path fill="#000000" d="m778.82776 269.43365l1.796875 -8.609375l0.984375 0l-0.1875 0.859375q0.546875 -0.5625 0.96875 -0.78125q0.4375 -0.21875 0.921875 -0.21875q0.890625 0 1.46875 0.65625q0.59375 0.640625 0.59375 1.84375q0 0.96875 -0.328125 1.765625q-0.3125 0.796875 -0.78125 1.296875q-0.46875 0.484375 -0.953125 0.71875q-0.484375 0.21875 -0.984375 0.21875q-1.125 0 -1.734375 -1.140625l-0.703125 3.390625l-1.0625 0zm2.09375 -4.9375q0 0.703125 0.109375 0.96875q0.140625 0.375 0.46875 0.609375q0.34375 0.234375 0.78125 0.234375q0.921875 0 1.484375 -1.015625q0.5625 -1.03125 0.5625 -2.109375q0 -0.78125 -0.390625 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.40625 0 -0.75 0.21875q-0.34375 0.21875 -0.640625 0.640625q-0.296875 0.421875 -0.5 1.046875q-0.1875 0.609375 -0.1875 1.0625zm5.298828 0.1875q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm6.4648438 -0.859375l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm4.435547 -0.015625l1.078125 -0.109375q-0.015625 0.234375 -0.015625 0.28125q0 0.375 0.1875 0.78125q0.203125 0.390625 0.53125 0.59375q0.34375 0.203125 0.71875 0.203125q0.5 0 1.0 -0.328125q0.515625 -0.34375 0.828125 -0.984375q0.3125 -0.65625 0.3125 -1.296875q0 -0.71875 -0.421875 -1.15625q-0.421875 -0.4375 -1.109375 -0.4375q-0.46875 0 -0.890625 0.234375q-0.40625 0.21875 -0.765625 0.65625l-0.921875 -0.0625l1.296875 -4.390625l4.203125 0l-0.21875 0.96875l-3.25 0l-0.65625 2.1875q0.375 -0.265625 0.75 -0.390625q0.390625 -0.140625 0.796875 -0.140625q0.984375 0 1.609375 0.65625q0.640625 0.640625 0.640625 1.78125q0 0.984375 -0.4375 1.828125q-0.4375 0.828125 -1.203125 1.28125q-0.75 0.4375 -1.625 0.4375q-0.75 0 -1.3125 -0.328125q-0.5625 -0.328125 -0.859375 -0.90625q-0.28125 -0.59375 -0.28125 -1.1875q0 -0.0625 0.015625 -0.171875zm8.533203 -2.265625q-0.46875 -0.265625 -0.71875 -0.671875q-0.234375 -0.421875 -0.234375 -0.890625q0 -0.78125 0.5625 -1.453125q0.75 -0.890625 2.03125 -0.890625q1.125 0 1.78125 0.609375q0.65625 0.609375 0.65625 1.484375q0 0.625 -0.359375 1.125q-0.34375 0.5 -1.09375 0.8125q0.515625 0.3125 0.734375 0.671875q0.28125 0.484375 0.28125 1.109375q0 1.15625 -0.8125 2.0625q-0.8125 0.90625 -2.171875 0.90625q-1.09375 0 -1.78125 -0.671875q-0.6875 -0.671875 -0.6875 -1.671875q0 -0.9375 0.484375 -1.609375q0.5 -0.6875 1.328125 -0.921875zm0.09375 -1.515625q0 0.546875 0.34375 0.890625q0.359375 0.328125 0.984375 0.328125q0.71875 0 1.171875 -0.4375q0.453125 -0.453125 0.453125 -1.09375q0 -0.546875 -0.359375 -0.890625q-0.34375 -0.34375 -0.96875 -0.34375q-0.46875 0 -0.859375 0.21875q-0.390625 0.21875 -0.578125 0.59375q-0.1875 0.375 -0.1875 0.734375zm-0.828125 4.140625q0 0.359375 0.171875 0.6875q0.171875 0.328125 0.515625 0.515625q0.359375 0.1875 0.8125 0.1875q0.859375 0 1.390625 -0.796875q0.421875 -0.640625 0.421875 -1.375q0 -0.59375 -0.390625 -0.96875q-0.390625 -0.390625 -1.015625 -0.390625q-0.796875 0 -1.359375 0.59375q-0.546875 0.59375 -0.546875 1.546875zm6.439453 2.09375q0.140625 -0.875 0.609375 -2.09375q0.546875 -1.46875 1.421875 -2.90625q0.890625 -1.453125 1.96875 -2.5l-4.28125 0l0.203125 -0.96875l5.484375 0l-0.203125 0.96875q-1.390625 1.21875 -2.4375 3.15625q-1.203125 2.234375 -1.65625 4.34375l-1.109375 0z" fill-rule="nonzero"/><path fill="#000000" d="m683.87463 287.04303l0 -8.59375l3.25 0q0.84375 0 1.296875 0.078125q0.640625 0.109375 1.0625 0.40625q0.4375 0.296875 0.6875 0.828125q0.265625 0.53125 0.265625 1.171875q0 1.09375 -0.703125 1.859375q-0.6875 0.75 -2.515625 0.75l-2.203125 0l0 3.5l-1.140625 0zm1.140625 -4.5l2.21875 0q1.109375 0 1.5625 -0.40625q0.46875 -0.421875 0.46875 -1.171875q0 -0.53125 -0.28125 -0.90625q-0.265625 -0.390625 -0.703125 -0.515625q-0.296875 -0.078125 -1.0625 -0.078125l-2.203125 0l0 3.078125zm6.7226562 4.5l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm3.6210938 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 5.5l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.861328 3.703125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm6.3085938 -0.9375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm1.0371094 -6.4375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm6.9160156 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z" fill-rule="nonzero"/><path fill="#000000" d="m779.45276 284.91803l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm10.140625 1.0q-1.109375 1.265625 -2.28125 1.265625q-0.71875 0 -1.171875 -0.40625q-0.4375 -0.421875 -0.4375 -1.015625q0 -0.390625 0.203125 -1.34375l0.75 -3.59375l1.0625 0l-0.84375 3.96875q-0.09375 0.5 -0.09375 0.78125q0 0.34375 0.203125 0.546875q0.21875 0.1875 0.640625 0.1875q0.4375 0 0.859375 -0.203125q0.4375 -0.21875 0.734375 -0.59375q0.3125 -0.375 0.5 -0.875q0.140625 -0.3125 0.296875 -1.125l0.5625 -2.6875l1.0625 0l-1.296875 6.21875l-0.984375 0l0.234375 -1.125zm2.4394531 1.125l1.78125 -8.59375l1.0625 0l-0.640625 3.078125q0.515625 -0.46875 0.921875 -0.65625q0.40625 -0.1875 0.859375 -0.1875q0.859375 0 1.4375 0.65625q0.59375 0.640625 0.59375 1.875q0 0.828125 -0.234375 1.515625q-0.234375 0.6875 -0.578125 1.15625q-0.34375 0.46875 -0.71875 0.75q-0.359375 0.265625 -0.75 0.40625q-0.390625 0.140625 -0.75 0.140625q-0.609375 0 -1.078125 -0.328125q-0.46875 -0.328125 -0.734375 -0.984375l-0.25 1.171875l-0.921875 0zm1.5625 -2.5625l0 0.203125q0 0.796875 0.375 1.21875q0.375 0.40625 0.953125 0.40625q0.546875 0 1.015625 -0.375q0.46875 -0.390625 0.765625 -1.21875q0.296875 -0.828125 0.296875 -1.53125q0 -0.78125 -0.375 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.5625 0 -1.046875 0.453125q-0.484375 0.4375 -0.8125 1.359375q-0.234375 0.6875 -0.234375 1.140625zm6.611328 -4.828125l0.25 -1.203125l1.046875 0l-0.25 1.203125l-1.046875 0zm-3.359375 9.734375l0.1875 -0.90625q0.359375 0.09375 0.640625 0.09375q0.34375 0 0.546875 -0.234375q0.203125 -0.21875 0.421875 -1.234375l1.296875 -6.28125l1.0625 0l-1.34375 6.46875q-0.25 1.21875 -0.578125 1.65625q-0.4375 0.578125 -1.234375 0.578125q-0.40625 0 -1.0 -0.140625zm9.087891 -4.453125l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm9.470703 1.390625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm4.1875 1.40625l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm8.185547 -4.5625l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm1.6835938 7.0l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm16.835938 -3.03125l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm7.9160156 1.234375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m779.359 301.04303l1.78125 -8.59375l1.0625 0l-0.640625 3.078125q0.515625 -0.46875 0.921875 -0.65625q0.40625 -0.1875 0.859375 -0.1875q0.859375 0 1.4375 0.65625q0.59375 0.640625 0.59375 1.875q0 0.828125 -0.234375 1.515625q-0.234375 0.6875 -0.578125 1.15625q-0.34375 0.46875 -0.71875 0.75q-0.359375 0.265625 -0.75 0.40625q-0.390625 0.140625 -0.75 0.140625q-0.609375 0 -1.078125 -0.328125q-0.46875 -0.328125 -0.734375 -0.984375l-0.25 1.171875l-0.921875 0zm1.5625 -2.5625l0 0.203125q0 0.796875 0.375 1.21875q0.375 0.40625 0.953125 0.40625q0.546875 0 1.015625 -0.375q0.46875 -0.390625 0.765625 -1.21875q0.296875 -0.828125 0.296875 -1.53125q0 -0.78125 -0.375 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.5625 0 -1.046875 0.453125q-0.484375 0.4375 -0.8125 1.359375q-0.234375 0.6875 -0.234375 1.140625zm5.298828 0.203125q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm9.626953 1.5625q-0.90625 1.046875 -1.890625 1.046875q-0.890625 0 -1.484375 -0.65625q-0.578125 -0.65625 -0.578125 -1.890625q0 -1.140625 0.453125 -2.078125q0.46875 -0.9375 1.171875 -1.40625q0.703125 -0.46875 1.40625 -0.46875q1.15625 0 1.75 1.109375l0.703125 -3.34375l1.046875 0l-1.78125 8.59375l-0.984375 0l0.1875 -0.90625zm-2.90625 -1.703125q0 0.65625 0.125 1.03125q0.140625 0.375 0.4375 0.625q0.3125 0.25 0.75 0.25q0.71875 0 1.3125 -0.75q0.78125 -1.0 0.78125 -2.46875q0 -0.734375 -0.390625 -1.140625q-0.390625 -0.421875 -0.96875 -0.421875q-0.375 0 -0.703125 0.171875q-0.3125 0.15625 -0.625 0.5625q-0.296875 0.40625 -0.515625 1.046875q-0.203125 0.625 -0.203125 1.09375zm4.986328 5.015625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm16.083984 -7.453125l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm8.185547 -4.5625l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m867.9977 290.17975l28.02539 0l0 13.798828l-28.02539 0l0 -13.798828z" fill-rule="nonzero"/><path fill="#000000" d="m872.8102 301.04303l-1.765625 0l0.703125 -3.34375l-2.375 -5.25l1.890625 0l1.0625 2.421875q0.375 0.875 0.5 1.265625q0.21875 -0.375 0.515625 -0.8125l0.8125 -1.125l1.203125 -1.75l2.0625 0l-3.9375 5.40625l-0.671875 3.1875zm6.8945312 -1.75l-3.375 0l0.296875 -1.40625l4.640625 -5.4375l1.453125 0l-1.125 5.40625l1.046875 0l-0.3125 1.4375l-1.03125 0l-0.375 1.75l-1.578125 0l0.359375 -1.75zm0.3125 -1.4375l0.546875 -2.671875l-2.265625 2.671875l1.71875 0zm3.4238281 0.71875l1.625 -0.140625q0 0.125 0 0.171875q0 0.609375 0.265625 0.90625q0.265625 0.296875 0.703125 0.296875q0.59375 0 1.078125 -0.5625q0.484375 -0.578125 0.484375 -1.59375q0 -0.59375 -0.28125 -0.890625q-0.265625 -0.3125 -0.71875 -0.3125q-0.328125 0 -0.640625 0.15625q-0.296875 0.140625 -0.59375 0.453125l-1.375 -0.09375l1.296875 -4.390625l4.328125 0l-0.328125 1.53125l-2.84375 0l-0.390625 1.328125q0.234375 -0.109375 0.484375 -0.171875q0.25 -0.0625 0.484375 -0.0625q0.984375 0 1.609375 0.671875q0.640625 0.65625 0.640625 1.765625q0 0.890625 -0.4375 1.765625q-0.4375 0.859375 -1.234375 1.328125q-0.78125 0.453125 -1.640625 0.453125q-1.09375 0 -1.796875 -0.703125q-0.703125 -0.703125 -0.71875 -1.90625zm12.814453 -4.03125l-1.578125 0.140625q-0.046875 -0.515625 -0.25 -0.71875q-0.1875 -0.203125 -0.515625 -0.203125q-0.4375 0 -0.765625 0.34375q-0.46875 0.484375 -0.765625 1.5625q0.546875 -0.359375 1.15625 -0.359375q1.0 0 1.640625 0.6875q0.640625 0.671875 0.640625 1.84375q0 0.9375 -0.453125 1.765625q-0.4375 0.828125 -1.125 1.21875q-0.671875 0.375 -1.421875 0.375q-0.71875 0 -1.296875 -0.359375q-0.578125 -0.359375 -0.890625 -1.046875q-0.3125 -0.703125 -0.3125 -1.859375q0 -2.3125 1.015625 -3.90625q1.03125 -1.609375 2.703125 -1.609375q0.953125 0 1.515625 0.53125q0.578125 0.515625 0.703125 1.59375zm-4.3125 3.96875q0 0.6875 0.265625 1.015625q0.265625 0.3125 0.671875 0.3125q0.484375 0 0.84375 -0.453125q0.46875 -0.625 0.46875 -1.65625q0 -0.671875 -0.265625 -0.984375q-0.265625 -0.328125 -0.640625 -0.328125q-0.328125 0 -0.65625 0.25q-0.328125 0.25 -0.515625 0.78125q-0.171875 0.515625 -0.171875 1.0625z" fill-rule="nonzero"/><path fill="#000000" d="m901.2633 293.6524l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m867.9977 301.82303l28.02539 0l0 1.0800171l-28.02539 0l0 -1.0800171z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m428.9186 232.81627l245.03934 -82.64566" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m428.9186 232.81627l245.03934 -82.64566" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m428.9186 232.81627l245.03934 34.173233" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m428.9186 232.81627l245.03934 34.173233" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m667.3228 18.233574l296.9449 0l0 28.031494l-296.9449 0z" fill-rule="evenodd"/><path fill="#000000" d="m679.29156 41.313572l-2.796875 -10.484373l1.4375 0l1.59375 6.874998q0.25 1.078125 0.4375 2.140625q0.40625 -1.6875 0.46875 -1.9375l2.0 -7.078123l1.671875 0l1.5 5.296873q0.578125 1.984375 0.828125 3.71875q0.203125 -1.0 0.515625 -2.28125l1.640625 -6.734373l1.40625 0l-2.875 10.484373l-1.34375 0l-2.203125 -8.0q-0.28125 -1.0 -0.34375 -1.21875q-0.15625 0.71875 -0.296875 1.21875l-2.234375 8.0l-1.40625 0zm11.833313 0l0 -10.484373l1.28125 0l0 3.749998q0.90625 -1.03125 2.28125 -1.03125q0.84375 0 1.46875 0.328125q0.625 0.328125 0.890625 0.921875q0.265625 0.578125 0.265625 1.703125l0 4.8125l-1.28125 0l0 -4.8125q0 -0.96875 -0.421875 -1.40625q-0.421875 -0.4375 -1.1875 -0.4375q-0.578125 0 -1.078125 0.296875q-0.5 0.296875 -0.71875 0.8125q-0.21875 0.5 -0.21875 1.390625l0 4.15625l-1.28125 0zm13.354187 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.182373 4.53125l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm17.176208 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm12.3324585 1.03125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm1.890625 -1.015625q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307373 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm13.072998 0l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.749998l1.28125 0l0 10.484373l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm7.291687 -5.21875l0 -1.4687481l1.296875 0l0 1.4687481l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm6.0686646 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.8906231l1.28125 -0.765625l0 2.656248l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594604 -7.859375l0 -1.4687481l1.296875 0l0 1.4687481l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717896 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307373 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm12.2230835 -9.015625l0 -1.4687481l1.296875 0l0 1.4687481l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7405396 -2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm16.837585 2.265625l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.749998l1.28125 0l0 10.484373l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm12.494873 1.34375l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm9.994812 3.375l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.8906231l1.28125 -0.765625l0 2.656248l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm6.4626465 -1.296875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm12.135437 1.75l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm5.1875 1.625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.8906231l1.28125 -0.765625l0 2.656248l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm6.4626465 -1.296875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm12.104187 4.53125l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.749998l1.28125 0l0 10.484373l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm7.619873 3.796875l0 -1.46875l1.46875 0l0 1.46875q0 0.8125 -0.28125 1.296875q-0.28125 0.5 -0.90625 0.78125l-0.359375 -0.546875q0.40625 -0.1875 0.59375 -0.53125q0.203125 -0.34375 0.21875 -1.0l-0.734375 0zm10.628357 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.8906231l1.28125 -0.765625l0 2.656248l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594604 1.15625l0 -10.484373l1.28125 0l0 3.749998q0.90625 -1.03125 2.28125 -1.03125q0.84375 0 1.46875 0.328125q0.625 0.328125 0.890625 0.921875q0.265625 0.578125 0.265625 1.703125l0 4.8125l-1.28125 0l0 -4.8125q0 -0.96875 -0.421875 -1.40625q-0.421875 -0.4375 -1.1875 -0.4375q-0.578125 0 -1.078125 0.296875q-0.5 0.296875 -0.71875 0.8125q-0.21875 0.5 -0.21875 1.390625l0 4.15625l-1.28125 0zm13.354248 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm16.207458 3.59375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm8.260437 1.03125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm5.1875 1.625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.8906231l1.28125 -0.765625l0 2.656248l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2595215 -7.859375l0 -1.4687481l1.296875 0l0 1.4687481l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717285 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307373 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm12.2230835 -9.015625l0 -1.4687481l1.296875 0l0 1.4687481l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7405396 -2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125z" fill-rule="nonzero"/><path fill="#000000" d="m677.29156 62.219826l0 -10.5l1.171875 0l0 0.984375q0.421875 -0.578125 0.9375 -0.859375q0.515625 -0.296875 1.265625 -0.296875q0.96875 0 1.71875 0.5q0.75 0.5 1.125 1.421875q0.375 0.90625 0.375 1.984375q0 1.171875 -0.421875 2.109375q-0.40625 0.921875 -1.21875 1.421875q-0.796875 0.5 -1.671875 0.5q-0.640625 0 -1.15625 -0.265625q-0.515625 -0.28125 -0.84375 -0.6875l0 3.6875l-1.28125 0zm1.15625 -6.65625q0 1.453125 0.59375 2.15625q0.609375 0.703125 1.453125 0.703125q0.859375 0 1.46875 -0.71875q0.609375 -0.734375 0.609375 -2.25q0 -1.453125 -0.609375 -2.171875q-0.59375 -0.734375 -1.421875 -0.734375q-0.8125 0 -1.453125 0.78125q-0.640625 0.765625 -0.640625 2.234375zm6.510498 -0.046875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307373 6.703125l0 -10.5l1.171875 0l0 0.984375q0.421875 -0.578125 0.9375 -0.859375q0.515625 -0.296875 1.265625 -0.296875q0.96875 0 1.71875 0.5q0.75 0.5 1.125 1.421875q0.375 0.90625 0.375 1.984375q0 1.171875 -0.421875 2.109375q-0.40625 0.921875 -1.21875 1.421875q-0.796875 0.5 -1.671875 0.5q-0.640625 0 -1.15625 -0.265625q-0.515625 -0.28125 -0.84375 -0.6875l0 3.6875l-1.28125 0zm1.15625 -6.65625q0 1.453125 0.59375 2.15625q0.609375 0.703125 1.453125 0.703125q0.859375 0 1.46875 -0.71875q0.609375 -0.734375 0.609375 -2.25q0 -1.453125 -0.609375 -2.171875q-0.59375 -0.734375 -1.421875 -0.734375q-0.8125 0 -1.453125 0.78125q-0.640625 0.765625 -0.640625 2.234375zm11.979248 3.75l0 -1.109375q-0.890625 1.28125 -2.421875 1.28125q-0.671875 0 -1.25 -0.25q-0.578125 -0.265625 -0.875 -0.65625q-0.28125 -0.390625 -0.390625 -0.953125q-0.078125 -0.375 -0.078125 -1.203125l0 -4.703125l1.28125 0l0 4.203125q0 1.015625 0.078125 1.359375q0.125 0.515625 0.515625 0.8125q0.40625 0.28125 0.984375 0.28125q0.578125 0 1.078125 -0.296875q0.515625 -0.296875 0.71875 -0.8125q0.21875 -0.515625 0.21875 -1.484375l0 -4.0625l1.28125 0l0 7.59375l-1.140625 0zm3.135437 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.24054 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm6.119873 2.65625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm6.4625854 -1.296875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm12.104248 4.53125l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm12.7699585 3.796875l-2.328125 -7.59375l1.328125 0l1.203125 4.375l0.453125 1.640625q0.03125 -0.125 0.390625 -1.578125l1.21875 -4.4375l1.328125 0l1.125 4.40625l0.390625 1.453125l0.4375 -1.46875l1.296875 -4.390625l1.25 0l-2.375 7.59375l-1.34375 0l-1.203125 -4.546875l-0.296875 -1.296875l-1.53125 5.84375l-1.34375 0zm9.17804 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm6.0686035 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2595215 1.15625l0 -10.484375l1.28125 0l0 3.75q0.90625 -1.03125 2.28125 -1.03125q0.84375 0 1.46875 0.328125q0.625 0.328125 0.890625 0.921875q0.265625 0.578125 0.265625 1.703125l0 4.8125l-1.28125 0l0 -4.8125q0 -0.96875 -0.421875 -1.40625q-0.421875 -0.4375 -1.1875 -0.4375q-0.578125 0 -1.078125 0.296875q-0.5 0.296875 -0.71875 0.8125q-0.21875 0.5 -0.21875 1.390625l0 4.15625l-1.28125 0zm17.176208 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm3.276062 3.8125l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.49054 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm7.70874 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm7.440857 1.15625l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.171875 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm3.276123 3.8125l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.2717285 0l0 -1.109375q-0.890625 1.28125 -2.421875 1.28125q-0.671875 0 -1.25 -0.25q-0.578125 -0.265625 -0.875 -0.65625q-0.28125 -0.390625 -0.390625 -0.953125q-0.078125 -0.375 -0.078125 -1.203125l0 -4.703125l1.28125 0l0 4.203125q0 1.015625 0.078125 1.359375q0.125 0.515625 0.515625 0.8125q0.40625 0.28125 0.984375 0.28125q0.578125 0 1.078125 -0.296875q0.515625 -0.296875 0.71875 -0.8125q0.21875 -0.515625 0.21875 -1.484375l0 -4.0625l1.28125 0l0 7.59375l-1.140625 0zm8.369873 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm6.666748 2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm14.368835 5.34375q-1.0625 -1.34375 -1.796875 -3.140625q-0.734375 -1.8125 -0.734375 -3.734375q0 -1.703125 0.546875 -3.265625q0.640625 -1.8125 1.984375 -3.609375l0.921875 0q-0.859375 1.484375 -1.140625 2.125q-0.4375 0.984375 -0.6875 2.0625q-0.296875 1.34375 -0.296875 2.6875q0 3.453125 2.125 6.875l-0.921875 0zm5.5932617 -3.078125l4.0625 -5.46875l-3.578125 -5.015625l1.65625 0l1.890625 2.6875q0.59375 0.828125 0.84375 1.28125q0.359375 -0.5625 0.84375 -1.1875l2.109375 -2.78125l1.5 0l-3.6875 4.9375l3.984375 5.546875l-1.71875 0l-2.640625 -3.75q-0.21875 -0.3125 -0.46875 -0.6875q-0.34375 0.5625 -0.5 0.78125l-2.625 3.65625l-1.671875 0zm15.166199 0l-1.28125 0l0 -8.203125q-0.46875 0.4375 -1.21875 0.890625q-0.75 0.4375 -1.359375 0.65625l0 -1.25q1.078125 -0.5 1.890625 -1.21875q0.8125 -0.734375 1.140625 -1.40625l0.828125 0l0 10.53125zm10.072998 -1.234375l0 1.234375l-6.9375 0q-0.015625 -0.46875 0.15625 -0.890625q0.265625 -0.71875 0.84375 -1.40625q0.59375 -0.6875 1.6875 -1.578125q1.71875 -1.40625 2.3125 -2.21875q0.609375 -0.828125 0.609375 -1.5625q0 -0.765625 -0.5625 -1.28125q-0.546875 -0.53125 -1.421875 -0.53125q-0.921875 0 -1.484375 0.5625q-0.5625 0.5625 -0.5625 1.546875l-1.328125 -0.140625q0.140625 -1.484375 1.015625 -2.25q0.890625 -0.78125 2.390625 -0.78125q1.515625 0 2.390625 0.84375q0.875 0.828125 0.875 2.0625q0 0.640625 -0.265625 1.25q-0.25 0.59375 -0.84375 1.265625q-0.59375 0.671875 -1.984375 1.859375q-1.15625 0.96875 -1.484375 1.3125q-0.328125 0.34375 -0.546875 0.703125l5.140625 0zm1.385498 -1.53125l1.296875 -0.171875q0.21875 1.09375 0.75 1.578125q0.53125 0.484375 1.296875 0.484375q0.90625 0 1.53125 -0.625q0.640625 -0.640625 0.640625 -1.5625q0 -0.890625 -0.59375 -1.46875q-0.578125 -0.578125 -1.46875 -0.578125q-0.359375 0 -0.90625 0.140625l0.140625 -1.125q0.125 0.015625 0.203125 0.015625q0.828125 0 1.484375 -0.421875q0.65625 -0.4375 0.65625 -1.34375q0 -0.703125 -0.484375 -1.15625q-0.46875 -0.46875 -1.234375 -0.46875q-0.75 0 -1.25 0.46875q-0.5 0.46875 -0.640625 1.421875l-1.28125 -0.234375q0.234375 -1.296875 1.0625 -2.0q0.84375 -0.71875 2.09375 -0.71875q0.859375 0 1.578125 0.375q0.71875 0.359375 1.09375 1.0q0.390625 0.640625 0.390625 1.34375q0 0.6875 -0.375 1.25q-0.359375 0.546875 -1.0625 0.875q0.921875 0.21875 1.4375 0.90625q0.515625 0.671875 0.515625 1.6875q0 1.375 -1.0 2.328125q-1.0 0.953125 -2.53125 0.953125q-1.390625 0 -2.296875 -0.8125q-0.90625 -0.828125 -1.046875 -2.140625zm17.535583 1.828125q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm3.307373 3.8125l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm13.072937 0l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125z" fill-rule="nonzero"/><path fill="#000000" d="m680.41656 77.313576l0 -4.4375l-4.046875 -6.046875l1.6875 0l2.0625 3.15625q0.578125 0.890625 1.0625 1.78125q0.484375 -0.828125 1.15625 -1.859375l2.03125 -3.078125l1.609375 0l-4.1875 6.046875l0 4.4375l-1.375 0zm10.41626 0l0 -2.515625l-4.546875 0l0 -1.171875l4.78125 -6.796875l1.0625 0l0 6.796875l1.40625 0l0 1.171875l-1.40625 0l0 2.515625l-1.296875 0zm0 -3.6875l0 -4.734375l-3.28125 4.734375l3.28125 0zm4.026123 0.9375l1.34375 -0.109375q0.15625 0.984375 0.703125 1.484375q0.546875 0.5 1.328125 0.5q0.921875 0 1.5625 -0.703125q0.65625 -0.703125 0.65625 -1.859375q0 -1.109375 -0.625 -1.734375q-0.625 -0.640625 -1.625 -0.640625q-0.625 0 -1.125 0.28125q-0.5 0.28125 -0.78125 0.734375l-1.203125 -0.15625l1.015625 -5.390625l5.21875 0l0 1.21875l-4.1875 0l-0.578125 2.828125q0.953125 -0.65625 1.984375 -0.65625q1.375 0 2.3125 0.953125q0.953125 0.953125 0.953125 2.4375q0 1.4375 -0.828125 2.46875q-1.015625 1.265625 -2.75 1.265625q-1.4375 0 -2.34375 -0.796875q-0.90625 -0.796875 -1.03125 -2.125zm14.838562 -5.171875l-1.28125 0.09375q-0.171875 -0.75 -0.484375 -1.09375q-0.53125 -0.546875 -1.296875 -0.546875q-0.609375 0 -1.078125 0.34375q-0.609375 0.4375 -0.96875 1.296875q-0.34375 0.84375 -0.359375 2.421875q0.46875 -0.71875 1.140625 -1.0625q0.671875 -0.34375 1.40625 -0.34375q1.296875 0 2.203125 0.953125q0.90625 0.953125 0.90625 2.453125q0 0.984375 -0.4375 1.84375q-0.421875 0.84375 -1.171875 1.296875q-0.734375 0.4375 -1.6875 0.4375q-1.609375 0 -2.625 -1.171875q-1.015625 -1.1875 -1.015625 -3.90625q0 -3.046875 1.125 -4.421875q0.984375 -1.203125 2.640625 -1.203125q1.234375 0 2.03125 0.703125q0.796875 0.6875 0.953125 1.90625zm-5.265625 4.515625q0 0.671875 0.28125 1.28125q0.28125 0.609375 0.78125 0.9375q0.515625 0.3125 1.078125 0.3125q0.8125 0 1.390625 -0.65625q0.59375 -0.671875 0.59375 -1.796875q0 -1.09375 -0.578125 -1.71875q-0.578125 -0.625 -1.453125 -0.625q-0.875 0 -1.484375 0.625q-0.609375 0.625 -0.609375 1.640625zm7.932373 6.484375l-0.921875 0q2.140625 -3.421875 2.140625 -6.875q0 -1.34375 -0.3125 -2.671875q-0.25 -1.0625 -0.671875 -2.046875q-0.28125 -0.65625 -1.15625 -2.15625l0.921875 0q1.34375 1.796875 1.984375 3.609375q0.546875 1.5625 0.546875 3.265625q0 1.921875 -0.734375 3.734375q-0.734375 1.796875 -1.796875 3.140625zm13.062012 -4.015625q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm3.307373 3.8125l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm13.072937 0l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm16.566833 1.34375l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm6.322998 4.53125l2.78125 -3.953125l-2.578125 -3.640625l1.609375 0l1.171875 1.78125q0.328125 0.5 0.53125 0.84375q0.3125 -0.46875 0.578125 -0.828125l1.28125 -1.796875l1.53125 0l-2.625 3.578125l2.828125 4.015625l-1.578125 0l-1.5625 -2.359375l-0.40625 -0.640625l-2.0 3.0l-1.5625 0zm13.390625 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm12.135498 1.75l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm7.359375 2.78125l0 -1.109375q-0.890625 1.28125 -2.421875 1.28125q-0.671875 0 -1.25 -0.25q-0.578125 -0.265625 -0.875 -0.65625q-0.28125 -0.390625 -0.390625 -0.953125q-0.078125 -0.375 -0.078125 -1.203125l0 -4.703125l1.28125 0l0 4.203125q0 1.015625 0.078125 1.359375q0.125 0.515625 0.515625 0.8125q0.40625 0.28125 0.984375 0.28125q0.578125 0 1.078125 -0.296875q0.515625 -0.296875 0.71875 -0.8125q0.21875 -0.515625 0.21875 -1.484375l0 -4.0625l1.28125 0l0 7.59375l-1.140625 0zm5.979248 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm6.4625854 -1.296875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm12.104248 4.53125l0 -0.953125q-0.71875 1.125 -2.125 1.125q-0.90625 0 -1.671875 -0.5q-0.75 -0.5 -1.171875 -1.390625q-0.421875 -0.90625 -0.421875 -2.078125q0 -1.140625 0.375 -2.0625q0.390625 -0.921875 1.140625 -1.40625q0.765625 -0.5 1.703125 -0.5q0.6875 0 1.21875 0.296875q0.53125 0.28125 0.875 0.734375l0 -3.75l1.28125 0l0 10.484375l-1.203125 0zm-4.0625 -3.796875q0 1.46875 0.609375 2.1875q0.625 0.71875 1.453125 0.71875q0.84375 0 1.4375 -0.6875q0.59375 -0.6875 0.59375 -2.109375q0 -1.5625 -0.609375 -2.28125q-0.59375 -0.734375 -1.484375 -0.734375q-0.84375 0 -1.421875 0.703125q-0.578125 0.703125 -0.578125 2.203125zm7.651062 3.796875l0 -1.46875l1.46875 0l0 1.46875l-1.46875 0z" fill-rule="nonzero"/></g></svg> \ No newline at end of file diff --git a/docs/user/alerting/images/what-is-an-alert.svg b/docs/user/alerting/images/what-is-an-alert.svg new file mode 100644 index 0000000000000..b4dc02fae39a5 --- /dev/null +++ b/docs/user/alerting/images/what-is-an-alert.svg @@ -0,0 +1 @@ +<svg version="1.1" viewBox="0.0 0.0 484.2257217847769 307.87139107611546" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l484.2257 0l0 307.8714l-484.2257 0l0 -307.8714z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l484.2257 0l0 307.8714l-484.2257 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m13.120735 12.889764l451.9685 0l0 282.2677l-451.9685 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m13.120735 12.889764l451.9685 0l0 282.2677l-451.9685 0z" fill-rule="evenodd"/><path fill="#000000" d="m22.089485 39.80976l5.125 -13.359375l1.90625 0l5.46875 13.359375l-2.015625 0l-1.546875 -4.046875l-5.59375 0l-1.46875 4.046875l-1.875 0zm3.859375 -5.484375l4.53125 0l-1.40625 -3.703125q-0.625 -1.6875 -0.9375 -2.765625q-0.265625 1.28125 -0.71875 2.546875l-1.46875 3.921875zm9.802948 5.484375l0 -13.359375l1.640625 0l0 13.359375l-1.640625 0zm10.816696 -3.109375l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8125 -2.765625 0.8125q-2.125 0 -3.375 -1.296875q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm9.125717 5.765625l0 -9.671875l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.0625l-1.625 0zm9.806427 -1.46875l0.234375 1.453125q-0.6875 0.140625 -1.234375 0.140625q-0.890625 0 -1.390625 -0.28125q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 57.062992l418.2677 0l0 33.511814l-418.2677 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 57.062992l418.2677 0l0 33.511814l-418.2677 0z" fill-rule="evenodd"/><path fill="#000000" d="m43.750084 77.40737l1.515625 0.375q-0.46875 1.875 -1.71875 2.859375q-1.234375 0.984375 -3.015625 0.984375q-1.859375 0 -3.015625 -0.75q-1.15625 -0.765625 -1.765625 -2.1875q-0.609375 -1.4375 -0.609375 -3.078125q0 -1.796875 0.6875 -3.125q0.6875 -1.328125 1.9375 -2.015625q1.265625 -0.703125 2.78125 -0.703125q1.71875 0 2.890625 0.875q1.171875 0.875 1.640625 2.46875l-1.5 0.34375q-0.390625 -1.25 -1.15625 -1.8125q-0.75 -0.578125 -1.90625 -0.578125q-1.3125 0 -2.203125 0.640625q-0.890625 0.625 -1.25 1.703125q-0.359375 1.0625 -0.359375 2.1875q0 1.46875 0.421875 2.5625q0.4375 1.078125 1.328125 1.625q0.90625 0.53125 1.953125 0.53125q1.265625 0 2.140625 -0.734375q0.890625 -0.734375 1.203125 -2.171875zm2.6796875 -0.140625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0zm14.2734375 0l0 -1.046875q-0.78125 1.234375 -2.3125 1.234375q-1.0 0 -1.828125 -0.546875q-0.828125 -0.546875 -1.296875 -1.53125q-0.453125 -0.984375 -0.453125 -2.25q0 -1.25 0.40625 -2.25q0.421875 -1.015625 1.25 -1.546875q0.828125 -0.546875 1.859375 -0.546875q0.75 0 1.328125 0.3125q0.59375 0.3125 0.953125 0.828125l0 -4.109375l1.40625 0l0 11.453125l-1.3125 0zm-4.4375 -4.140625q0 1.59375 0.671875 2.390625q0.671875 0.78125 1.578125 0.78125q0.921875 0 1.5625 -0.75q0.65625 -0.765625 0.65625 -2.3125q0 -1.703125 -0.65625 -2.5q-0.65625 -0.796875 -1.625 -0.796875q-0.9375 0 -1.5625 0.765625q-0.625 0.765625 -0.625 2.421875zm7.9609375 -5.703125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm6.6171875 -1.265625l0.203125 1.25q-0.59375 0.125 -1.0625 0.125q-0.765625 0 -1.1875 -0.234375q-0.421875 -0.25 -0.59375 -0.640625q-0.171875 -0.40625 -0.171875 -1.671875l0 -4.765625l-1.03125 0l0 -1.09375l1.03125 0l0 -2.0625l1.40625 -0.84375l0 2.90625l1.40625 0l0 1.09375l-1.40625 0l0 4.84375q0 0.609375 0.0625 0.78125q0.078125 0.171875 0.25 0.28125q0.171875 0.09375 0.484375 0.09375q0.234375 0 0.609375 -0.0625zm1.3828125 -8.578125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm3.0234375 -4.15625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0z" fill-rule="nonzero"/><path fill="#000000" d="m131.0157 78.57925l1.40625 -0.078125q0 0.609375 0.1875 1.046875q0.1875 0.421875 0.6875 0.703125q0.515625 0.265625 1.1875 0.265625q0.9375 0 1.40625 -0.375q0.46875 -0.375 0.46875 -0.890625q0 -0.359375 -0.28125 -0.6875q-0.296875 -0.328125 -1.421875 -0.8125q-1.109375 -0.484375 -1.421875 -0.671875q-0.53125 -0.328125 -0.796875 -0.75q-0.265625 -0.4375 -0.265625 -1.0q0 -0.984375 0.78125 -1.6875q0.78125 -0.703125 2.1875 -0.703125q1.5625 0 2.375 0.71875q0.828125 0.71875 0.859375 1.90625l-1.390625 0.09375q-0.03125 -0.75 -0.53125 -1.1875q-0.5 -0.4375 -1.40625 -0.4375q-0.734375 0 -1.140625 0.34375q-0.40625 0.328125 -0.40625 0.71875q0 0.390625 0.34375 0.6875q0.234375 0.203125 1.21875 0.625q1.625 0.703125 2.046875 1.109375q0.671875 0.640625 0.671875 1.578125q0 0.609375 -0.390625 1.203125q-0.375 0.59375 -1.15625 0.953125q-0.765625 0.359375 -1.828125 0.359375q-1.4375 0 -2.4375 -0.71875q-1.015625 -0.703125 -0.953125 -2.3125zm13.953125 0.015625l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm6.9140625 4.875l1.734375 -8.296875l1.25 0l-0.34375 1.6875q0.640625 -0.953125 1.25 -1.40625q0.609375 -0.46875 1.25 -0.46875q0.421875 0 1.046875 0.296875l-0.578125 1.3125q-0.375 -0.265625 -0.8125 -0.265625q-0.734375 0 -1.515625 0.828125q-0.78125 0.828125 -1.234375 2.984375l-0.703125 3.328125l-1.34375 0zm7.421875 0l-1.359375 -8.296875l1.375 0l0.71875 4.5625q0.109375 0.75 0.28125 2.453125q0.40625 -0.890625 1.046875 -2.03125l2.765625 -4.984375l1.5 0l-4.75 8.296875l-1.578125 0zm12.0 -2.828125l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm6.9140625 4.875l1.734375 -8.296875l1.25 0l-0.34375 1.6875q0.640625 -0.953125 1.25 -1.40625q0.609375 -0.46875 1.25 -0.46875q0.421875 0 1.046875 0.296875l-0.578125 1.3125q-0.375 -0.265625 -0.8125 -0.265625q-0.734375 0 -1.515625 0.828125q-0.78125 0.828125 -1.234375 2.984375l-0.703125 3.328125l-1.34375 0zm15.3671875 -1.03125q-0.734375 0.625 -1.421875 0.921875q-0.671875 0.296875 -1.4375 0.296875q-1.15625 0 -1.859375 -0.671875q-0.703125 -0.6875 -0.703125 -1.734375q0 -0.703125 0.3125 -1.234375q0.328125 -0.53125 0.84375 -0.859375q0.53125 -0.328125 1.28125 -0.46875q0.484375 -0.09375 1.8125 -0.140625q1.328125 -0.0625 1.90625 -0.28125q0.171875 -0.578125 0.171875 -0.96875q0 -0.484375 -0.359375 -0.765625q-0.5 -0.390625 -1.4375 -0.390625q-0.890625 0 -1.46875 0.390625q-0.5625 0.390625 -0.8125 1.125l-1.4375 -0.125q0.4375 -1.234375 1.390625 -1.890625q0.953125 -0.65625 2.390625 -0.65625q1.546875 0 2.4375 0.734375q0.6875 0.546875 0.6875 1.421875q0 0.65625 -0.1875 1.53125l-0.46875 2.0625q-0.21875 0.984375 -0.21875 1.609375q0 0.390625 0.171875 1.125l-1.421875 0q-0.125 -0.40625 -0.171875 -1.03125zm0.53125 -3.171875q-0.296875 0.109375 -0.640625 0.171875q-0.34375 0.0625 -1.140625 0.140625q-1.234375 0.109375 -1.75 0.28125q-0.5 0.15625 -0.765625 0.53125q-0.25 0.359375 -0.25 0.8125q0 0.59375 0.40625 0.984375q0.421875 0.375 1.171875 0.375q0.703125 0 1.34375 -0.375q0.65625 -0.375 1.03125 -1.03125q0.375 -0.671875 0.59375 -1.890625zm4.8671875 4.203125l-1.359375 -8.296875l1.375 0l0.71875 4.5625q0.109375 0.75 0.28125 2.453125q0.40625 -0.890625 1.046875 -2.03125l2.765625 -4.984375l1.5 0l-4.75 8.296875l-1.578125 0zm5.828125 0.75l1.4375 0.140625q-0.015625 0.484375 0.109375 0.71875q0.140625 0.25 0.421875 0.375q0.390625 0.171875 1.0 0.171875q1.28125 0 1.84375 -0.65625q0.359375 -0.453125 0.671875 -1.953125l0.140625 -0.671875q-1.09375 1.125 -2.34375 1.125q-1.25 0 -2.109375 -0.921875q-0.84375 -0.9375 -0.84375 -2.625q0 -1.40625 0.671875 -2.578125q0.671875 -1.171875 1.59375 -1.765625q0.921875 -0.59375 1.890625 -0.59375q1.640625 0 2.515625 1.546875l0.28125 -1.359375l1.296875 0l-1.671875 8.0q-0.265625 1.328125 -0.71875 2.0625q-0.4375 0.75 -1.234375 1.15625q-0.796875 0.40625 -1.828125 0.40625q-1.0 0 -1.734375 -0.265625q-0.71875 -0.25 -1.078125 -0.75q-0.34375 -0.5 -0.34375 -1.140625q0 -0.1875 0.03125 -0.421875zm1.765625 -4.421875q0 0.84375 0.171875 1.28125q0.234375 0.59375 0.65625 0.921875q0.4375 0.3125 0.96875 0.3125q0.703125 0 1.390625 -0.484375q0.6875 -0.5 1.109375 -1.515625q0.421875 -1.03125 0.421875 -1.953125q0 -1.03125 -0.5625 -1.625q-0.5625 -0.609375 -1.40625 -0.609375q-0.515625 0 -1.0 0.28125q-0.484375 0.265625 -0.90625 0.84375q-0.40625 0.5625 -0.625 1.359375q-0.21875 0.78125 -0.21875 1.1875zm20.78125 -0.359375l1.515625 0.203125q-0.71875 1.984375 -2.09375 3.015625q-1.375 1.015625 -3.09375 1.015625q-2.078125 0 -3.3125 -1.28125q-1.21875 -1.28125 -1.21875 -3.65625q0 -3.09375 1.859375 -5.109375q1.65625 -1.8125 4.125 -1.8125q1.8125 0 2.9375 0.984375q1.140625 0.96875 1.3125 2.625l-1.421875 0.125q-0.234375 -1.234375 -0.9375 -1.84375q-0.703125 -0.625 -1.828125 -0.625q-2.09375 0 -3.390625 1.875q-1.125 1.609375 -1.125 3.8125q0 1.765625 0.859375 2.703125q0.875 0.921875 2.265625 0.921875q1.1875 0 2.140625 -0.765625q0.96875 -0.78125 1.40625 -2.1875zm2.5859375 4.03125l2.40625 -11.453125l4.78125 0q1.234375 0 1.859375 0.28125q0.625 0.28125 1.03125 0.984375q0.40625 0.6875 0.40625 1.546875q0 0.703125 -0.296875 1.4375q-0.28125 0.734375 -0.734375 1.21875q-0.4375 0.46875 -0.890625 0.71875q-0.453125 0.234375 -0.96875 0.359375q-1.09375 0.25 -2.21875 0.25l-2.875 0l-0.96875 4.65625l-1.53125 0zm2.78125 -5.953125l2.515625 0q1.46875 0 2.15625 -0.3125q0.6875 -0.3125 1.09375 -0.953125q0.421875 -0.65625 0.421875 -1.390625q0 -0.5625 -0.21875 -0.90625q-0.21875 -0.359375 -0.625 -0.53125q-0.390625 -0.171875 -1.515625 -0.171875l-2.9375 0l-0.890625 4.265625zm10.375 -5.5l1.53125 0l-1.46875 7.03125q-0.171875 0.875 -0.171875 1.296875q0 0.921875 0.71875 1.484375q0.734375 0.5625 1.828125 0.5625q0.875 0 1.625 -0.390625q0.765625 -0.40625 1.203125 -1.1875q0.4375 -0.78125 0.78125 -2.5l1.328125 -6.296875l1.53125 0l-1.40625 6.71875q-0.359375 1.71875 -0.953125 2.71875q-0.578125 1.0 -1.625 1.609375q-1.03125 0.609375 -2.40625 0.609375q-1.296875 0 -2.25 -0.421875q-0.9375 -0.4375 -1.421875 -1.1875q-0.46875 -0.765625 -0.46875 -1.734375q0 -0.59375 0.328125 -2.0625l1.296875 -6.25zm21.85939 6.4375l-7.5937653 3.25l0 -1.40625l6.015625 -2.484375l-6.015625 -2.46875l0 -1.40625l7.5937653 3.203125l0 1.3125zm5.8984375 1.25q0 -1.453125 0.421875 -2.953125q0.4375 -1.5 0.96875 -2.390625q0.53125 -0.90625 1.09375 -1.40625q0.578125 -0.515625 1.125 -0.75q0.5625 -0.234375 1.234375 -0.234375q1.3125 0 2.1875 0.984375q0.875 0.96875 0.875 2.8125q0 1.890625 -0.609375 3.75q-0.71875 2.203125 -2.0 3.3125q-0.984375 0.84375 -2.234375 0.84375q-1.296875 0 -2.1875 -1.015625q-0.875 -1.015625 -0.875 -2.953125zm1.359375 0.359375q0 1.0625 0.359375 1.65625q0.46875 0.8125 1.40625 0.8125q0.828125 0 1.5 -0.734375q0.96875 -1.046875 1.453125 -3.0625q0.484375 -2.03125 0.484375 -3.3125q0 -1.234375 -0.46875 -1.78125q-0.46875 -0.546875 -1.25 -0.546875q-0.5625 0 -1.046875 0.296875q-0.484375 0.28125 -0.9375 0.984375q-0.625 1.0 -1.09375 2.96875q-0.40625 1.6875 -0.40625 2.71875zm7.3203125 3.40625l0.34375 -1.609375l1.59375 0l-0.34375 1.609375l-1.59375 0zm4.6015625 -2.625l1.328125 -0.140625q0.140625 0.96875 0.546875 1.375q0.421875 0.40625 0.984375 0.40625q0.8125 0 1.515625 -0.703125q1.015625 -1.03125 1.5 -2.96875q-0.703125 0.53125 -1.265625 0.765625q-0.546875 0.21875 -1.125 0.21875q-1.046875 0 -1.875 -0.703125q-1.09375 -0.90625 -1.09375 -2.625q0 -1.9375 1.265625 -3.3125q1.09375 -1.1875 2.671875 -1.1875q1.421875 0 2.359375 1.046875q0.9375 1.046875 0.9375 2.96875q0 1.875 -0.625 3.6875q-0.75 2.140625 -2.015625 3.15625q-1.03125 0.84375 -2.3125 0.84375q-1.1875 0 -1.953125 -0.734375q-0.75 -0.75 -0.84375 -2.09375zm1.890625 -4.53125q0 1.078125 0.5625 1.71875q0.578125 0.640625 1.375 0.640625q0.578125 0 1.15625 -0.390625q0.578125 -0.40625 0.984375 -1.203125q0.421875 -0.796875 0.421875 -1.625q0 -0.65625 -0.28125 -1.21875q-0.265625 -0.5625 -0.734375 -0.84375q-0.46875 -0.296875 -0.953125 -0.296875q-0.46875 0 -0.921875 0.25q-0.453125 0.25 -0.84375 0.734375q-0.390625 0.484375 -0.578125 1.125q-0.1875 0.625 -0.1875 1.109375zm11.109375 7.15625l1.5 -7.203125l-1.265625 0l0.234375 -1.09375l1.265625 0l0.234375 -1.1875q0.1875 -0.875 0.390625 -1.265625q0.203125 -0.390625 0.640625 -0.640625q0.453125 -0.265625 1.234375 -0.265625q0.53125 0 1.5625 0.234375l-0.265625 1.234375q-0.71875 -0.1875 -1.203125 -0.1875q-0.40625 0 -0.625 0.203125q-0.21875 0.203125 -0.375 0.984375l-0.1875 0.890625l1.578125 0l-0.21875 1.09375l-1.578125 0l-1.515625 7.203125l-1.40625 0zm4.4921875 -3.15625q0 -2.421875 1.4375 -4.015625q1.171875 -1.3125 3.09375 -1.3125q1.5 0 2.40625 0.9375q0.921875 0.9375 0.921875 2.53125q0 1.421875 -0.578125 2.65625q-0.578125 1.234375 -1.640625 1.890625q-1.0625 0.65625 -2.25 0.65625q-0.96875 0 -1.765625 -0.40625q-0.78125 -0.421875 -1.203125 -1.171875q-0.421875 -0.765625 -0.421875 -1.765625zm1.421875 -0.140625q0 1.171875 0.5625 1.78125q0.5625 0.609375 1.421875 0.609375q0.453125 0 0.890625 -0.1875q0.453125 -0.1875 0.828125 -0.5625q0.390625 -0.375 0.65625 -0.84375q0.28125 -0.484375 0.4375 -1.046875q0.25 -0.765625 0.25 -1.484375q0 -1.125 -0.578125 -1.734375q-0.5625 -0.625 -1.421875 -0.625q-0.65625 0 -1.203125 0.3125q-0.546875 0.3125 -1.0 0.921875q-0.4375 0.609375 -0.640625 1.421875q-0.203125 0.8125 -0.203125 1.4375zm7.2265625 3.296875l1.734375 -8.296875l1.25 0l-0.34375 1.6875q0.640625 -0.953125 1.25 -1.40625q0.609375 -0.46875 1.25 -0.46875q0.421875 0 1.046875 0.296875l-0.578125 1.3125q-0.375 -0.265625 -0.8125 -0.265625q-0.734375 0 -1.515625 0.828125q-0.78125 0.828125 -1.234375 2.984375l-0.703125 3.328125l-1.34375 0zm9.6640625 0l2.390625 -11.453125l1.421875 0l-2.390625 11.453125l-1.421875 0zm9.2578125 -1.03125q-0.734375 0.625 -1.421875 0.921875q-0.671875 0.296875 -1.4375 0.296875q-1.15625 0 -1.859375 -0.671875q-0.703125 -0.6875 -0.703125 -1.734375q0 -0.703125 0.3125 -1.234375q0.328125 -0.53125 0.84375 -0.859375q0.53125 -0.328125 1.28125 -0.46875q0.484375 -0.09375 1.8125 -0.140625q1.328125 -0.0625 1.90625 -0.28125q0.171875 -0.578125 0.171875 -0.96875q0 -0.484375 -0.359375 -0.765625q-0.5 -0.390625 -1.4375 -0.390625q-0.890625 0 -1.46875 0.390625q-0.5625 0.390625 -0.8125 1.125l-1.4375 -0.125q0.4375 -1.234375 1.390625 -1.890625q0.953125 -0.65625 2.390625 -0.65625q1.546875 0 2.4375 0.734375q0.6875 0.546875 0.6875 1.421875q0 0.65625 -0.1875 1.53125l-0.46875 2.0625q-0.21875 0.984375 -0.21875 1.609375q0 0.390625 0.171875 1.125l-1.421875 0q-0.125 -0.40625 -0.171875 -1.03125zm0.53125 -3.171875q-0.296875 0.109375 -0.640625 0.171875q-0.34375 0.0625 -1.140625 0.140625q-1.234375 0.109375 -1.75 0.28125q-0.5 0.15625 -0.765625 0.53125q-0.25 0.359375 -0.25 0.8125q0 0.59375 0.40625 0.984375q0.421875 0.375 1.171875 0.375q0.703125 0 1.34375 -0.375q0.65625 -0.375 1.03125 -1.03125q0.375 -0.671875 0.59375 -1.890625zm2.9140625 1.359375l1.40625 -0.078125q0 0.609375 0.1875 1.046875q0.1875 0.421875 0.6875 0.703125q0.515625 0.265625 1.1875 0.265625q0.9375 0 1.40625 -0.375q0.46875 -0.375 0.46875 -0.890625q0 -0.359375 -0.28125 -0.6875q-0.296875 -0.328125 -1.421875 -0.8125q-1.109375 -0.484375 -1.421875 -0.671875q-0.53125 -0.328125 -0.796875 -0.75q-0.265625 -0.4375 -0.265625 -1.0q0 -0.984375 0.78125 -1.6875q0.78125 -0.703125 2.1875 -0.703125q1.5625 0 2.375 0.71875q0.828125 0.71875 0.859375 1.90625l-1.390625 0.09375q-0.03125 -0.75 -0.53125 -1.1875q-0.5 -0.4375 -1.40625 -0.4375q-0.734375 0 -1.140625 0.34375q-0.40625 0.328125 -0.40625 0.71875q0 0.390625 0.34375 0.6875q0.234375 0.203125 1.21875 0.625q1.625 0.703125 2.046875 1.109375q0.671875 0.640625 0.671875 1.578125q0 0.609375 -0.390625 1.203125q-0.375 0.59375 -1.15625 0.953125q-0.765625 0.359375 -1.828125 0.359375q-1.4375 0 -2.4375 -0.71875q-1.015625 -0.703125 -0.953125 -2.3125zm11.171875 1.6875l-0.234375 1.171875q-0.515625 0.125 -0.984375 0.125q-0.84375 0 -1.34375 -0.421875q-0.375 -0.3125 -0.375 -0.84375q0 -0.28125 0.203125 -1.265625l1.0 -4.8125l-1.109375 0l0.21875 -1.09375l1.125 0l0.421875 -2.046875l1.625 -0.96875l-0.640625 3.015625l1.390625 0l-0.234375 1.09375l-1.375 0l-0.96875 4.578125q-0.171875 0.875 -0.171875 1.046875q0 0.25 0.140625 0.390625q0.140625 0.125 0.46875 0.125q0.46875 0 0.84375 -0.09375zm5.984375 1.15625q0.21875 -1.03125 0.609375 -1.71875q0.390625 -0.6875 1.03125 -1.3125q0.640625 -0.625 2.46875 -2.078125q1.109375 -0.890625 1.515625 -1.3125q0.578125 -0.609375 0.84375 -1.1875q0.1875 -0.40625 0.1875 -0.875q0 -0.78125 -0.5625 -1.328125q-0.5625 -0.5625 -1.375 -0.5625q-0.796875 0 -1.40625 0.5625q-0.59375 0.5625 -0.859375 1.796875l-1.375 -0.203125q0.203125 -1.53125 1.171875 -2.40625q0.984375 -0.875 2.453125 -0.875q0.96875 0 1.765625 0.40625q0.796875 0.40625 1.1875 1.140625q0.40625 0.734375 0.40625 1.515625q0 1.140625 -0.8125 2.1875q-0.5 0.65625 -2.9375 2.59375q-1.046875 0.828125 -1.5625 1.359375q-0.515625 0.53125 -0.765625 1.0l5.125 0l-0.28125 1.296875l-6.828125 0zm12.9375 0l1.734375 -8.296875l1.40625 0l-0.28125 1.359375q0.78125 -0.875 1.40625 -1.203125q0.625 -0.34375 1.34375 -0.34375q0.78125 0 1.296875 0.40625q0.515625 0.40625 0.6875 1.140625q0.625 -0.78125 1.328125 -1.15625q0.703125 -0.390625 1.46875 -0.390625q1.046875 0 1.5625 0.5q0.515625 0.484375 0.515625 1.375q0 0.375 -0.171875 1.265625l-1.125 5.34375l-1.40625 0l1.140625 -5.484375q0.140625 -0.671875 0.140625 -0.96875q0 -0.40625 -0.265625 -0.640625q-0.25 -0.234375 -0.71875 -0.234375q-0.625 0 -1.28125 0.390625q-0.65625 0.375 -1.03125 1.0q-0.359375 0.625 -0.625 1.90625l-0.84375 4.03125l-1.421875 0l1.171875 -5.609375q0.125 -0.5625 0.125 -0.8125q0 -0.40625 -0.25 -0.65625q-0.25 -0.25 -0.65625 -0.25q-0.609375 0 -1.265625 0.390625q-0.65625 0.375 -1.078125 1.0625q-0.40625 0.6875 -0.671875 1.953125l-0.828125 3.921875l-1.40625 0zm15.34375 -9.859375l0.328125 -1.59375l1.40625 0l-0.328125 1.59375l-1.40625 0zm-2.0625 9.859375l1.734375 -8.296875l1.40625 0l-1.734375 8.296875l-1.40625 0zm3.6015625 0l1.734375 -8.296875l1.28125 0l-0.296875 1.4375q0.828125 -0.84375 1.546875 -1.234375q0.734375 -0.390625 1.5 -0.390625q1.0 0 1.578125 0.546875q0.578125 0.546875 0.578125 1.453125q0 0.46875 -0.203125 1.453125l-1.0625 5.03125l-1.40625 0l1.09375 -5.265625q0.171875 -0.765625 0.171875 -1.125q0 -0.421875 -0.296875 -0.671875q-0.28125 -0.265625 -0.8125 -0.265625q-1.09375 0 -1.9375 0.78125q-0.84375 0.78125 -1.25 2.6875l-0.796875 3.859375l-1.421875 0zm14.5703125 -1.5q-1.5 1.6875 -3.0625 1.6875q-0.953125 0 -1.546875 -0.546875q-0.59375 -0.5625 -0.59375 -1.359375q0 -0.515625 0.265625 -1.796875l1.0 -4.78125l1.421875 0l-1.109375 5.296875q-0.140625 0.671875 -0.140625 1.03125q0 0.46875 0.28125 0.734375q0.28125 0.265625 0.84375 0.265625q0.59375 0 1.15625 -0.28125q0.5625 -0.296875 0.96875 -0.78125q0.421875 -0.5 0.671875 -1.171875q0.171875 -0.4375 0.40625 -1.515625l0.75 -3.578125l1.40625 0l-1.734375 8.296875l-1.296875 0l0.3125 -1.5zm6.5390625 0.34375l-0.234375 1.171875q-0.515625 0.125 -0.984375 0.125q-0.84375 0 -1.34375 -0.421875q-0.375 -0.3125 -0.375 -0.84375q0 -0.28125 0.203125 -1.265625l1.0 -4.8125l-1.109375 0l0.21875 -1.09375l1.125 0l0.421875 -2.046875l1.625 -0.96875l-0.640625 3.015625l1.390625 0l-0.234375 1.09375l-1.375 0l-0.96875 4.578125q-0.171875 0.875 -0.171875 1.046875q0 0.25 0.140625 0.390625q0.140625 0.125 0.46875 0.125q0.46875 0 0.84375 -0.09375zm7.2265625 -1.671875l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.921875 -2.546875 0.921875q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.4375 -1.171875 -1.25q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm7.0546875 2.03125l1.40625 -0.078125q0 0.609375 0.1875 1.046875q0.1875 0.421875 0.6875 0.703125q0.515625 0.265625 1.1875 0.265625q0.9375 0 1.40625 -0.375q0.46875 -0.375 0.46875 -0.890625q0 -0.359375 -0.28125 -0.6875q-0.296875 -0.328125 -1.421875 -0.8125q-1.109375 -0.484375 -1.421875 -0.671875q-0.53125 -0.328125 -0.796875 -0.75q-0.265625 -0.4375 -0.265625 -1.0q0 -0.984375 0.78125 -1.6875q0.78125 -0.703125 2.1875 -0.703125q1.5625 0 2.375 0.71875q0.828125 0.71875 0.859375 1.90625l-1.390625 0.09375q-0.03125 -0.75 -0.53125 -1.1875q-0.5 -0.4375 -1.40625 -0.4375q-0.734375 0 -1.140625 0.34375q-0.40625 0.328125 -0.40625 0.71875q0 0.390625 0.34375 0.6875q0.234375 0.203125 1.21875 0.625q1.625 0.703125 2.046875 1.109375q0.671875 0.640625 0.671875 1.578125q0 0.609375 -0.390625 1.203125q-0.375 0.59375 -1.15625 0.953125q-0.765625 0.359375 -1.828125 0.359375q-1.4375 0 -2.4375 -0.71875q-1.015625 -0.703125 -0.953125 -2.3125z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 104.07612l418.2677 0l0 39.30709l-418.2677 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 104.07612l418.2677 0l0 39.30709l-418.2677 0z" fill-rule="evenodd"/><path fill="#000000" d="m35.062584 124.74862l1.4375 -0.125q0.09375 0.859375 0.46875 1.421875q0.375 0.546875 1.15625 0.890625q0.78125 0.328125 1.75 0.328125q0.875 0 1.53125 -0.25q0.671875 -0.265625 0.984375 -0.703125q0.328125 -0.453125 0.328125 -0.984375q0 -0.546875 -0.3125 -0.9375q-0.3125 -0.40625 -1.03125 -0.6875q-0.453125 -0.171875 -2.03125 -0.546875q-1.578125 -0.390625 -2.21875 -0.71875q-0.8125 -0.4375 -1.21875 -1.0625q-0.40625 -0.640625 -0.40625 -1.4375q0 -0.859375 0.484375 -1.609375q0.5 -0.765625 1.4375 -1.15625q0.953125 -0.390625 2.109375 -0.390625q1.28125 0 2.25 0.421875q0.96875 0.40625 1.484375 1.203125q0.53125 0.796875 0.578125 1.796875l-1.453125 0.109375q-0.125 -1.078125 -0.796875 -1.625q-0.671875 -0.5625 -2.0 -0.5625q-1.375 0 -2.0 0.5q-0.625 0.5 -0.625 1.21875q0 0.609375 0.4375 1.015625q0.4375 0.390625 2.28125 0.8125q1.859375 0.421875 2.546875 0.734375q1.0 0.453125 1.46875 1.171875q0.484375 0.703125 0.484375 1.625q0 0.90625 -0.53125 1.71875q-0.515625 0.8125 -1.5 1.2656174q-0.984375 0.453125 -2.203125 0.453125q-1.5625 0 -2.609375 -0.453125q-1.046875 -0.46874237 -1.65625 -1.3749924q-0.59375 -0.90625 -0.625 -2.0625zm16.421875 0.640625l1.390625 0.1875q-0.234375 1.421875 -1.171875 2.234375q-0.921875 0.8124924 -2.28125 0.8124924q-1.703125 0 -2.75 -1.1093674q-1.03125 -1.125 -1.03125 -3.203125q0 -1.34375 0.4375 -2.34375q0.453125 -1.015625 1.359375 -1.515625q0.921875 -0.5 1.984375 -0.5q1.359375 0 2.21875 0.6875q0.859375 0.671875 1.09375 1.9375l-1.359375 0.203125q-0.203125 -0.828125 -0.703125 -1.25q-0.484375 -0.421875 -1.1875 -0.421875q-1.0625 0 -1.734375 0.765625q-0.65625 0.75 -0.65625 2.40625q0 1.671875 0.640625 2.4375q0.640625 0.75 1.671875 0.75q0.828125 0 1.375 -0.5q0.5625 -0.515625 0.703125 -1.578125zm2.59375 3.0468674l0 -11.453117l1.40625 0l0 4.109375q0.984375 -1.140625 2.484375 -1.140625q0.921875 0 1.59375 0.359375q0.6875 0.359375 0.96875 1.0q0.296875 0.640625 0.296875 1.859375l0 5.2656174l-1.40625 0l0 -5.2656174q0 -1.046875 -0.453125 -1.53125q-0.453125 -0.484375 -1.296875 -0.484375q-0.625 0 -1.171875 0.328125q-0.546875 0.328125 -0.78125 0.890625q-0.234375 0.546875 -0.234375 1.515625l0 4.5468674l-1.40625 0zm14.5703125 -2.6718674l1.453125 0.171875q-0.34375 1.28125 -1.28125 1.984375q-0.921875 0.7031174 -2.359375 0.7031174q-1.828125 0 -2.890625 -1.1249924q-1.0625 -1.125 -1.0625 -3.140625q0 -2.09375 1.078125 -3.25q1.078125 -1.15625 2.796875 -1.15625q1.65625 0 2.703125 1.140625q1.0625 1.125 1.0625 3.171875q0 0.125 0 0.375l-6.1875 0q0.078125 1.375 0.765625 2.109375q0.703125 0.71875 1.734375 0.71875q0.78125 0 1.328125 -0.40625q0.546875 -0.40625 0.859375 -1.296875zm-4.609375 -2.28125l4.625 0q-0.09375 -1.046875 -0.53125 -1.5625q-0.671875 -0.8125 -1.734375 -0.8125q-0.96875 0 -1.640625 0.65625q-0.65625 0.640625 -0.71875 1.71875zm13.2109375 4.9531174l0 -1.0468674q-0.78125 1.2343674 -2.3125 1.2343674q-1.0 0 -1.828125 -0.546875q-0.828125 -0.5468674 -1.296875 -1.5312424q-0.453125 -0.984375 -0.453125 -2.25q0 -1.25 0.40625 -2.25q0.421875 -1.015625 1.25 -1.546875q0.828125 -0.546875 1.859375 -0.546875q0.75 0 1.328125 0.3125q0.59375 0.3125 0.953125 0.828125l0 -4.109375l1.40625 0l0 11.453117l-1.3125 0zm-4.4375 -4.1406174q0 1.59375 0.671875 2.390625q0.671875 0.78125 1.578125 0.78125q0.921875 0 1.5625 -0.75q0.65625 -0.765625 0.65625 -2.3125q0 -1.703125 -0.65625 -2.5q-0.65625 -0.796875 -1.625 -0.796875q-0.9375 0 -1.5625 0.765625q-0.625 0.765625 -0.625 2.421875zm13.3984375 4.1406174l0 -1.2187424q-0.96875 1.4062424 -2.640625 1.4062424q-0.734375 0 -1.375 -0.28125q-0.625 -0.28125 -0.9375 -0.7031174q-0.3125 -0.4375 -0.4375 -1.046875q-0.078125 -0.421875 -0.078125 -1.3125l0 -5.140625l1.40625 0l0 4.59375q0 1.109375 0.078125 1.484375q0.140625 0.5625 0.5625 0.875q0.4375 0.3125 1.0625 0.3125q0.640625 0 1.1875 -0.3125q0.5625 -0.328125 0.78125 -0.890625q0.234375 -0.5625 0.234375 -1.625l0 -4.4375l1.40625 0l0 8.296867l-1.25 0zm3.4296875 0l0 -11.453117l1.40625 0l0 11.453117l-1.40625 0zm9.2578125 -2.6718674l1.453125 0.171875q-0.34375 1.28125 -1.28125 1.984375q-0.921875 0.7031174 -2.359375 0.7031174q-1.828125 0 -2.890625 -1.1249924q-1.0625 -1.125 -1.0625 -3.140625q0 -2.09375 1.078125 -3.25q1.078125 -1.15625 2.796875 -1.15625q1.65625 0 2.703125 1.140625q1.0625 1.125 1.0625 3.171875q0 0.125 0 0.375l-6.1875 0q0.078125 1.375 0.765625 2.109375q0.703125 0.71875 1.734375 0.71875q0.78125 0 1.328125 -0.40625q0.546875 -0.40625 0.859375 -1.296875zm-4.609375 -2.28125l4.625 0q-0.09375 -1.046875 -0.53125 -1.5625q-0.671875 -0.8125 -1.734375 -0.8125q-0.96875 0 -1.640625 0.65625q-0.65625 0.640625 -0.71875 1.71875z" fill-rule="nonzero"/><path fill="#000000" d="m136.96883 125.607994l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.9218674 -2.546875 0.9218674q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.43749237 -1.171875 -1.2499924q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm9.0078125 4.8749924l-1.359375 -8.296867l1.375 0l0.71875 4.5625q0.109375 0.75 0.28125 2.453125q0.40625 -0.890625 1.046875 -2.03125l2.765625 -4.984375l1.5 0l-4.75 8.296867l-1.578125 0zm12.0 -2.8281174l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.9218674 -2.546875 0.9218674q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.43749237 -1.171875 -1.2499924q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625zm6.9140625 4.8749924l1.734375 -8.296867l1.25 0l-0.34375 1.6875q0.640625 -0.953125 1.25 -1.40625q0.609375 -0.46875 1.25 -0.46875q0.421875 0 1.046875 0.296875l-0.578125 1.3125q-0.375 -0.265625 -0.8125 -0.265625q-0.734375 0 -1.515625 0.828125q-0.78125 0.828125 -1.234375 2.984375l-0.703125 3.3281174l-1.34375 0zm4.796875 3.203125l0.09375 -1.328125q0.4375 0.125 0.84375 0.125q0.4375 0 0.703125 -0.1875q0.34375 -0.265625 0.75 -0.96875l0.453125 -0.8125l-1.390625 -8.328117l1.390625 0l0.625 4.1875q0.1875 1.25 0.328125 2.484375l3.703125 -6.671875l1.484375 0l-5.296875 9.406242q-0.765625 1.375 -1.359375 1.8125q-0.59375 0.453125 -1.359375 0.453125q-0.5 0 -0.96875 -0.171875zm12.9765625 -3.203125l1.734375 -8.296867l1.40625 0l-0.28125 1.359375q0.78125 -0.875 1.40625 -1.203125q0.625 -0.34375 1.34375 -0.34375q0.78125 0 1.296875 0.40625q0.515625 0.40625 0.6875 1.140625q0.625 -0.78125 1.328125 -1.15625q0.703125 -0.390625 1.46875 -0.390625q1.046875 0 1.5625 0.5q0.515625 0.484375 0.515625 1.375q0 0.375 -0.171875 1.265625l-1.125 5.3437424l-1.40625 0l1.140625 -5.4843674q0.140625 -0.671875 0.140625 -0.96875q0 -0.40625 -0.265625 -0.640625q-0.25 -0.234375 -0.71875 -0.234375q-0.625 0 -1.28125 0.390625q-0.65625 0.375 -1.03125 1.0q-0.359375 0.625 -0.625 1.90625l-0.84375 4.0312424l-1.421875 0l1.171875 -5.6093674q0.125 -0.5625 0.125 -0.8125q0 -0.40625 -0.25 -0.65625q-0.25 -0.25 -0.65625 -0.25q-0.609375 0 -1.265625 0.390625q-0.65625 0.375 -1.078125 1.0625q-0.40625 0.6875 -0.671875 1.953125l-0.828125 3.9218674l-1.40625 0zm15.34375 -9.859367l0.328125 -1.59375l1.40625 0l-0.328125 1.59375l-1.40625 0zm-2.0625 9.859367l1.734375 -8.296867l1.40625 0l-1.734375 8.296867l-1.40625 0zm3.6015625 0l1.734375 -8.296867l1.28125 0l-0.296875 1.4375q0.828125 -0.84375 1.546875 -1.234375q0.734375 -0.390625 1.5 -0.390625q1.0 0 1.578125 0.546875q0.578125 0.546875 0.578125 1.453125q0 0.46875 -0.203125 1.453125l-1.0625 5.0312424l-1.40625 0l1.09375 -5.2656174q0.171875 -0.765625 0.171875 -1.125q0 -0.421875 -0.296875 -0.671875q-0.28125 -0.265625 -0.8125 -0.265625q-1.09375 0 -1.9375 0.78125q-0.84375 0.78125 -1.25 2.6875l-0.796875 3.8593674l-1.421875 0zm14.5703125 -1.4999924q-1.5 1.6874924 -3.0625 1.6874924q-0.953125 0 -1.546875 -0.546875q-0.59375 -0.5624924 -0.59375 -1.3593674q0 -0.515625 0.265625 -1.796875l1.0 -4.78125l1.421875 0l-1.109375 5.296875q-0.140625 0.671875 -0.140625 1.03125q0 0.46875 0.28125 0.734375q0.28125 0.265625 0.84375 0.265625q0.59375 0 1.15625 -0.28125q0.5625 -0.296875 0.96875 -0.78125q0.421875 -0.5 0.671875 -1.171875q0.171875 -0.4375 0.40625 -1.515625l0.75 -3.578125l1.40625 0l-1.734375 8.296867l-1.296875 0l0.3125 -1.4999924zm6.5390625 0.34375l-0.234375 1.1718674q-0.515625 0.125 -0.984375 0.125q-0.84375 0 -1.34375 -0.421875q-0.375 -0.31249237 -0.375 -0.8437424q0 -0.28125 0.203125 -1.265625l1.0 -4.8125l-1.109375 0l0.21875 -1.09375l1.125 0l0.421875 -2.046875l1.625 -0.96875l-0.640625 3.015625l1.390625 0l-0.234375 1.09375l-1.375 0l-0.96875 4.578125q-0.171875 0.875 -0.171875 1.046875q0 0.25 0.140625 0.390625q0.140625 0.125 0.46875 0.125q0.46875 0 0.84375 -0.09375zm7.2265625 -1.671875l1.375 0.140625q-0.296875 1.03125 -1.359375 1.953125q-1.0625 0.9218674 -2.546875 0.9218674q-0.921875 0 -1.6875 -0.421875q-0.765625 -0.43749237 -1.171875 -1.2499924q-0.40625 -0.8125 -0.40625 -1.84375q0 -1.359375 0.625 -2.640625q0.640625 -1.28125 1.640625 -1.890625q1.0 -0.625 2.15625 -0.625q1.484375 0 2.375 0.921875q0.890625 0.921875 0.890625 2.515625q0 0.609375 -0.109375 1.25l-6.109375 0q-0.03125 0.234375 -0.03125 0.4375q0 1.15625 0.53125 1.78125q0.53125 0.609375 1.3125 0.609375q0.71875 0 1.421875 -0.46875q0.703125 -0.484375 1.09375 -1.390625zm-4.109375 -2.046875l4.65625 0q0.015625 -0.21875 0.015625 -0.3125q0 -1.0625 -0.53125 -1.625q-0.53125 -0.578125 -1.375 -0.578125q-0.90625 0 -1.65625 0.625q-0.734375 0.625 -1.109375 1.890625z" fill-rule="nonzero"/><path fill="#f3f3f3" d="m25.343832 152.92389l418.2677 0l0 129.07086l-418.2677 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m25.343832 152.92389l418.2677 0l0 129.07086l-418.2677 0z" fill-rule="evenodd"/><path fill="#000000" d="m34.312584 177.28389l4.40625 -11.453125l1.640625 0l4.6875 11.453125l-1.734375 0l-1.328125 -3.46875l-4.796875 0l-1.25 3.46875l-1.625 0zm3.3125 -4.703125l3.890625 0l-1.203125 -3.171875q-0.546875 -1.453125 -0.8125 -2.375q-0.21875 1.09375 -0.609375 2.1875l-1.265625 3.359375zm13.859375 1.65625l1.390625 0.1875q-0.234375 1.421875 -1.171875 2.234375q-0.921875 0.8125 -2.28125 0.8125q-1.703125 0 -2.75 -1.109375q-1.03125 -1.125 -1.03125 -3.203125q0 -1.34375 0.4375 -2.34375q0.453125 -1.015625 1.359375 -1.515625q0.921875 -0.5 1.984375 -0.5q1.359375 0 2.21875 0.6875q0.859375 0.671875 1.09375 1.9375l-1.359375 0.203125q-0.203125 -0.828125 -0.703125 -1.25q-0.484375 -0.421875 -1.1875 -0.421875q-1.0625 0 -1.734375 0.765625q-0.65625 0.75 -0.65625 2.40625q0 1.671875 0.640625 2.4375q0.640625 0.75 1.671875 0.75q0.828125 0 1.375 -0.5q0.5625 -0.515625 0.703125 -1.578125zm5.65625 1.78125l0.203125 1.25q-0.59375 0.125 -1.0625 0.125q-0.765625 0 -1.1875 -0.234375q-0.421875 -0.25 -0.59375 -0.640625q-0.171875 -0.40625 -0.171875 -1.671875l0 -4.765625l-1.03125 0l0 -1.09375l1.03125 0l0 -2.0625l1.40625 -0.84375l0 2.90625l1.40625 0l0 1.09375l-1.40625 0l0 4.84375q0 0.609375 0.0625 0.78125q0.078125 0.171875 0.25 0.28125q0.171875 0.09375 0.484375 0.09375q0.234375 0 0.609375 -0.0625zm1.3828125 -8.578125l0 -1.609375l1.40625 0l0 1.609375l-1.40625 0zm0 9.84375l0 -8.296875l1.40625 0l0 8.296875l-1.40625 0zm3.0234375 -4.15625q0 -2.296875 1.28125 -3.40625q1.078125 -0.921875 2.609375 -0.921875q1.71875 0 2.796875 1.125q1.09375 1.109375 1.09375 3.09375q0 1.59375 -0.484375 2.515625q-0.484375 0.921875 -1.40625 1.4375q-0.90625 0.5 -2.0 0.5q-1.734375 0 -2.8125 -1.109375q-1.078125 -1.125 -1.078125 -3.234375zm1.453125 0q0 1.59375 0.6875 2.390625q0.703125 0.796875 1.75 0.796875q1.046875 0 1.734375 -0.796875q0.703125 -0.796875 0.703125 -2.4375q0 -1.53125 -0.703125 -2.328125q-0.6875 -0.796875 -1.734375 -0.796875q-1.046875 0 -1.75 0.796875q-0.6875 0.78125 -0.6875 2.375zm7.9765625 4.15625l0 -8.296875l1.265625 0l0 1.171875q0.90625 -1.359375 2.640625 -1.359375q0.75 0 1.375 0.265625q0.625 0.265625 0.9375 0.703125q0.3125 0.4375 0.4375 1.046875q0.078125 0.390625 0.078125 1.359375l0 5.109375l-1.40625 0l0 -5.046875q0 -0.859375 -0.171875 -1.28125q-0.15625 -0.4375 -0.578125 -0.6875q-0.40625 -0.25 -0.96875 -0.25q-0.90625 0 -1.5625 0.578125q-0.640625 0.5625 -0.640625 2.15625l0 4.53125l-1.40625 0zm8.3359375 -2.484375l1.390625 -0.21875q0.109375 0.84375 0.640625 1.296875q0.546875 0.4375 1.5 0.4375q0.96875 0 1.4375 -0.390625q0.46875 -0.40625 0.46875 -0.9375q0 -0.46875 -0.40625 -0.75q-0.296875 -0.1875 -1.4375 -0.46875q-1.546875 -0.390625 -2.15625 -0.671875q-0.59375 -0.296875 -0.90625 -0.796875q-0.296875 -0.5 -0.296875 -1.109375q0 -0.5625 0.25 -1.03125q0.25 -0.46875 0.6875 -0.78125q0.328125 -0.25 0.890625 -0.40625q0.578125 -0.171875 1.21875 -0.171875q0.984375 0 1.71875 0.28125q0.734375 0.28125 1.078125 0.765625q0.359375 0.46875 0.5 1.28125l-1.375 0.1875q-0.09375 -0.640625 -0.546875 -1.0q-0.453125 -0.359375 -1.265625 -0.359375q-0.96875 0 -1.390625 0.328125q-0.40625 0.3125 -0.40625 0.734375q0 0.28125 0.171875 0.5q0.171875 0.21875 0.53125 0.375q0.21875 0.078125 1.25 0.359375q1.484375 0.390625 2.078125 0.65625q0.59375 0.25 0.921875 0.734375q0.34375 0.484375 0.34375 1.203125q0 0.703125 -0.421875 1.328125q-0.40625 0.609375 -1.1875 0.953125q-0.765625 0.34375 -1.734375 0.34375q-1.625 0 -2.46875 -0.671875q-0.84375 -0.671875 -1.078125 -2.0z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m131.97372 163.3517l296.9449 0l0 106.92915l-296.9449 0z" fill-rule="evenodd"/><path stroke="#999999" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m131.97372 163.3517l296.9449 0l0 106.92915l-296.9449 0z" fill-rule="evenodd"/><path fill="#000000" d="m144.0831 183.8717l0 -7.578125l-2.828125 0l0 -1.015625l6.8125 0l0 1.015625l-2.84375 0l0 7.578125l-1.140625 0zm4.3085938 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm6.046875 0l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm6.158203 -1.3125l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m241.94247 181.76233l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.173828 3.65625l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm14.199219 -0.78125q-0.546875 0.484375 -1.0625 0.703125q-0.5 0.21875 -1.09375 0.21875q-0.859375 0 -1.390625 -0.5q-0.515625 -0.515625 -0.515625 -1.3125q0 -0.515625 0.234375 -0.90625q0.234375 -0.40625 0.625 -0.640625q0.390625 -0.25 0.96875 -0.359375q0.359375 -0.078125 1.359375 -0.109375q1.0 -0.046875 1.421875 -0.21875q0.125 -0.4375 0.125 -0.71875q0 -0.375 -0.265625 -0.578125q-0.375 -0.296875 -1.078125 -0.296875q-0.671875 0 -1.09375 0.296875q-0.421875 0.296875 -0.625 0.84375l-1.0625 -0.09375q0.328125 -0.921875 1.03125 -1.40625q0.71875 -0.5 1.796875 -0.5q1.15625 0 1.828125 0.546875q0.515625 0.40625 0.515625 1.0625q0 0.5 -0.140625 1.15625l-0.34375 1.546875q-0.171875 0.734375 -0.171875 1.203125q0 0.296875 0.140625 0.84375l-1.078125 0q-0.09375 -0.3125 -0.125 -0.78125zm0.390625 -2.375q-0.21875 0.09375 -0.484375 0.140625q-0.25 0.046875 -0.84375 0.09375q-0.921875 0.078125 -1.3125 0.21875q-0.375 0.125 -0.578125 0.40625q-0.1875 0.265625 -0.1875 0.59375q0 0.453125 0.3125 0.734375q0.3125 0.28125 0.875 0.28125q0.53125 0 1.015625 -0.265625q0.484375 -0.28125 0.765625 -0.78125q0.28125 -0.5 0.4375 -1.421875zm3.5957031 -4.234375l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6191406 0l1.796875 -8.59375l1.0625 0l-1.796875 8.59375l-1.0625 0z" fill-rule="nonzero"/><path fill="#000000" d="m148.03622 199.85608l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.0097656 -0.09375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.673828 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.923828 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.939453 1.421875l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm4.234375 1.34375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm1.0371094 -6.4375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.2753906 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 3.109375l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.955078 -5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0zm0 5.015625l0 -1.203125l1.203125 0l0 1.203125l-1.203125 0z" fill-rule="nonzero"/><path fill="#000000" d="m237.37997 202.8717l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm6.861328 -2.359375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.533203 0.34375l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm8.375 1.265625l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm3.9824219 2.4375l1.296875 -6.21875l1.0625 0l-0.21875 1.015625q0.59375 -0.65625 1.0625 -0.90625q0.46875 -0.25 1.015625 -0.25q0.578125 0 0.96875 0.3125q0.390625 0.296875 0.515625 0.84375q0.46875 -0.578125 0.984375 -0.859375q0.53125 -0.296875 1.109375 -0.296875q0.78125 0 1.171875 0.375q0.390625 0.359375 0.390625 1.03125q0 0.28125 -0.140625 0.9375l-0.828125 4.015625l-1.0625 0l0.859375 -4.109375q0.109375 -0.515625 0.109375 -0.71875q0 -0.3125 -0.203125 -0.484375q-0.1875 -0.171875 -0.53125 -0.171875q-0.484375 0 -0.984375 0.28125q-0.484375 0.28125 -0.765625 0.75q-0.265625 0.46875 -0.46875 1.4375l-0.625 3.015625l-1.0625 0l0.875 -4.203125q0.09375 -0.421875 0.09375 -0.609375q0 -0.296875 -0.1875 -0.484375q-0.1875 -0.1875 -0.5 -0.1875q-0.453125 0 -0.953125 0.28125q-0.484375 0.28125 -0.796875 0.796875q-0.3125 0.515625 -0.515625 1.46875l-0.609375 2.9375l-1.0625 0zm9.605469 2.40625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm5.796875 -2.40625l0.25 -1.203125l1.203125 0l-0.25 1.203125l-1.203125 0zm7.3339844 -2.265625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm1.90625 -0.09375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125z" fill-rule="nonzero"/><path fill="#000000" d="m236.84872 219.26233l1.796875 -8.609375l0.984375 0l-0.1875 0.859375q0.546875 -0.5625 0.96875 -0.78125q0.4375 -0.21875 0.921875 -0.21875q0.890625 0 1.46875 0.65625q0.59375 0.640625 0.59375 1.84375q0 0.96875 -0.328125 1.765625q-0.3125 0.796875 -0.78125 1.296875q-0.46875 0.484375 -0.953125 0.71875q-0.484375 0.21875 -0.984375 0.21875q-1.125 0 -1.734375 -1.140625l-0.703125 3.390625l-1.0625 0zm2.09375 -4.9375q0 0.703125 0.109375 0.96875q0.140625 0.375 0.46875 0.609375q0.34375 0.234375 0.78125 0.234375q0.921875 0 1.484375 -1.015625q0.5625 -1.03125 0.5625 -2.109375q0 -0.78125 -0.390625 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.40625 0 -0.75 0.21875q-0.34375 0.21875 -0.640625 0.640625q-0.296875 0.421875 -0.5 1.046875q-0.1875 0.609375 -0.1875 1.0625zm5.298828 0.1875q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm6.4648438 -0.859375l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm4.435547 -0.015625l1.078125 -0.109375q-0.015625 0.234375 -0.015625 0.28125q0 0.375 0.1875 0.78125q0.203125 0.390625 0.53125 0.59375q0.34375 0.203125 0.71875 0.203125q0.5 0 1.0 -0.328125q0.515625 -0.34375 0.828125 -0.984375q0.3125 -0.65625 0.3125 -1.296875q0 -0.71875 -0.421875 -1.15625q-0.421875 -0.4375 -1.109375 -0.4375q-0.46875 0 -0.890625 0.234375q-0.40625 0.21875 -0.765625 0.65625l-0.921875 -0.0625l1.296875 -4.390625l4.203125 0l-0.21875 0.96875l-3.25 0l-0.65625 2.1875q0.375 -0.265625 0.75 -0.390625q0.390625 -0.140625 0.796875 -0.140625q0.984375 0 1.609375 0.65625q0.640625 0.640625 0.640625 1.78125q0 0.984375 -0.4375 1.828125q-0.4375 0.828125 -1.203125 1.28125q-0.75 0.4375 -1.625 0.4375q-0.75 0 -1.3125 -0.328125q-0.5625 -0.328125 -0.859375 -0.90625q-0.28125 -0.59375 -0.28125 -1.1875q0 -0.0625 0.015625 -0.171875zm8.533203 -2.265625q-0.46875 -0.265625 -0.71875 -0.671875q-0.234375 -0.421875 -0.234375 -0.890625q0 -0.78125 0.5625 -1.453125q0.75 -0.890625 2.03125 -0.890625q1.125 0 1.78125 0.609375q0.65625 0.609375 0.65625 1.484375q0 0.625 -0.359375 1.125q-0.34375 0.5 -1.09375 0.8125q0.515625 0.3125 0.734375 0.671875q0.28125 0.484375 0.28125 1.109375q0 1.15625 -0.8125 2.0625q-0.8125 0.90625 -2.171875 0.90625q-1.09375 0 -1.78125 -0.671875q-0.6875 -0.671875 -0.6875 -1.671875q0 -0.9375 0.484375 -1.609375q0.5 -0.6875 1.328125 -0.921875zm0.09375 -1.515625q0 0.546875 0.34375 0.890625q0.359375 0.328125 0.984375 0.328125q0.71875 0 1.171875 -0.4375q0.453125 -0.453125 0.453125 -1.09375q0 -0.546875 -0.359375 -0.890625q-0.34375 -0.34375 -0.96875 -0.34375q-0.46875 0 -0.859375 0.21875q-0.390625 0.21875 -0.578125 0.59375q-0.1875 0.375 -0.1875 0.734375zm-0.828125 4.140625q0 0.359375 0.171875 0.6875q0.171875 0.328125 0.515625 0.515625q0.359375 0.1875 0.8125 0.1875q0.859375 0 1.390625 -0.796875q0.421875 -0.640625 0.421875 -1.375q0 -0.59375 -0.390625 -0.96875q-0.390625 -0.390625 -1.015625 -0.390625q-0.796875 0 -1.359375 0.59375q-0.546875 0.59375 -0.546875 1.546875zm6.439453 2.09375q0.140625 -0.875 0.609375 -2.09375q0.546875 -1.46875 1.421875 -2.90625q0.890625 -1.453125 1.96875 -2.5l-4.28125 0l0.203125 -0.96875l5.484375 0l-0.203125 0.96875q-1.390625 1.21875 -2.4375 3.15625q-1.203125 2.234375 -1.65625 4.34375l-1.109375 0z" fill-rule="nonzero"/><path fill="#000000" d="m141.8956 236.8717l0 -8.59375l3.25 0q0.84375 0 1.296875 0.078125q0.640625 0.109375 1.0625 0.40625q0.4375 0.296875 0.6875 0.828125q0.265625 0.53125 0.265625 1.171875q0 1.09375 -0.703125 1.859375q-0.6875 0.75 -2.515625 0.75l-2.203125 0l0 3.5l-1.140625 0zm1.140625 -4.5l2.21875 0q1.109375 0 1.5625 -0.40625q0.46875 -0.421875 0.46875 -1.171875q0 -0.53125 -0.28125 -0.90625q-0.265625 -0.390625 -0.703125 -0.515625q-0.296875 -0.078125 -1.0625 -0.078125l-2.203125 0l0 3.078125zm6.7226562 4.5l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm3.6210938 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.986328 5.5l0 -8.609375l0.953125 0l0 0.796875q0.34375 -0.46875 0.765625 -0.703125q0.4375 -0.234375 1.046875 -0.234375q0.796875 0 1.40625 0.40625q0.609375 0.40625 0.90625 1.15625q0.3125 0.75 0.3125 1.640625q0 0.953125 -0.34375 1.71875q-0.328125 0.765625 -0.984375 1.171875q-0.65625 0.40625 -1.375 0.40625q-0.53125 0 -0.953125 -0.21875q-0.421875 -0.234375 -0.6875 -0.5625l0 3.03125l-1.046875 0zm0.953125 -5.46875q0 1.203125 0.484375 1.78125q0.484375 0.5625 1.171875 0.5625q0.703125 0 1.203125 -0.59375q0.5 -0.59375 0.5 -1.84375q0 -1.1875 -0.484375 -1.765625q-0.484375 -0.59375 -1.171875 -0.59375q-0.671875 0 -1.1875 0.625q-0.515625 0.625 -0.515625 1.828125zm9.970703 1.078125l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.861328 3.703125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm6.3085938 -0.9375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm1.0371094 -6.4375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm6.9160156 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z" fill-rule="nonzero"/><path fill="#000000" d="m237.47372 234.7467l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm10.140625 1.0q-1.109375 1.265625 -2.28125 1.265625q-0.71875 0 -1.171875 -0.40625q-0.4375 -0.421875 -0.4375 -1.015625q0 -0.390625 0.203125 -1.34375l0.75 -3.59375l1.0625 0l-0.84375 3.96875q-0.09375 0.5 -0.09375 0.78125q0 0.34375 0.203125 0.546875q0.21875 0.1875 0.640625 0.1875q0.4375 0 0.859375 -0.203125q0.4375 -0.21875 0.734375 -0.59375q0.3125 -0.375 0.5 -0.875q0.140625 -0.3125 0.296875 -1.125l0.5625 -2.6875l1.0625 0l-1.296875 6.21875l-0.984375 0l0.234375 -1.125zm2.4394531 1.125l1.78125 -8.59375l1.0625 0l-0.640625 3.078125q0.515625 -0.46875 0.921875 -0.65625q0.40625 -0.1875 0.859375 -0.1875q0.859375 0 1.4375 0.65625q0.59375 0.640625 0.59375 1.875q0 0.828125 -0.234375 1.515625q-0.234375 0.6875 -0.578125 1.15625q-0.34375 0.46875 -0.71875 0.75q-0.359375 0.265625 -0.75 0.40625q-0.390625 0.140625 -0.75 0.140625q-0.609375 0 -1.078125 -0.328125q-0.46875 -0.328125 -0.734375 -0.984375l-0.25 1.171875l-0.921875 0zm1.5625 -2.5625l0 0.203125q0 0.796875 0.375 1.21875q0.375 0.40625 0.953125 0.40625q0.546875 0 1.015625 -0.375q0.46875 -0.390625 0.765625 -1.21875q0.296875 -0.828125 0.296875 -1.53125q0 -0.78125 -0.375 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.5625 0 -1.046875 0.453125q-0.484375 0.4375 -0.8125 1.359375q-0.234375 0.6875 -0.234375 1.140625zm6.611328 -4.828125l0.25 -1.203125l1.046875 0l-0.25 1.203125l-1.046875 0zm-3.359375 9.734375l0.1875 -0.90625q0.359375 0.09375 0.640625 0.09375q0.34375 0 0.546875 -0.234375q0.203125 -0.21875 0.421875 -1.234375l1.296875 -6.28125l1.0625 0l-1.34375 6.46875q-0.25 1.21875 -0.578125 1.65625q-0.4375 0.578125 -1.234375 0.578125q-0.40625 0 -1.0 -0.140625zm9.087891 -4.453125l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm9.470703 1.390625l1.0625 0.109375q-0.390625 1.171875 -1.125 1.734375q-0.734375 0.5625 -1.671875 0.5625q-1.015625 0 -1.65625 -0.65625q-0.625 -0.65625 -0.625 -1.828125q0 -1.03125 0.40625 -2.015625q0.40625 -0.984375 1.15625 -1.484375q0.75 -0.515625 1.71875 -0.515625q1.0 0 1.578125 0.5625q0.59375 0.5625 0.59375 1.5l-1.03125 0.0625q-0.015625 -0.59375 -0.359375 -0.921875q-0.328125 -0.328125 -0.875 -0.328125q-0.640625 0 -1.109375 0.40625q-0.46875 0.390625 -0.734375 1.21875q-0.265625 0.828125 -0.265625 1.578125q0 0.796875 0.34375 1.203125q0.359375 0.390625 0.875 0.390625q0.515625 0 0.984375 -0.390625q0.46875 -0.390625 0.734375 -1.1875zm4.1875 1.40625l-0.171875 0.859375q-0.375 0.109375 -0.734375 0.109375q-0.640625 0 -1.015625 -0.3125q-0.28125 -0.234375 -0.28125 -0.640625q0 -0.203125 0.15625 -0.9375l0.75 -3.625l-0.828125 0l0.171875 -0.8125l0.828125 0l0.328125 -1.53125l1.203125 -0.734375l-0.46875 2.265625l1.046875 0l-0.171875 0.8125l-1.046875 0l-0.71875 3.4375q-0.140625 0.65625 -0.140625 0.796875q0 0.1875 0.109375 0.28125q0.109375 0.09375 0.359375 0.09375q0.359375 0 0.625 -0.0625zm10.542969 -4.1875l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm8.185547 -4.5625l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm1.6835938 7.0l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm16.835938 -3.03125l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm7.9160156 1.234375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/><path fill="#000000" d="m237.37997 250.8717l1.78125 -8.59375l1.0625 0l-0.640625 3.078125q0.515625 -0.46875 0.921875 -0.65625q0.40625 -0.1875 0.859375 -0.1875q0.859375 0 1.4375 0.65625q0.59375 0.640625 0.59375 1.875q0 0.828125 -0.234375 1.515625q-0.234375 0.6875 -0.578125 1.15625q-0.34375 0.46875 -0.71875 0.75q-0.359375 0.265625 -0.75 0.40625q-0.390625 0.140625 -0.75 0.140625q-0.609375 0 -1.078125 -0.328125q-0.46875 -0.328125 -0.734375 -0.984375l-0.25 1.171875l-0.921875 0zm1.5625 -2.5625l0 0.203125q0 0.796875 0.375 1.21875q0.375 0.40625 0.953125 0.40625q0.546875 0 1.015625 -0.375q0.46875 -0.390625 0.765625 -1.21875q0.296875 -0.828125 0.296875 -1.53125q0 -0.78125 -0.375 -1.21875q-0.375 -0.4375 -0.9375 -0.4375q-0.5625 0 -1.046875 0.453125q-0.484375 0.4375 -0.8125 1.359375q-0.234375 0.6875 -0.234375 1.140625zm5.298828 0.203125q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm9.626953 1.5625q-0.90625 1.046875 -1.890625 1.046875q-0.890625 0 -1.484375 -0.65625q-0.578125 -0.65625 -0.578125 -1.890625q0 -1.140625 0.453125 -2.078125q0.46875 -0.9375 1.171875 -1.40625q0.703125 -0.46875 1.40625 -0.46875q1.15625 0 1.75 1.109375l0.703125 -3.34375l1.046875 0l-1.78125 8.59375l-0.984375 0l0.1875 -0.90625zm-2.90625 -1.703125q0 0.65625 0.125 1.03125q0.140625 0.375 0.4375 0.625q0.3125 0.25 0.75 0.25q0.71875 0 1.3125 -0.75q0.78125 -1.0 0.78125 -2.46875q0 -0.734375 -0.390625 -1.140625q-0.390625 -0.421875 -0.96875 -0.421875q-0.375 0 -0.703125 0.171875q-0.3125 0.15625 -0.625 0.5625q-0.296875 0.40625 -0.515625 1.046875q-0.203125 0.625 -0.203125 1.09375zm4.986328 5.015625l0.0625 -1.0q0.328125 0.09375 0.640625 0.09375q0.328125 0 0.515625 -0.140625q0.265625 -0.203125 0.5625 -0.734375l0.34375 -0.609375l-1.03125 -6.234375l1.046875 0l0.46875 3.140625q0.140625 0.9375 0.234375 1.859375l2.78125 -5.0l1.109375 0l-3.96875 7.046875q-0.578125 1.03125 -1.03125 1.359375q-0.4375 0.34375 -1.015625 0.34375q-0.359375 0 -0.71875 -0.125zm16.083984 -7.453125l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm0 2.609375l-5.671875 0l0 -0.984375l5.671875 0l0 0.984375zm8.185547 -4.5625l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.1875 -0.953125 0.609375 -1.390625q0.421875 -0.4375 0.984375 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.921875 1.28125l0.65625 0zm-1.875 0l-0.25 1.203125l-1.203125 0l0.21875 -1.046875q0.203125 -0.953125 0.609375 -1.390625q0.421875 -0.4375 1.0 -0.5l-0.09375 0.453125q-0.6875 0.1875 -0.9375 1.28125l0.65625 0zm8.511719 3.96875l1.140625 0.171875q-0.546875 1.484375 -1.578125 2.25q-1.03125 0.75 -2.3125 0.75q-1.5625 0 -2.484375 -0.953125q-0.90625 -0.953125 -0.90625 -2.734375q0 -2.3125 1.390625 -3.84375q1.25 -1.34375 3.09375 -1.34375q1.359375 0 2.203125 0.734375q0.84375 0.734375 0.984375 1.96875l-1.078125 0.09375q-0.15625 -0.9375 -0.6875 -1.390625q-0.53125 -0.453125 -1.375 -0.453125q-1.578125 0 -2.546875 1.390625q-0.84375 1.203125 -0.84375 2.875q0 1.3125 0.640625 2.015625q0.65625 0.703125 1.703125 0.703125q0.890625 0 1.609375 -0.578125q0.71875 -0.59375 1.046875 -1.65625zm1.9472656 3.03125l1.796875 -8.59375l3.59375 0q0.921875 0 1.390625 0.21875q0.46875 0.21875 0.765625 0.734375q0.3125 0.515625 0.3125 1.15625q0 0.53125 -0.21875 1.09375q-0.21875 0.546875 -0.546875 0.90625q-0.328125 0.359375 -0.671875 0.546875q-0.34375 0.171875 -0.71875 0.25q-0.828125 0.203125 -1.671875 0.203125l-2.15625 0l-0.71875 3.484375l-1.15625 0zm2.078125 -4.453125l1.890625 0q1.109375 0 1.625 -0.234375q0.515625 -0.25 0.828125 -0.734375q0.3125 -0.484375 0.3125 -1.03125q0 -0.421875 -0.171875 -0.6875q-0.15625 -0.265625 -0.46875 -0.390625q-0.296875 -0.125 -1.140625 -0.125l-2.203125 0l-0.671875 3.203125zm7.7851562 -4.140625l1.15625 0l-1.109375 5.28125q-0.125 0.65625 -0.125 0.96875q0 0.703125 0.53125 1.125q0.546875 0.421875 1.375 0.421875q0.65625 0 1.21875 -0.296875q0.578125 -0.3125 0.90625 -0.890625q0.328125 -0.59375 0.59375 -1.875l0.984375 -4.734375l1.140625 0l-1.046875 5.046875q-0.265625 1.28125 -0.703125 2.046875q-0.4375 0.75 -1.21875 1.203125q-0.78125 0.453125 -1.8125 0.453125q-0.96875 0 -1.6875 -0.328125q-0.703125 -0.328125 -1.0625 -0.890625q-0.359375 -0.5625 -0.359375 -1.28125q0 -0.453125 0.25 -1.5625l0.96875 -4.6875zm10.21875 6.234375q0 -1.828125 1.0625 -3.015625q0.890625 -0.984375 2.328125 -0.984375q1.125 0 1.8125 0.703125q0.6875 0.703125 0.6875 1.890625q0 1.078125 -0.4375 2.0q-0.4375 0.921875 -1.234375 1.421875q-0.796875 0.484375 -1.6875 0.484375q-0.71875 0 -1.3125 -0.3125q-0.59375 -0.3125 -0.90625 -0.875q-0.3125 -0.578125 -0.3125 -1.3125zm1.046875 -0.109375q0 0.875 0.421875 1.328125q0.421875 0.453125 1.078125 0.453125q0.34375 0 0.671875 -0.125q0.34375 -0.140625 0.625 -0.421875q0.28125 -0.28125 0.484375 -0.640625q0.203125 -0.359375 0.328125 -0.78125q0.1875 -0.578125 0.1875 -1.109375q0 -0.84375 -0.4375 -1.3125q-0.421875 -0.46875 -1.0625 -0.46875q-0.5 0 -0.90625 0.25q-0.40625 0.234375 -0.75 0.6875q-0.328125 0.453125 -0.484375 1.0625q-0.15625 0.609375 -0.15625 1.078125zm5.439453 2.46875l1.296875 -6.21875l0.953125 0l-0.21875 1.078125q0.625 -0.640625 1.171875 -0.921875q0.546875 -0.296875 1.109375 -0.296875q0.75 0 1.171875 0.40625q0.4375 0.40625 0.4375 1.09375q0 0.34375 -0.140625 1.09375l-0.796875 3.765625l-1.0625 0l0.828125 -3.9375q0.125 -0.578125 0.125 -0.859375q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.8125 0 -1.453125 0.59375q-0.640625 0.578125 -0.9375 2.0l-0.609375 2.890625l-1.046875 0zm10.4296875 -3.59375q0.453125 0 0.78125 -0.234375q0.34375 -0.234375 0.546875 -0.703125q0.171875 -0.34375 0.375 -1.3125q0.21875 -0.984375 0.34375 -1.34375q0.15625 -0.484375 0.375 -0.78125q0.21875 -0.3125 0.46875 -0.46875q0.25 -0.171875 0.625 -0.25q0.203125 -0.046875 0.671875 -0.046875l0.328125 0l-0.203125 0.921875l-0.171875 0q-0.5 0 -0.71875 0.125q-0.21875 0.109375 -0.34375 0.375q-0.109375 0.25 -0.3125 1.125q-0.234375 1.171875 -0.453125 1.6875q-0.203125 0.5 -0.53125 0.828125q-0.3125 0.3125 -0.90625 0.5625q0.421875 0.203125 0.625 0.53125q0.203125 0.328125 0.203125 0.875q0 0.453125 -0.265625 1.671875q-0.1875 0.859375 -0.1875 1.078125q0 0.21875 0.09375 0.34375q0.0625 0.109375 0.21875 0.15625q0.171875 0.0625 0.703125 0.0625l-0.1875 0.921875l-0.328125 0q-0.546875 0 -0.765625 -0.078125q-0.34375 -0.125 -0.546875 -0.40625q-0.1875 -0.265625 -0.1875 -0.734375q0 -0.390625 0.265625 -1.671875q0.203125 -0.96875 0.203125 -1.3125q0 -0.46875 -0.234375 -0.703125q-0.234375 -0.25 -0.6875 -0.25l0.203125 -0.96875zm4.0078125 0q0.453125 0 0.78125 -0.234375q0.34375 -0.234375 0.546875 -0.703125q0.171875 -0.34375 0.375 -1.3125q0.21875 -0.984375 0.34375 -1.34375q0.15625 -0.484375 0.375 -0.78125q0.21875 -0.3125 0.46875 -0.46875q0.25 -0.171875 0.625 -0.25q0.203125 -0.046875 0.671875 -0.046875l0.328125 0l-0.203125 0.921875l-0.171875 0q-0.5 0 -0.71875 0.125q-0.21875 0.109375 -0.34375 0.375q-0.109375 0.25 -0.3125 1.125q-0.234375 1.171875 -0.453125 1.6875q-0.203125 0.5 -0.53125 0.828125q-0.3125 0.3125 -0.90625 0.5625q0.421875 0.203125 0.625 0.53125q0.203125 0.328125 0.203125 0.875q0 0.453125 -0.265625 1.671875q-0.1875 0.859375 -0.1875 1.078125q0 0.21875 0.09375 0.34375q0.0625 0.109375 0.21875 0.15625q0.171875 0.0625 0.703125 0.0625l-0.1875 0.921875l-0.328125 0q-0.546875 0 -0.765625 -0.078125q-0.34375 -0.125 -0.546875 -0.40625q-0.1875 -0.265625 -0.1875 -0.734375q0 -0.390625 0.265625 -1.671875q0.203125 -0.96875 0.203125 -1.3125q0 -0.46875 -0.234375 -0.703125q-0.234375 -0.25 -0.6875 -0.25l0.203125 -0.96875zm3.6796875 1.46875l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm10.46875 0.015625l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.189453 3.65625l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm5.5585938 0l-1.015625 -6.21875l1.03125 0l0.53125 3.421875q0.09375 0.5625 0.21875 1.828125q0.296875 -0.65625 0.78125 -1.515625l2.078125 -3.734375l1.109375 0l-3.5625 6.21875l-1.171875 0zm9.0 -2.109375l1.03125 0.09375q-0.21875 0.765625 -1.03125 1.46875q-0.796875 0.6875 -1.890625 0.6875q-0.703125 0 -1.28125 -0.3125q-0.578125 -0.328125 -0.875 -0.9375q-0.296875 -0.609375 -0.296875 -1.390625q0 -1.015625 0.46875 -1.96875q0.46875 -0.96875 1.21875 -1.421875q0.75 -0.46875 1.625 -0.46875q1.109375 0 1.765625 0.6875q0.671875 0.6875 0.671875 1.890625q0 0.453125 -0.078125 0.9375l-4.578125 0q-0.03125 0.171875 -0.03125 0.328125q0 0.875 0.40625 1.328125q0.40625 0.453125 0.984375 0.453125q0.546875 0 1.0625 -0.34375q0.53125 -0.359375 0.828125 -1.03125zm-3.078125 -1.546875l3.484375 0q0.015625 -0.171875 0.015625 -0.234375q0 -0.796875 -0.40625 -1.21875q-0.390625 -0.421875 -1.015625 -0.421875q-0.6875 0 -1.25 0.46875q-0.5625 0.46875 -0.828125 1.40625zm5.189453 3.65625l1.296875 -6.21875l0.9375 0l-0.265625 1.265625q0.484375 -0.71875 0.9375 -1.0625q0.46875 -0.34375 0.9375 -0.34375q0.328125 0 0.78125 0.21875l-0.4375 0.984375q-0.265625 -0.1875 -0.59375 -0.1875q-0.5625 0 -1.15625 0.625q-0.578125 0.609375 -0.90625 2.21875l-0.53125 2.5l-1.0 0zm7.2929688 -3.59375l-0.203125 0.96875q-0.4375 0 -0.78125 0.234375q-0.328125 0.234375 -0.515625 0.640625q-0.1875 0.40625 -0.40625 1.390625q-0.21875 0.984375 -0.3125 1.3125q-0.171875 0.5 -0.390625 0.796875q-0.21875 0.3125 -0.484375 0.46875q-0.25 0.171875 -0.609375 0.265625q-0.203125 0.046875 -0.6875 0.046875l-0.328125 0l0.203125 -0.921875l0.1875 0q0.5 0 0.71875 -0.125q0.21875 -0.125 0.359375 -0.421875q0.09375 -0.21875 0.28125 -1.09375q0.234375 -1.109375 0.40625 -1.546875q0.234375 -0.59375 0.59375 -0.953125q0.359375 -0.359375 0.890625 -0.578125q-0.453125 -0.234375 -0.640625 -0.546875q-0.171875 -0.3125 -0.171875 -0.859375q0 -0.453125 0.25 -1.671875q0.1875 -0.84375 0.1875 -1.078125q0 -0.296875 -0.15625 -0.421875q-0.15625 -0.125 -0.671875 -0.125l-0.1875 0l0.203125 -0.921875l0.328125 0q0.546875 0 0.765625 0.078125q0.34375 0.109375 0.53125 0.390625q0.203125 0.265625 0.203125 0.734375q0 0.390625 -0.203125 1.296875q-0.28125 1.359375 -0.28125 1.6875q0 0.46875 0.234375 0.71875q0.234375 0.234375 0.6875 0.234375zm4.0078125 0l-0.203125 0.96875q-0.4375 0 -0.78125 0.234375q-0.328125 0.234375 -0.515625 0.640625q-0.1875 0.40625 -0.40625 1.390625q-0.21875 0.984375 -0.3125 1.3125q-0.171875 0.5 -0.390625 0.796875q-0.21875 0.3125 -0.484375 0.46875q-0.25 0.171875 -0.609375 0.265625q-0.203125 0.046875 -0.6875 0.046875l-0.328125 0l0.203125 -0.921875l0.1875 0q0.5 0 0.71875 -0.125q0.21875 -0.125 0.359375 -0.421875q0.09375 -0.21875 0.28125 -1.09375q0.234375 -1.109375 0.40625 -1.546875q0.234375 -0.59375 0.59375 -0.953125q0.359375 -0.359375 0.890625 -0.578125q-0.453125 -0.234375 -0.640625 -0.546875q-0.171875 -0.3125 -0.171875 -0.859375q0 -0.453125 0.25 -1.671875q0.1875 -0.84375 0.1875 -1.078125q0 -0.296875 -0.15625 -0.421875q-0.15625 -0.125 -0.671875 -0.125l-0.1875 0l0.203125 -0.921875l0.328125 0q0.546875 0 0.765625 0.078125q0.34375 0.109375 0.53125 0.390625q0.203125 0.265625 0.203125 0.734375q0 0.390625 -0.203125 1.296875q-0.28125 1.359375 -0.28125 1.6875q0 0.46875 0.234375 0.71875q0.234375 0.234375 0.6875 0.234375zm5.544922 -3.796875l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.8066406 -2.125l1.0625 -0.0625q0 0.453125 0.140625 0.78125q0.140625 0.3125 0.515625 0.515625q0.375 0.203125 0.875 0.203125q0.703125 0 1.046875 -0.28125q0.359375 -0.28125 0.359375 -0.65625q0 -0.28125 -0.203125 -0.515625q-0.21875 -0.25 -1.0625 -0.609375q-0.84375 -0.359375 -1.078125 -0.515625q-0.390625 -0.234375 -0.59375 -0.5625q-0.1875 -0.328125 -0.1875 -0.75q0 -0.734375 0.578125 -1.25q0.59375 -0.53125 1.640625 -0.53125q1.171875 0 1.78125 0.546875q0.625 0.53125 0.640625 1.421875l-1.03125 0.0625q-0.03125 -0.5625 -0.40625 -0.890625q-0.375 -0.328125 -1.0625 -0.328125q-0.546875 0 -0.859375 0.25q-0.296875 0.25 -0.296875 0.546875q0 0.296875 0.265625 0.515625q0.171875 0.15625 0.90625 0.46875q1.21875 0.53125 1.53125 0.828125q0.515625 0.5 0.515625 1.1875q0 0.46875 -0.296875 0.921875q-0.28125 0.4375 -0.859375 0.703125q-0.578125 0.265625 -1.375 0.265625q-1.078125 0 -1.828125 -0.53125q-0.765625 -0.53125 -0.71875 -1.734375zm9.240234 2.125l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm8.173828 -7.390625l0.25 -1.203125l1.0625 0l-0.265625 1.203125l-1.046875 0zm-1.546875 7.390625l1.296875 -6.21875l1.0625 0l-1.296875 6.21875l-1.0625 0zm2.6503906 0.5625l1.0625 0.09375q0 0.375 0.09375 0.546875q0.09375 0.1875 0.3125 0.28125q0.296875 0.140625 0.75 0.140625q0.953125 0 1.375 -0.5q0.28125 -0.34375 0.515625 -1.453125l0.109375 -0.515625q-0.828125 0.84375 -1.765625 0.84375q-0.9375 0 -1.578125 -0.6875q-0.625 -0.703125 -0.625 -1.96875q0 -1.046875 0.5 -1.921875q0.5 -0.890625 1.1875 -1.328125q0.6875 -0.453125 1.421875 -0.453125q1.21875 0 1.890625 1.15625l0.203125 -1.015625l0.984375 0l-1.265625 6.0q-0.203125 1.0 -0.546875 1.546875q-0.328125 0.546875 -0.921875 0.84375q-0.59375 0.3125 -1.375 0.3125q-0.75 0 -1.296875 -0.1875q-0.53125 -0.1875 -0.796875 -0.5625q-0.265625 -0.375 -0.265625 -0.859375q0 -0.140625 0.03125 -0.3125zm1.328125 -3.3125q0 0.640625 0.109375 0.953125q0.1875 0.453125 0.5 0.6875q0.328125 0.234375 0.734375 0.234375q0.515625 0 1.03125 -0.359375q0.515625 -0.359375 0.828125 -1.125q0.328125 -0.78125 0.328125 -1.46875q0 -0.765625 -0.421875 -1.21875q-0.421875 -0.46875 -1.046875 -0.46875q-0.390625 0 -0.765625 0.21875q-0.359375 0.203125 -0.671875 0.625q-0.3125 0.421875 -0.46875 1.015625q-0.15625 0.59375 -0.15625 0.90625zm5.408203 2.75l1.78125 -8.59375l1.0625 0l-0.6875 3.296875q0.59375 -0.5625 1.09375 -0.8125q0.515625 -0.25 1.0625 -0.25q0.765625 0 1.1875 0.40625q0.4375 0.40625 0.4375 1.078125q0 0.328125 -0.1875 1.203125l-0.765625 3.671875l-1.0625 0l0.796875 -3.765625q0.171875 -0.828125 0.171875 -1.03125q0 -0.3125 -0.21875 -0.5q-0.203125 -0.1875 -0.609375 -0.1875q-0.578125 0 -1.09375 0.296875q-0.515625 0.296875 -0.8125 0.828125q-0.296875 0.515625 -0.546875 1.6875l-0.5625 2.671875l-1.046875 0zm7.892578 -7.359375l0.25 -1.203125l1.203125 0l-0.21875 1.0625q-0.203125 0.9375 -0.625 1.375q-0.40625 0.4375 -0.984375 0.515625l0.109375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0zm1.921875 0l0.25 -1.203125l1.21875 0l-0.234375 1.0625q-0.1875 0.9375 -0.609375 1.375q-0.421875 0.4375 -0.984375 0.515625l0.09375 -0.46875q0.6875 -0.1875 0.921875 -1.28125l-0.65625 0z" fill-rule="nonzero"/></g></svg> \ No newline at end of file diff --git a/docs/user/alerting/index.asciidoc b/docs/user/alerting/index.asciidoc new file mode 100644 index 0000000000000..b4f7e6af3d61c --- /dev/null +++ b/docs/user/alerting/index.asciidoc @@ -0,0 +1,202 @@ +[role="xpack"] +[[alerting-getting-started]] += Alerting and Actions + +beta[] + +-- + +Alerting allows you to detect complex conditions within different {kib} apps and trigger actions when those conditions are met. Alerting is integrated with <<xpack-apm,*APM*>>, <<xpack-infra,*Metrics*>>, <<xpack-siem,*SIEM*>>, <<xpack-uptime,*Uptime*>>, can be centrally managed from the <<management,*Management*>> UI, and provides a set of built-in <<action-types, actions>> and <<alert-types, alerts>> for you to use. + +image::images/alerting-overview.png[Alerts and actions UI] + +[IMPORTANT] +============================================== +To make sure you can access alerting and actions, see the <<alerting-setup-prerequisites, setup and pre-requisites>> section. +============================================== + +[float] +== Concepts and terminology + +*Alerts* work by running checks on a schedule to detect conditions. When a condition is met, the alert tracks it as an *alert instance* and responds by triggering one or more *actions*. +Actions typically involve interaction with {kib} services or third party integrations. *Connectors* allow actions to talk to these services and integrations. +This section describes all of these elements and how they operate together. + +[float] +=== What is an alert? + +An alert specifies a background task that runs on the {kib} server to check for specific conditions. It consists of three main parts: + +* *Conditions*: what needs to be detected? +* *Schedule*: when/how often should detection checks run? +* *Actions*: what happens when a condition is detected? + +For example, when monitoring a set of servers, an alert might check for average CPU usage > 0.9 on each server for the two minutes (condition), checked every minute (schedule), sending a warning email message via SMTP with subject `CPU on {{server}} is high` (action). + +image::images/what-is-an-alert.svg[Three components of an alert] + +The following sections each part of the alert is described in more detail. + +[float] +[[alerting-concepts-conditions]] +==== Conditions + +Under the hood, {kib} alerts detect conditions by running javascript function on the {kib} server, which gives it flexibility to support a wide range of detections, anything from the results of a simple {es} query to heavy computations involving data from multiple sources or external systems. + +These detections are packaged and exposed as *alert types*. An alert type hides the underlying details of the detection, and exposes a set of parameters +to control the details of the conditions to detect. + +For example, an <<alert-types, index threshold alert type>> lets you specify the index to query, an aggregation field, and a time window, but the details of the underlying {es} query are hidden. + +See <<alert-types>> for the types of alerts provided by {kib} and how they express their conditions. + +[float] +[[alerting-concepts-scheduling]] +==== Schedule + +Alert schedules are defined as an interval between subsequent checks, and can range from a few seconds to months. + +[IMPORTANT] +============================================== +The intervals of alert checks in {kib} are approximate, their timing of their execution is affected by factors such as the frequency at which tasks are claimed and the task load on the system. See <<alerting-scale-performance>> for more information. +============================================== + +[float] +[[alerting-concepts-actions]] +==== Actions + +Actions are invocations of {kib} services or integrations with third-party systems, that run as background tasks on the {kib} server when alert conditions are met. + +When defining actions in an alert, you specify +* the *action type*: the type of service or integration to use> +* the connection for that type by referencing a <<alerting-concepts-connectors, connector>>. +* a mapping of alert values to properties exposed for that type of action. + +The result is a template: all the parameters needed to invoke a service are supplied except for specific values that are only known at the time the alert condition is detected. + +In the server monitoring example, the `email` action type is used, and `server` is mapped to the body of the email, using the template string `CPU on {{server}} is high`. + +When the alert detects the condition, it creates an <<alerting-concepts-alert-instances, alert instance>> containing the details of the condition, renders the template with these details such as server name, and executes the action on the {kib} server by invoking the `email` action type. + +image::images/what-is-an-action.svg[Actions are like templates that are rendered when an alert detects a condition] + +See <<action-types>> for details on the types of actions provided by {kib}. + +[float] +[[alerting-concepts-alert-instances]] +=== Alert instances + +When checking for a condition, an alert might identify multiple occurrences of the condition. {kib} tracks each of these *alert instances* separately and takes action per instance. + +Using the server monitoring example, each server with average CPU > 0.9 is tracked as an alert instance. This means a separate email is sent for each server that exceeds the threshold. + +image::images/alert-instances.svg[{kib} tracks each detected condition as an alert instance and takes action on each instance] + +[float] +[[alerting-concepts-suppressing-duplicate-notifications]] +=== Suppressing duplicate notifications + +Since actions are taken per instance, alerts can end up generating a large number of actions. Take the following example where an alert is monitoring three servers every minute for CPU usage > 0.9: + +* Minute 1: server X123 > 0.9. *One email* is sent for server X123. +* Minute 2: X123 and Y456 > 0.9. *Two emails* are sent, on for X123 and one for Y456. +* Minute 3: X123, Y456, Z789 > 0.9. *Three emails* are sent, one for each of X123, Y456, Z789. + +In the above example, three emails are sent for server X123 in the span of 3 minutes for the same condition. Often it's desirable to suppress frequent re-notification. Operations like muting and re-notification throttling can be applied at the instance level. If we set the alert re-notify interval to 5 minutes, we reduce noise by only getting emails for new servers that exceed the threshold: + +* Minute 1: server X123 > 0.9. *One email* is sent for server X123. +* Minute 2: X123 and Y456 > 0.9. *One email* is sent for Y456 +* Minute 3: X123, Y456, Z789 > 0.9. *One email* is sent for Z789. + +[float] +[[alerting-concepts-connectors]] +=== Connectors + +Actions often involve connecting with services inside {kib} or integrations with third-party systems. +Rather than repeatedly entering connection information and credentials for each action, {kib} simplifies action setup using *connectors*. + +*Connectors* provide a central place to store connection information for services and integrations. For example if four alerts send email notifications via the same SMTP service, +they all reference the same SMTP connector. When the SMTP settings change they are updated once in the connector, instead of having to update four alerts. + +image::images/alert-concepts-connectors.svg[Connectors provide a central place to store service connection settings] + +[float] +=== Summary + +An _alert_ consists of conditions, _actions_, and a schedule. When conditions are met, _alert instances_ are created that render _actions_ and invoke them. To make action setup and update easier, actions refer to _connectors_ that centralize the information used to connect with {kib} services and third-party integrations. + +image::images/alert-concepts-summary.svg[Alerts, actions, alert instances and connectors work together to convert detection into action] + +* *Alert*: a specification of the conditions to be detected, the schedule for detection, and the response when detection occurs. +* *Action*: the response to a detected condition defined in the alert. Typically actions specify a service or third party integration along with alert details that will be sent to it. +* *Alert instance*: state tracked by {kib} for every occurrence of a detected condition. Actions as well as controls like muting and re-notification are controlled at the instance level. +* *Connector*: centralized configurations for services and third party integration that are referenced by actions. + +[float] +[[alerting-concepts-differences]] +== Differences from Watcher + +{kib} alerting and <<watcher-ui, {es} alerting>> are both used to detect conditions and can trigger actions in response, but they are completely independent alerting systems. + +This section will clarify some of the important differences in the function and intent of the two systems. + +Functionally, {kib} alerting differs in that: + +* Scheduled checks are run on {kib} instead of {es} +* {kib} <<alerting-concepts-conditions, alerts hide the details of detecting conditions>> through *alert types*, whereas watches provide low-level control over inputs, conditions, and transformations. +* {kib} alerts tracks and persists the state of each detected condition through *alert instances*. This makes it possible to mute and throttle individual instances, and detect changes in state such as resolution. +* Actions are linked to *alert instances* in {kib} alerting. Actions are fired for each occurrence of a detected condition, rather than for the entire alert. + +At a higher level, {kib} alerts allow rich integrations across use cases like <<xpack-apm,*APM*>>, <<xpack-infra,*Metrics*>>, <<xpack-siem,*SIEM*>>, and <<xpack-uptime,*Uptime*>>. +Pre-packaged *alert types* simplify setup, hide the details complex domain-specific detections, while providing a consistent interface across {kib}. + +[float] +[[alerting-setup-prerequisites]] +== Setup and prerequisites + +If you are using an *on-premises* Elastic Stack deployment with <<using-kibana-with-security, *security*>>: + +* TLS must be configured for communication <<configuring-tls-kib-es, between {es} and {kib}>>. {kib} alerting uses <<api-keys, API keys>> to secure background alert checks and actions, and API keys require {ref}/configuring-tls.html#tls-http[TLS on the HTTP interface]. +* In the kibana.yml configuration file, add the <<alert-action-settings-kb,`xpack.encrypted_saved_objects.encryptionKey` setting>> + +[float] +[[alerting-security]] +== Security + +To access alerting in a space, a user must have access to one of the following features: +* <<xpack-apm,*APM*>> +* <<xpack-infra,*Metrics*>> +* <<xpack-siem,*SIEM*>> +* <<xpack-uptime,*Uptime*>> + +See <<kibana-feature-privileges, feature privileges>> for more information on configuring roles that provide access to these features. + +[float] +[[alerting-spaces]] +=== Space isolation + +Alerts and connectors are isolated to the {kib} space in which they were created. An alert or connector created in one space will not be visible in another. + +[float] +[[alerting-authorization]] +=== Authorization + +Alerts, including all background detection and the actions they generate are authorized using an <<api-keys, API key>> associated with the last user to edit the alert. Upon creating or modifying an alert, an API key is generated for that user, capturing a snapshot of their privileges at that moment in time. The API key is then used to run all background tasks associated with the alert including detection checks and executing actions. + +[IMPORTANT] +============================================== +If an alert requires certain privileges to run such as index privileges, keep in mind that if a user without those privileges updates the alert, the alert will no longer function. +============================================== + +[float] +[[alerting-restricting-actions]] +=== Restricting actions + +For security reasons you may wish to limit the extent to which {kib} can connect to external services. <<action-settings>> allows you to disable certain <<action-types>> and whitelist the hostnames that {kib} can connect with. + +-- + +include::defining-alerts.asciidoc[] +include::action-types.asciidoc[] +include::alert-types.asciidoc[] +include::alerting-scale-performance.asciidoc[] diff --git a/docs/user/index.asciidoc b/docs/user/index.asciidoc index ff100d0763368..0f53497716552 100644 --- a/docs/user/index.asciidoc +++ b/docs/user/index.asciidoc @@ -40,6 +40,8 @@ include::management.asciidoc[] include::reporting/index.asciidoc[] +include::alerting/index.asciidoc[] + include::api.asciidoc[] include::plugins.asciidoc[] diff --git a/docs/user/management.asciidoc b/docs/user/management.asciidoc index 34a3790529ca3..fa34802abe2a9 100644 --- a/docs/user/management.asciidoc +++ b/docs/user/management.asciidoc @@ -83,6 +83,10 @@ a| <<advanced-options, *Advanced Settings*>> Customize {kib} to suit your needs. Change the format for displaying dates, turn on dark mode, set the timespan for notification messages, and much more. +| <<managing-alerts-and-actions, *Alerts and Actions*>> + +Centrally manage your alerts from across {kib}. Create and manage re-usable connectors for triggering actions. + | <<managing-fields, *Index Patterns*>> Create and manage the index patterns that help you retrieve your data from {es}. @@ -111,6 +115,14 @@ so you can tailor it to your needs without impacting others. include::{kib-repo-dir}/management/advanced-options.asciidoc[] +include::{kib-repo-dir}/management/alerting/alerts-and-actions-intro.asciidoc[] + +include::{kib-repo-dir}/management/alerting/alert-management.asciidoc[] + +include::{kib-repo-dir}/management/alerting/alert-details.asciidoc[] + +include::{kib-repo-dir}/management/alerting/connector-management.asciidoc[] + include::{kib-repo-dir}/management/managing-beats.asciidoc[] include::{kib-repo-dir}/management/index-lifecycle-policies/intro-to-lifecycle-policies.asciidoc[] diff --git a/examples/alerting_example/tsconfig.json b/examples/alerting_example/tsconfig.json index 078522b36cb12..fbcec9de439bd 100644 --- a/examples/alerting_example/tsconfig.json +++ b/examples/alerting_example/tsconfig.json @@ -1,9 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "outDir": "./target", - "skipLibCheck": true, - "resolveJsonModule": true + "outDir": "./target" }, "include": [ "index.ts", diff --git a/package.json b/package.json index 74369646eccda..71994beb5c494 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "uiFramework:documentComponent": "cd packages/kbn-ui-framework && yarn documentComponent", "kbn:watch": "node scripts/kibana --dev --logging.json=false", "build:types": "tsc --p tsconfig.types.json", - "docs:acceptApiChanges": "node scripts/check_published_api_changes.js --accept", + "docs:acceptApiChanges": "node --max-old-space-size=6144 scripts/check_published_api_changes.js --accept", "kbn:bootstrap": "yarn build:types && node scripts/register_git_hook", "spec_to_console": "node scripts/spec_to_console", "backport-skip-ci": "backport --prDescription \"[skip-ci]\"", @@ -118,7 +118,7 @@ "@babel/core": "^7.9.0", "@babel/register": "^7.9.0", "@elastic/apm-rum": "^4.6.0", - "@elastic/charts": "^18.1.0", + "@elastic/charts": "^18.1.1", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "7.7.1", "@elastic/eui": "22.1.1", @@ -170,8 +170,8 @@ "deepmerge": "^4.2.2", "del": "^5.1.0", "elastic-apm-node": "^3.2.0", - "elasticsearch": "^16.5.0", - "elasticsearch-browser": "^16.5.0", + "elasticsearch": "^16.7.0", + "elasticsearch-browser": "^16.7.0", "execa": "^4.0.0", "expiry-js": "0.1.7", "fast-deep-equal": "^3.1.1", @@ -330,11 +330,13 @@ "@types/glob": "^7.1.1", "@types/globby": "^8.0.0", "@types/graphql": "^0.13.2", + "@types/h2o2": "^8.1.1", "@types/hapi": "^17.0.18", "@types/hapi-auth-cookie": "^9.1.0", "@types/has-ansi": "^3.0.0", "@types/history": "^4.7.3", "@types/hoek": "^4.1.3", + "@types/inert": "^5.1.2", "@types/jest": "24.0.19", "@types/joi": "^13.4.2", "@types/jquery": "^3.3.31", diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index d32c7489641a0..b648004760d7c 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -14,9 +14,12 @@ "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/ui-shared-deps": "1.0.0", + "@types/estree": "^0.0.44", "@types/loader-utils": "^1.1.3", "@types/watchpack": "^1.1.5", "@types/webpack": "^4.41.3", + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1", "autoprefixer": "^9.7.4", "babel-loader": "^8.0.6", "clean-webpack-plugin": "^3.0.0", diff --git a/packages/kbn-optimizer/src/worker/__snapshots__/parse_path.test.ts.snap b/packages/kbn-optimizer/src/common/__snapshots__/parse_path.test.ts.snap similarity index 64% rename from packages/kbn-optimizer/src/worker/__snapshots__/parse_path.test.ts.snap rename to packages/kbn-optimizer/src/common/__snapshots__/parse_path.test.ts.snap index 2973ac116d6bd..f537674c3fff7 100644 --- a/packages/kbn-optimizer/src/worker/__snapshots__/parse_path.test.ts.snap +++ b/packages/kbn-optimizer/src/common/__snapshots__/parse_path.test.ts.snap @@ -4,6 +4,7 @@ exports[`parseDirPath() parses / 1`] = ` Object { "dirs": Array [], "filename": undefined, + "query": undefined, "root": "/", } `; @@ -14,6 +15,7 @@ Object { "foo", ], "filename": undefined, + "query": undefined, "root": "/", } `; @@ -26,6 +28,7 @@ Object { "baz", ], "filename": undefined, + "query": undefined, "root": "/", } `; @@ -38,6 +41,7 @@ Object { "baz", ], "filename": undefined, + "query": undefined, "root": "/", } `; @@ -46,6 +50,7 @@ exports[`parseDirPath() parses c:\\ 1`] = ` Object { "dirs": Array [], "filename": undefined, + "query": undefined, "root": "c:", } `; @@ -56,6 +61,7 @@ Object { "foo", ], "filename": undefined, + "query": undefined, "root": "c:", } `; @@ -68,6 +74,7 @@ Object { "baz", ], "filename": undefined, + "query": undefined, "root": "c:", } `; @@ -80,6 +87,7 @@ Object { "baz", ], "filename": undefined, + "query": undefined, "root": "c:", } `; @@ -88,6 +96,7 @@ exports[`parseFilePath() parses /foo 1`] = ` Object { "dirs": Array [], "filename": "foo", + "query": undefined, "root": "/", } `; @@ -99,6 +108,7 @@ Object { "bar", ], "filename": "baz", + "query": undefined, "root": "/", } `; @@ -110,6 +120,36 @@ Object { "bar", ], "filename": "baz.json", + "query": undefined, + "root": "/", +} +`; + +exports[`parseFilePath() parses /foo/bar/baz.json?light 1`] = ` +Object { + "dirs": Array [ + "foo", + "bar", + ], + "filename": "baz.json", + "query": Object { + "light": "", + }, + "root": "/", +} +`; + +exports[`parseFilePath() parses /foo/bar/baz.json?light=true&dark=false 1`] = ` +Object { + "dirs": Array [ + "foo", + "bar", + ], + "filename": "baz.json", + "query": Object { + "dark": "false", + "light": "true", + }, "root": "/", } `; @@ -121,6 +161,7 @@ Object { "bar", ], "filename": "baz.json", + "query": undefined, "root": "c:", } `; @@ -129,6 +170,7 @@ exports[`parseFilePath() parses c:\\foo 1`] = ` Object { "dirs": Array [], "filename": "foo", + "query": undefined, "root": "c:", } `; @@ -140,6 +182,7 @@ Object { "bar", ], "filename": "baz", + "query": undefined, "root": "c:", } `; @@ -151,6 +194,36 @@ Object { "bar", ], "filename": "baz.json", + "query": undefined, + "root": "c:", +} +`; + +exports[`parseFilePath() parses c:\\foo\\bar\\baz.json?dark 1`] = ` +Object { + "dirs": Array [ + "foo", + "bar", + ], + "filename": "baz.json", + "query": Object { + "dark": "", + }, + "root": "c:", +} +`; + +exports[`parseFilePath() parses c:\\foo\\bar\\baz.json?dark=true&light=false 1`] = ` +Object { + "dirs": Array [ + "foo", + "bar", + ], + "filename": "baz.json", + "query": Object { + "dark": "true", + "light": "false", + }, "root": "c:", } `; diff --git a/packages/kbn-optimizer/src/common/disallowed_syntax_plugin/disallowed_syntax.ts b/packages/kbn-optimizer/src/common/disallowed_syntax_plugin/disallowed_syntax.ts new file mode 100644 index 0000000000000..ba19bdc9c3be7 --- /dev/null +++ b/packages/kbn-optimizer/src/common/disallowed_syntax_plugin/disallowed_syntax.ts @@ -0,0 +1,194 @@ +/* + * 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 estree from 'estree'; + +export interface DisallowedSyntaxCheck { + name: string; + nodeType: estree.Node['type'] | Array<estree.Node['type']>; + test?: (n: any) => boolean | void; +} + +export const checks: DisallowedSyntaxCheck[] = [ + /** + * es2015 + */ + // https://github.com/estree/estree/blob/master/es2015.md#functions + { + name: '[es2015] generator function', + nodeType: ['FunctionDeclaration', 'FunctionExpression'], + test: (n: estree.FunctionDeclaration | estree.FunctionExpression) => !!n.generator, + }, + // https://github.com/estree/estree/blob/master/es2015.md#forofstatement + { + name: '[es2015] for-of statement', + nodeType: 'ForOfStatement', + }, + // https://github.com/estree/estree/blob/master/es2015.md#variabledeclaration + { + name: '[es2015] let/const variable declaration', + nodeType: 'VariableDeclaration', + test: (n: estree.VariableDeclaration) => n.kind === 'let' || n.kind === 'const', + }, + // https://github.com/estree/estree/blob/master/es2015.md#expressions + { + name: '[es2015] `super`', + nodeType: 'Super', + }, + // https://github.com/estree/estree/blob/master/es2015.md#expressions + { + name: '[es2015] ...spread', + nodeType: 'SpreadElement', + }, + // https://github.com/estree/estree/blob/master/es2015.md#arrowfunctionexpression + { + name: '[es2015] arrow function expression', + nodeType: 'ArrowFunctionExpression', + }, + // https://github.com/estree/estree/blob/master/es2015.md#yieldexpression + { + name: '[es2015] `yield` expression', + nodeType: 'YieldExpression', + }, + // https://github.com/estree/estree/blob/master/es2015.md#templateliteral + { + name: '[es2015] template literal', + nodeType: 'TemplateLiteral', + }, + // https://github.com/estree/estree/blob/master/es2015.md#patterns + { + name: '[es2015] destructuring', + nodeType: ['ObjectPattern', 'ArrayPattern', 'AssignmentPattern'], + }, + // https://github.com/estree/estree/blob/master/es2015.md#classes + { + name: '[es2015] class', + nodeType: [ + 'ClassDeclaration', + 'ClassExpression', + 'ClassBody', + 'MethodDefinition', + 'MetaProperty', + ], + }, + + /** + * es2016 + */ + { + name: '[es2016] exponent operator', + nodeType: 'BinaryExpression', + test: (n: estree.BinaryExpression) => n.operator === '**', + }, + { + name: '[es2016] exponent assignment', + nodeType: 'AssignmentExpression', + test: (n: estree.AssignmentExpression) => n.operator === '**=', + }, + + /** + * es2017 + */ + // https://github.com/estree/estree/blob/master/es2017.md#function + { + name: '[es2017] async function', + nodeType: ['FunctionDeclaration', 'FunctionExpression'], + test: (n: estree.FunctionDeclaration | estree.FunctionExpression) => n.async, + }, + // https://github.com/estree/estree/blob/master/es2017.md#awaitexpression + { + name: '[es2017] await expression', + nodeType: 'AwaitExpression', + }, + + /** + * es2018 + */ + // https://github.com/estree/estree/blob/master/es2018.md#statements + { + name: '[es2018] for-await-of statements', + nodeType: 'ForOfStatement', + test: (n: estree.ForOfStatement) => n.await, + }, + // https://github.com/estree/estree/blob/master/es2018.md#expressions + { + name: '[es2018] object spread properties', + nodeType: 'ObjectExpression', + test: (n: estree.ObjectExpression) => n.properties.some(p => p.type === 'SpreadElement'), + }, + // https://github.com/estree/estree/blob/master/es2018.md#template-literals + { + name: '[es2018] tagged template literal with invalid escape', + nodeType: 'TemplateElement', + test: (n: estree.TemplateElement) => n.value.cooked === null, + }, + // https://github.com/estree/estree/blob/master/es2018.md#patterns + { + name: '[es2018] rest properties', + nodeType: 'ObjectPattern', + test: (n: estree.ObjectPattern) => n.properties.some(p => p.type === 'RestElement'), + }, + + /** + * es2019 + */ + // https://github.com/estree/estree/blob/master/es2019.md#catchclause + { + name: '[es2019] catch clause without a binding', + nodeType: 'CatchClause', + test: (n: estree.CatchClause) => !n.param, + }, + + /** + * es2020 + */ + // https://github.com/estree/estree/blob/master/es2020.md#bigintliteral + { + name: '[es2020] bigint literal', + nodeType: 'Literal', + test: (n: estree.Literal) => typeof n.value === 'bigint', + }, + + /** + * webpack transforms import/export in order to support tree shaking and async imports + * + * // https://github.com/estree/estree/blob/master/es2020.md#importexpression + * { + * name: '[es2020] import expression', + * nodeType: 'ImportExpression', + * }, + * // https://github.com/estree/estree/blob/master/es2020.md#exportalldeclaration + * { + * name: '[es2020] export all declaration', + * nodeType: 'ExportAllDeclaration', + * }, + * + */ +]; + +export const checksByNodeType = new Map<estree.Node['type'], DisallowedSyntaxCheck[]>(); +for (const check of checks) { + const nodeTypes = Array.isArray(check.nodeType) ? check.nodeType : [check.nodeType]; + for (const nodeType of nodeTypes) { + if (!checksByNodeType.has(nodeType)) { + checksByNodeType.set(nodeType, []); + } + checksByNodeType.get(nodeType)!.push(check); + } +} diff --git a/packages/kbn-optimizer/src/common/disallowed_syntax_plugin/disallowed_syntax_plugin.ts b/packages/kbn-optimizer/src/common/disallowed_syntax_plugin/disallowed_syntax_plugin.ts new file mode 100644 index 0000000000000..7377462eb267b --- /dev/null +++ b/packages/kbn-optimizer/src/common/disallowed_syntax_plugin/disallowed_syntax_plugin.ts @@ -0,0 +1,73 @@ +/* + * 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 webpack from 'webpack'; +import acorn from 'acorn'; +import * as AcornWalk from 'acorn-walk'; + +import { checksByNodeType, DisallowedSyntaxCheck } from './disallowed_syntax'; +import { parseFilePath } from '../parse_path'; + +export class DisallowedSyntaxPlugin { + apply(compiler: webpack.Compiler) { + compiler.hooks.normalModuleFactory.tap(DisallowedSyntaxPlugin.name, factory => { + factory.hooks.parser.for('javascript/auto').tap(DisallowedSyntaxPlugin.name, parser => { + parser.hooks.program.tap(DisallowedSyntaxPlugin.name, (program: acorn.Node) => { + const module = parser.state?.current; + if (!module || !module.resource) { + return; + } + + const resource: string = module.resource; + const { dirs } = parseFilePath(resource); + + if (!dirs.includes('node_modules')) { + return; + } + + const failedChecks = new Set<DisallowedSyntaxCheck>(); + + AcornWalk.full(program, node => { + const checks = checksByNodeType.get(node.type as any); + if (!checks) { + return; + } + + for (const check of checks) { + if (!check.test || check.test(node)) { + failedChecks.add(check); + } + } + }); + + if (!failedChecks.size) { + return; + } + + // throw an error to trigger a parse failure, causing this module to be reported as invalid + throw new Error( + `disallowed syntax found in file ${resource}:\n - ${Array.from(failedChecks) + .map(c => c.name) + .join('\n - ')}` + ); + }); + }); + }); + } +} diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/index.ts b/packages/kbn-optimizer/src/common/disallowed_syntax_plugin/index.ts similarity index 92% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/index.ts rename to packages/kbn-optimizer/src/common/disallowed_syntax_plugin/index.ts index 4b69616a777e9..ca5ba1b90fe95 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/index.ts +++ b/packages/kbn-optimizer/src/common/disallowed_syntax_plugin/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export * from '../../../../../../plugins/embeddable/public'; +export * from './disallowed_syntax_plugin'; diff --git a/packages/kbn-optimizer/src/common/index.ts b/packages/kbn-optimizer/src/common/index.ts index ea0560f132153..c51905be04565 100644 --- a/packages/kbn-optimizer/src/common/index.ts +++ b/packages/kbn-optimizer/src/common/index.ts @@ -26,3 +26,5 @@ export * from './ts_helpers'; export * from './rxjs_helpers'; export * from './array_helpers'; export * from './event_stream_helpers'; +export * from './disallowed_syntax_plugin'; +export * from './parse_path'; diff --git a/packages/kbn-optimizer/src/worker/parse_path.test.ts b/packages/kbn-optimizer/src/common/parse_path.test.ts similarity index 83% rename from packages/kbn-optimizer/src/worker/parse_path.test.ts rename to packages/kbn-optimizer/src/common/parse_path.test.ts index 72197e8c8fb07..61be44348cfae 100644 --- a/packages/kbn-optimizer/src/worker/parse_path.test.ts +++ b/packages/kbn-optimizer/src/common/parse_path.test.ts @@ -21,7 +21,15 @@ import { parseFilePath, parseDirPath } from './parse_path'; const DIRS = ['/', '/foo/bar/baz/', 'c:\\', 'c:\\foo\\bar\\baz\\']; const AMBIGUOUS = ['/foo', '/foo/bar/baz', 'c:\\foo', 'c:\\foo\\bar\\baz']; -const FILES = ['/foo/bar/baz.json', 'c:/foo/bar/baz.json', 'c:\\foo\\bar\\baz.json']; +const FILES = [ + '/foo/bar/baz.json', + 'c:/foo/bar/baz.json', + 'c:\\foo\\bar\\baz.json', + '/foo/bar/baz.json?light', + '/foo/bar/baz.json?light=true&dark=false', + 'c:\\foo\\bar\\baz.json?dark', + 'c:\\foo\\bar\\baz.json?dark=true&light=false', +]; describe('parseFilePath()', () => { it.each([...FILES, ...AMBIGUOUS])('parses %s', path => { diff --git a/packages/kbn-optimizer/src/worker/parse_path.ts b/packages/kbn-optimizer/src/common/parse_path.ts similarity index 83% rename from packages/kbn-optimizer/src/worker/parse_path.ts rename to packages/kbn-optimizer/src/common/parse_path.ts index 88152df55b84f..4c96417800252 100644 --- a/packages/kbn-optimizer/src/worker/parse_path.ts +++ b/packages/kbn-optimizer/src/common/parse_path.ts @@ -18,6 +18,7 @@ */ import normalizePath from 'normalize-path'; +import Qs from 'querystring'; /** * Parse an absolute path, supporting normalized paths from webpack, @@ -33,11 +34,19 @@ export function parseDirPath(path: string) { } export function parseFilePath(path: string) { - const normalized = normalizePath(path); + let normalized = normalizePath(path); + let query; + const queryIndex = normalized.indexOf('?'); + if (queryIndex !== -1) { + query = Qs.parse(normalized.slice(queryIndex + 1)); + normalized = normalized.slice(0, queryIndex); + } + const [root, ...others] = normalized.split('/'); return { root: root === '' ? '/' : root, dirs: others.slice(0, -1), + query, filename: others[others.length - 1] || undefined, }; } diff --git a/packages/kbn-optimizer/src/index.ts b/packages/kbn-optimizer/src/index.ts index 48777f1d54aaf..8026cf39db73d 100644 --- a/packages/kbn-optimizer/src/index.ts +++ b/packages/kbn-optimizer/src/index.ts @@ -20,3 +20,4 @@ export { OptimizerConfig } from './optimizer'; export * from './run_optimizer'; export * from './log_optimizer_state'; +export * from './common/disallowed_syntax_plugin'; diff --git a/packages/kbn-optimizer/src/worker/run_compilers.ts b/packages/kbn-optimizer/src/worker/run_compilers.ts index e87ddc7d0185c..0dfce4b5addba 100644 --- a/packages/kbn-optimizer/src/worker/run_compilers.ts +++ b/packages/kbn-optimizer/src/worker/run_compilers.ts @@ -27,10 +27,17 @@ import webpack, { Stats } from 'webpack'; import * as Rx from 'rxjs'; import { mergeMap, map, mapTo, takeUntil } from 'rxjs/operators'; -import { CompilerMsgs, CompilerMsg, maybeMap, Bundle, WorkerConfig, ascending } from '../common'; +import { + CompilerMsgs, + CompilerMsg, + maybeMap, + Bundle, + WorkerConfig, + ascending, + parseFilePath, +} from '../common'; import { getWebpackConfig } from './webpack.config'; import { isFailureStats, failedStatsToErrorMessage } from './webpack_helpers'; -import { parseFilePath } from './parse_path'; import { isExternalModule, isNormalModule, diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 5d8ef7626f630..9337daf419bfa 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -29,8 +29,7 @@ import webpackMerge from 'webpack-merge'; import { CleanWebpackPlugin } from 'clean-webpack-plugin'; import * as SharedDeps from '@kbn/ui-shared-deps'; -import { Bundle, WorkerConfig } from '../common'; -import { parseDirPath } from './parse_path'; +import { Bundle, WorkerConfig, parseDirPath, DisallowedSyntaxPlugin } from '../common'; const IS_CODE_COVERAGE = !!process.env.CODE_COVERAGE; const ISTANBUL_PRESET_PATH = require.resolve('@kbn/babel-preset/istanbul_preset'); @@ -77,7 +76,7 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { ...SharedDeps.externals, }, - plugins: [new CleanWebpackPlugin()], + plugins: [new CleanWebpackPlugin(), new DisallowedSyntaxPlugin()], module: { // no parse rules for a few known large packages which have no require() statements @@ -131,12 +130,21 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { loader: 'resolve-url-loader', options: { join: (_: string, __: any) => (uri: string, base?: string) => { - if (!base) { + // apply only to legacy platform styles + if (!base || !parseDirPath(base).dirs.includes('legacy')) { return null; } + if (uri.startsWith('ui/assets')) { + return Path.resolve( + worker.repoRoot, + 'src/core/server/core_app/', + uri.replace('ui/', '') + ); + } + // manually force ui/* urls in legacy styles to resolve to ui/legacy/public - if (uri.startsWith('ui/') && parseDirPath(base).dirs.includes('legacy')) { + if (uri.startsWith('ui/')) { return Path.resolve( worker.repoRoot, 'src/legacy/ui/public', diff --git a/packages/kbn-optimizer/src/worker/webpack_helpers.ts b/packages/kbn-optimizer/src/worker/webpack_helpers.ts index a11c85c64198e..e30920b960144 100644 --- a/packages/kbn-optimizer/src/worker/webpack_helpers.ts +++ b/packages/kbn-optimizer/src/worker/webpack_helpers.ts @@ -18,7 +18,6 @@ */ import webpack from 'webpack'; -import { defaults } from 'lodash'; // @ts-ignore import Stats from 'webpack/lib/Stats'; @@ -55,12 +54,14 @@ const STATS_WARNINGS_FILTER = new RegExp( ); export function failedStatsToErrorMessage(stats: webpack.Stats) { - const details = stats.toString( - defaults( - { colors: true, warningsFilter: STATS_WARNINGS_FILTER }, - Stats.presetToOptions('minimal') - ) - ); + const details = stats.toString({ + ...Stats.presetToOptions('minimal'), + colors: true, + warningsFilter: STATS_WARNINGS_FILTER, + errors: true, + errorDetails: true, + moduleTrace: true, + }); return `Optimizations failure.\n${details.split('\n').join('\n ')}`; } diff --git a/packages/kbn-plugin-generator/integration_tests/generate_plugin.test.js b/packages/kbn-plugin-generator/integration_tests/generate_plugin.test.js index d7d4dc14519c3..f434d09c6bf81 100644 --- a/packages/kbn-plugin-generator/integration_tests/generate_plugin.test.js +++ b/packages/kbn-plugin-generator/integration_tests/generate_plugin.test.js @@ -17,163 +17,46 @@ * under the License. */ -/* eslint-disable no-restricted-syntax */ import { spawn } from 'child_process'; +import Fs from 'fs'; import { resolve } from 'path'; -import util from 'util'; -import { stat, readFileSync } from 'fs'; -import { snakeCase } from 'lodash'; +import { promisify } from 'util'; + import del from 'del'; -import { ProcRunner, ToolingLog } from '@kbn/dev-utils'; -import { createLegacyEsTestCluster } from '@kbn/test'; -import execa from 'execa'; +import { snakeCase } from 'lodash'; -const statP = util.promisify(stat); +const statAsync = promisify(Fs.stat); const ROOT_DIR = resolve(__dirname, '../../../'); -const oneMinute = 60000; - -describe(`running the plugin-generator via 'node scripts/generate_plugin.js plugin-name' with default config`, () => { - const pluginName = 'ispec-plugin'; - const snakeCased = snakeCase(pluginName); - const generatedPath = resolve(ROOT_DIR, `plugins/${snakeCased}`); - const collect = xs => data => xs.push(data + ''); // Coerce from Buffer to String - - beforeAll(() => { - jest.setTimeout(oneMinute * 10); - }); - - beforeAll(done => { - const create = spawn(process.execPath, ['scripts/generate_plugin.js', pluginName], { - cwd: ROOT_DIR, - }); - create.stdout.on('data', function selectDefaults() { - create.stdin.write('\n'); // Generate a plugin with default options. - }); - create.on('close', done); - }); - - afterAll(() => { - del.sync(generatedPath, { force: true }); - }); - - it(`should succeed on creating a plugin in a directory named 'plugins/${snakeCased}`, async () => { - const stats = await statP(generatedPath); - expect(stats.isDirectory()).toBe(true); - }); - // skipped until internationalization is re-introduced - it.skip(`should create an internationalization config file with a blank line appended to satisfy the parser`, async () => { - // Link to the error that happens when the blank line is not there: - // https://github.com/elastic/kibana/pull/45044#issuecomment-530092627 - const intlFile = `${generatedPath}/.i18nrc.json`; - expect(readFileSync(intlFile, 'utf8').endsWith('\n\n')).toBe(true); - }); - - describe(`then running`, () => { - it(`'yarn test:karma' should exit 0`, async () => { - await execa('yarn', ['test:karma'], { - cwd: generatedPath, - env: { - DISABLE_JUNIT_REPORTER: '1', - }, - }); - }); - - it.skip(`'yarn build' should exit 0`, async () => { - await execa('yarn', ['build'], { cwd: generatedPath }); - }); +const pluginName = 'ispec-plugin'; +const snakeCased = snakeCase(pluginName); +const generatedPath = resolve(ROOT_DIR, `plugins/${snakeCased}`); - describe('with es instance', () => { - const log = new ToolingLog({ - level: 'verbose', - writeTo: process.stdout, - }); - const pr = new ProcRunner(log); - - const es = createLegacyEsTestCluster({ license: 'basic', log }); - beforeAll(es.start); - afterAll(es.stop); - afterAll(() => pr.teardown()); +beforeAll(async () => { + await del(generatedPath, { force: true }); +}); - it(`'yarn start' should result in the spec plugin being initialized on kibana's stdout`, async () => { - await pr.run('kibana', { - cmd: 'yarn', - args: [ - 'start', - '--optimize.enabled=false', - '--logging.json=false', - '--logging.verbose=true', - '--migrations.skip=true', - ], - cwd: generatedPath, - wait: new RegExp('\\[ispecPlugin\\]\\[plugins\\] Setting up plugin'), - }); - await pr.stop('kibana'); - }); - }); +afterAll(async () => { + await del(generatedPath, { force: true }); +}); - it(`'yarn preinstall' should exit 0`, async () => { - await execa('yarn', ['preinstall'], { cwd: generatedPath }); +it('generates a plugin', async () => { + await new Promise((resolve, reject) => { + const proc = spawn(process.execPath, ['scripts/generate_plugin.js', pluginName], { + cwd: ROOT_DIR, + stdio: 'pipe', }); - it.skip(`'yarn lint' should exit 0`, async () => { - await execa('yarn', ['lint'], { cwd: generatedPath }); + proc.stdout.on('data', function selectDefaults() { + proc.stdin.write('\n'); // Generate a plugin with default options. }); - it(`'yarn kbn --help' should print out the kbn help msg`, done => { - const helpMsg = ` -usage: kbn <command> [<args>] - -By default commands are run for Kibana itself, all packages in the 'packages/' -folder and for all plugins in './plugins' and '../kibana-extra'. - -Available commands: - - bootstrap - Install dependencies and crosslink projects - clean - Remove the node_modules and target directories from all projects. - run - Run script defined in package.json in each package that contains that script. - watch - Runs \`kbn:watch\` script for every project. - -Global options: - - -e, --exclude Exclude specified project. Can be specified multiple times to exclude multiple projects, e.g. '-e kibana -e @kbn/pm'. - -i, --include Include only specified projects. If left unspecified, it defaults to including all projects. - --oss Do not include the x-pack when running command. - --skip-kibana-plugins Filter all plugins in ./plugins and ../kibana-extra when running command. -`; - const outData = []; - const kbnHelp = spawn('yarn', ['kbn', '--help'], { cwd: generatedPath }); - kbnHelp.stdout.on('data', collect(outData)); - kbnHelp.on('close', () => { - expect(outData.join('\n')).toContain(helpMsg); - done(); - }); - }); - - it(`'yarn es --help' should print out the es help msg`, done => { - const helpMsg = ` -usage: es <command> [<args>] - -Assists with running Elasticsearch for Kibana development - -Available commands: - - snapshot - Downloads and run from a nightly snapshot - source - Build and run from source - archive - Install and run from an Elasticsearch tar - build_snapshots - Build and collect ES snapshots - -Global options: - - --help -`; - const outData = []; - const kbnHelp = spawn('yarn', ['es', '--help'], { cwd: generatedPath }); - kbnHelp.stdout.on('data', collect(outData)); - kbnHelp.on('close', () => { - expect(outData.join('\n')).toContain(helpMsg); - done(); - }); - }); + proc.on('close', resolve); + proc.on('error', reject); }); + + const stats = await statAsync(generatedPath); + if (!stats.isDirectory()) { + throw new Error(`Expected [${generatedPath}] to be a directory`); + } }); diff --git a/packages/kbn-storybook/storybook_config/middleware.js b/packages/kbn-storybook/storybook_config/middleware.js index f517477b405bd..046758948b2cf 100644 --- a/packages/kbn-storybook/storybook_config/middleware.js +++ b/packages/kbn-storybook/storybook_config/middleware.js @@ -22,5 +22,5 @@ const path = require('path'); // Extend the Storybook Middleware to include a route to access Legacy UI assets module.exports = function(router) { - router.get('/ui', serve(path.resolve(__dirname, '../../../../src/legacy/ui/public/assets'))); + router.get('/ui', serve(path.resolve(__dirname, '../../../src/core/server/core_app/assets'))); }; diff --git a/packages/kbn-test/src/index.ts b/packages/kbn-test/src/index.ts index cfbd1ee0fe64c..57cdc8ffd494f 100644 --- a/packages/kbn-test/src/index.ts +++ b/packages/kbn-test/src/index.ts @@ -58,3 +58,5 @@ export { export { runFailedTestsReporterCli } from './failed_tests_reporter'; export { makeJunitReportPath } from './junit_report_path'; + +export { CI_PARALLEL_PROCESS_PREFIX } from './ci_parallel_process_prefix'; diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index e2e46dd9f01b9..37aa7d0d50ad7 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -9,7 +9,7 @@ "kbn:watch": "node scripts/build --watch" }, "devDependencies": { - "@elastic/charts": "^18.1.0", + "@elastic/charts": "^18.1.1", "abortcontroller-polyfill": "^1.4.0", "@elastic/eui": "22.1.1", "@kbn/babel-preset": "1.0.0", diff --git a/renovate.json5 b/renovate.json5 index 57f175d1afc8e..ffa006264873d 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -265,6 +265,14 @@ '(\\b|_)eslint(\\b|_)', ], }, + { + groupSlug: 'estree', + groupName: 'estree related packages', + packageNames: [ + 'estree', + '@types/estree', + ], + }, { groupSlug: 'fancy-log', groupName: 'fancy-log related packages', diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index 50fd1f716ba37..5d7b467052029 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -43,6 +43,7 @@ - [Core services](#core-services-1) - [Plugin services](#plugin-services) - [UI Exports](#ui-exports) + - [Plugin Spec](#plugin-spec) - [How to](#how-to) - [Configure plugin](#configure-plugin) - [Handle plugin configuration deprecations](#handle-plugin-configuration-deprecations) @@ -1152,12 +1153,12 @@ _See also: [Public's CoreStart API Docs](/docs/development/core/public/kibana-pl ##### Plugins for shared application services -In client code, we have a series of plugins which house shared application services that are being built in the shape of the new platform, but for the time being, are only available in legacy. So if your plugin depends on any of the APIs below, you'll need build your plugin as a legacy plugin that shims the new platform. Once these API's have been moved to the new platform you can migrate your plugin and declare a dependency on the plugin that owns the API's you require. +In client code, we have a series of plugins which house shared application services which are not technically part of `core`, but are often used in Kibana plugins. -The contracts for these plugins are exposed for you to consume in your own plugin; we have created dedicated exports for the `setup` and `start` contracts in a file called `legacy`. By passing these contracts to your plugin's `setup` and `start` methods, you can mimic the functionality that will eventually be provided in the new platform. +This table maps some of the most commonly used legacy items to their new platform locations. ```ts -import { setup, start } from '../core_plugins/visualizations/public/legacy'; +import { npStart: { plugins } } from 'ui/new_platform'; ``` | Legacy Platform | New Platform | Notes | @@ -1264,40 +1265,19 @@ This table shows where these uiExports have moved to in the New Platform. In mos | `visTypes` | `plugins.visualizations.types` | | | `visualize` | | | -Examples: - -- **uiSettingDefaults** - -Before: - -```js -uiExports: { - uiSettingDefaults: { - 'my-plugin:my-setting': { - name: 'just-work', - value: true, - description: 'make it work', - category: ['my-category'], - }, - } -} -``` - -After: - -```ts -// src/plugins/my-plugin/server/plugin.ts -setup(core: CoreSetup){ - core.uiSettings.register({ - 'my-plugin:my-setting': { - name: 'just-work', - value: true, - description: 'make it work', - category: ['my-category'], - }, - }) -} -``` +#### Plugin Spec +| Legacy Platform | New Platform | +| ----------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `id` | [`manifest.id`](/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md) | +| `require` | [`manifest.requiredPlugins`](/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md) | +| `version` | [`manifest.version`](/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md) | +| `kibanaVersion` | [`manifest.kibanaVersion`](/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md) | +| `configPrefix` | [`manifest.configPath`](/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md) | +| `config` | [export config](#configure-plugin) | +| `deprecations` | [export config](#handle-plugin-configuration-deprecations) | +| `uiExports` | `N/A`. Use platform & plugin public contracts | +| `publicDir` | `N/A`. Platform serves static assets from `/public/assets` folder under `/plugins/{id}/assets/{path*}` URL. | +| `preInit`, `init`, `postInit` | `N/A`. Use NP [lifecycle events](#services) | ## How to diff --git a/src/core/MIGRATION_EXAMPLES.md b/src/core/MIGRATION_EXAMPLES.md index 29edef476d7c3..37d0b9297ed3c 100644 --- a/src/core/MIGRATION_EXAMPLES.md +++ b/src/core/MIGRATION_EXAMPLES.md @@ -20,6 +20,7 @@ APIs to their New Platform equivalents. - [Chromeless Applications](#chromeless-applications) - [Render HTML Content](#render-html-content) - [Saved Objects types](#saved-objects-types) + - [UiSettings](#uisettings) ## Configuration @@ -975,4 +976,37 @@ const migration: SavedObjectMigrationFn = (doc, { log }) => {...} The `registerType` API will throw if called after the service has started, and therefor cannot be used from legacy plugin code. Legacy plugins should use the legacy savedObjects service and the legacy way to register -saved object types until migrated. \ No newline at end of file +saved object types until migrated. + +## UiSettings +UiSettings defaults registration performed during `setup` phase via `core.uiSettings.register` API. + +```js +// Before: +uiExports: { + uiSettingDefaults: { + 'my-plugin:my-setting': { + name: 'just-work', + value: true, + description: 'make it work', + category: ['my-category'], + }, + } +} +``` + +```ts +// After: +// src/plugins/my-plugin/server/plugin.ts +setup(core: CoreSetup){ + core.uiSettings.register({ + 'my-plugin:my-setting': { + name: 'just-work', + value: true, + description: 'make it work', + category: ['my-category'], + schema: schema.boolean(), + }, + }) +} +``` \ No newline at end of file diff --git a/src/legacy/ui/public/assets/favicons/android-chrome-192x192.png b/src/core/server/core_app/assets/favicons/android-chrome-192x192.png similarity index 100% rename from src/legacy/ui/public/assets/favicons/android-chrome-192x192.png rename to src/core/server/core_app/assets/favicons/android-chrome-192x192.png diff --git a/src/legacy/ui/public/assets/favicons/android-chrome-256x256.png b/src/core/server/core_app/assets/favicons/android-chrome-256x256.png similarity index 100% rename from src/legacy/ui/public/assets/favicons/android-chrome-256x256.png rename to src/core/server/core_app/assets/favicons/android-chrome-256x256.png diff --git a/src/legacy/ui/public/assets/favicons/android-chrome-512x512.png b/src/core/server/core_app/assets/favicons/android-chrome-512x512.png similarity index 100% rename from src/legacy/ui/public/assets/favicons/android-chrome-512x512.png rename to src/core/server/core_app/assets/favicons/android-chrome-512x512.png diff --git a/src/legacy/ui/public/assets/favicons/apple-touch-icon.png b/src/core/server/core_app/assets/favicons/apple-touch-icon.png similarity index 100% rename from src/legacy/ui/public/assets/favicons/apple-touch-icon.png rename to src/core/server/core_app/assets/favicons/apple-touch-icon.png diff --git a/src/legacy/ui/public/assets/favicons/browserconfig.xml b/src/core/server/core_app/assets/favicons/browserconfig.xml similarity index 100% rename from src/legacy/ui/public/assets/favicons/browserconfig.xml rename to src/core/server/core_app/assets/favicons/browserconfig.xml diff --git a/src/legacy/ui/public/assets/favicons/favicon-16x16.png b/src/core/server/core_app/assets/favicons/favicon-16x16.png similarity index 100% rename from src/legacy/ui/public/assets/favicons/favicon-16x16.png rename to src/core/server/core_app/assets/favicons/favicon-16x16.png diff --git a/src/legacy/ui/public/assets/favicons/favicon-32x32.png b/src/core/server/core_app/assets/favicons/favicon-32x32.png similarity index 100% rename from src/legacy/ui/public/assets/favicons/favicon-32x32.png rename to src/core/server/core_app/assets/favicons/favicon-32x32.png diff --git a/src/legacy/ui/public/assets/favicons/favicon.ico b/src/core/server/core_app/assets/favicons/favicon.ico similarity index 100% rename from src/legacy/ui/public/assets/favicons/favicon.ico rename to src/core/server/core_app/assets/favicons/favicon.ico diff --git a/src/legacy/ui/public/assets/favicons/manifest.json b/src/core/server/core_app/assets/favicons/manifest.json similarity index 100% rename from src/legacy/ui/public/assets/favicons/manifest.json rename to src/core/server/core_app/assets/favicons/manifest.json diff --git a/src/legacy/ui/public/assets/favicons/mstile-144x144.png b/src/core/server/core_app/assets/favicons/mstile-144x144.png similarity index 100% rename from src/legacy/ui/public/assets/favicons/mstile-144x144.png rename to src/core/server/core_app/assets/favicons/mstile-144x144.png diff --git a/src/legacy/ui/public/assets/favicons/mstile-150x150.png b/src/core/server/core_app/assets/favicons/mstile-150x150.png similarity index 100% rename from src/legacy/ui/public/assets/favicons/mstile-150x150.png rename to src/core/server/core_app/assets/favicons/mstile-150x150.png diff --git a/src/legacy/ui/public/assets/favicons/mstile-310x150.png b/src/core/server/core_app/assets/favicons/mstile-310x150.png similarity index 100% rename from src/legacy/ui/public/assets/favicons/mstile-310x150.png rename to src/core/server/core_app/assets/favicons/mstile-310x150.png diff --git a/src/legacy/ui/public/assets/favicons/mstile-310x310.png b/src/core/server/core_app/assets/favicons/mstile-310x310.png similarity index 100% rename from src/legacy/ui/public/assets/favicons/mstile-310x310.png rename to src/core/server/core_app/assets/favicons/mstile-310x310.png diff --git a/src/legacy/ui/public/assets/favicons/mstile-70x70.png b/src/core/server/core_app/assets/favicons/mstile-70x70.png similarity index 100% rename from src/legacy/ui/public/assets/favicons/mstile-70x70.png rename to src/core/server/core_app/assets/favicons/mstile-70x70.png diff --git a/src/legacy/ui/public/assets/favicons/safari-pinned-tab.svg b/src/core/server/core_app/assets/favicons/safari-pinned-tab.svg similarity index 100% rename from src/legacy/ui/public/assets/favicons/safari-pinned-tab.svg rename to src/core/server/core_app/assets/favicons/safari-pinned-tab.svg diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Black.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Black.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Black.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Black.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Black.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Black.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Black.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Black.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-BlackItalic.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-BlackItalic.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-BlackItalic.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-BlackItalic.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-BlackItalic.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-BlackItalic.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-BlackItalic.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-BlackItalic.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Bold.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Bold.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Bold.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Bold.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Bold.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Bold.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Bold.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Bold.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-BoldItalic.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-BoldItalic.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-BoldItalic.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-BoldItalic.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-BoldItalic.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-BoldItalic.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-BoldItalic.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-BoldItalic.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraBold.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraBold.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraBold.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraBold.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraBold.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraBold.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraBold.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraBold.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraBoldItalic.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraBoldItalic.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraBoldItalic.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraBoldItalic.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraBoldItalic.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraBoldItalic.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraBoldItalic.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraBoldItalic.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraLight-BETA.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraLight-BETA.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraLight-BETA.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraLight-BETA.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraLight-BETA.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraLight-BETA.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraLight-BETA.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraLight-BETA.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraLightItalic-BETA.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraLightItalic-BETA.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraLightItalic-BETA.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraLightItalic-BETA.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraLightItalic-BETA.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraLightItalic-BETA.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ExtraLightItalic-BETA.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ExtraLightItalic-BETA.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Italic.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Italic.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Italic.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Italic.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Italic.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Italic.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Italic.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Italic.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Light-BETA.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Light-BETA.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Light-BETA.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Light-BETA.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Light-BETA.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Light-BETA.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Light-BETA.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Light-BETA.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-LightItalic-BETA.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-LightItalic-BETA.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-LightItalic-BETA.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-LightItalic-BETA.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-LightItalic-BETA.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-LightItalic-BETA.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-LightItalic-BETA.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-LightItalic-BETA.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Medium.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Medium.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Medium.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Medium.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Medium.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Medium.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Medium.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Medium.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-MediumItalic.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-MediumItalic.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-MediumItalic.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-MediumItalic.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-MediumItalic.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-MediumItalic.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-MediumItalic.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-MediumItalic.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Regular.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Regular.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Regular.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Regular.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Regular.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Regular.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Regular.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Regular.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-SemiBold.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-SemiBold.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-SemiBold.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-SemiBold.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-SemiBold.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-SemiBold.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-SemiBold.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-SemiBold.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-SemiBoldItalic.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-SemiBoldItalic.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-SemiBoldItalic.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-SemiBoldItalic.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-SemiBoldItalic.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-SemiBoldItalic.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-SemiBoldItalic.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-SemiBoldItalic.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Thin-BETA.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Thin-BETA.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Thin-BETA.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Thin-BETA.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Thin-BETA.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Thin-BETA.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-Thin-BETA.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-Thin-BETA.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ThinItalic-BETA.woff b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ThinItalic-BETA.woff similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ThinItalic-BETA.woff rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ThinItalic-BETA.woff diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ThinItalic-BETA.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ThinItalic-BETA.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-ThinItalic-BETA.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-ThinItalic-BETA.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-italic.var.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-italic.var.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-italic.var.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-italic.var.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-upright.var.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-upright.var.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI-upright.var.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI-upright.var.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI.var.woff2 b/src/core/server/core_app/assets/fonts/inter_ui/Inter-UI.var.woff2 similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/Inter-UI.var.woff2 rename to src/core/server/core_app/assets/fonts/inter_ui/Inter-UI.var.woff2 diff --git a/src/legacy/ui/public/assets/fonts/inter_ui/LICENSE.txt b/src/core/server/core_app/assets/fonts/inter_ui/LICENSE.txt similarity index 100% rename from src/legacy/ui/public/assets/fonts/inter_ui/LICENSE.txt rename to src/core/server/core_app/assets/fonts/inter_ui/LICENSE.txt diff --git a/src/legacy/ui/public/assets/fonts/readme.md b/src/core/server/core_app/assets/fonts/readme.md similarity index 100% rename from src/legacy/ui/public/assets/fonts/readme.md rename to src/core/server/core_app/assets/fonts/readme.md diff --git a/src/legacy/ui/public/assets/fonts/roboto_mono/LICENSE.txt b/src/core/server/core_app/assets/fonts/roboto_mono/LICENSE.txt similarity index 100% rename from src/legacy/ui/public/assets/fonts/roboto_mono/LICENSE.txt rename to src/core/server/core_app/assets/fonts/roboto_mono/LICENSE.txt diff --git a/src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Bold.ttf b/src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Bold.ttf similarity index 100% rename from src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Bold.ttf rename to src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Bold.ttf diff --git a/src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-BoldItalic.ttf b/src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-BoldItalic.ttf similarity index 100% rename from src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-BoldItalic.ttf rename to src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-BoldItalic.ttf diff --git a/src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Italic.ttf b/src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Italic.ttf similarity index 100% rename from src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Italic.ttf rename to src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Italic.ttf diff --git a/src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Light.ttf b/src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Light.ttf similarity index 100% rename from src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Light.ttf rename to src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Light.ttf diff --git a/src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-LightItalic.ttf b/src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-LightItalic.ttf similarity index 100% rename from src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-LightItalic.ttf rename to src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-LightItalic.ttf diff --git a/src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Medium.ttf b/src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Medium.ttf similarity index 100% rename from src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Medium.ttf rename to src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Medium.ttf diff --git a/src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-MediumItalic.ttf b/src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-MediumItalic.ttf similarity index 100% rename from src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-MediumItalic.ttf rename to src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-MediumItalic.ttf diff --git a/src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Regular.ttf b/src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Regular.ttf similarity index 100% rename from src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Regular.ttf rename to src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Regular.ttf diff --git a/src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Thin.ttf b/src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Thin.ttf similarity index 100% rename from src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-Thin.ttf rename to src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-Thin.ttf diff --git a/src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-ThinItalic.ttf b/src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-ThinItalic.ttf similarity index 100% rename from src/legacy/ui/public/assets/fonts/roboto_mono/RobotoMono-ThinItalic.ttf rename to src/core/server/core_app/assets/fonts/roboto_mono/RobotoMono-ThinItalic.ttf diff --git a/src/legacy/ui/public/assets/images/bg_bottom_branded.svg b/src/core/server/core_app/assets/images/bg_bottom_branded.svg similarity index 100% rename from src/legacy/ui/public/assets/images/bg_bottom_branded.svg rename to src/core/server/core_app/assets/images/bg_bottom_branded.svg diff --git a/src/legacy/ui/public/assets/images/bg_bottom_branded_dark.svg b/src/core/server/core_app/assets/images/bg_bottom_branded_dark.svg similarity index 100% rename from src/legacy/ui/public/assets/images/bg_bottom_branded_dark.svg rename to src/core/server/core_app/assets/images/bg_bottom_branded_dark.svg diff --git a/src/legacy/ui/public/assets/images/bg_top_branded.svg b/src/core/server/core_app/assets/images/bg_top_branded.svg similarity index 100% rename from src/legacy/ui/public/assets/images/bg_top_branded.svg rename to src/core/server/core_app/assets/images/bg_top_branded.svg diff --git a/src/legacy/ui/public/assets/images/bg_top_branded_dark.svg b/src/core/server/core_app/assets/images/bg_top_branded_dark.svg similarity index 100% rename from src/legacy/ui/public/assets/images/bg_top_branded_dark.svg rename to src/core/server/core_app/assets/images/bg_top_branded_dark.svg diff --git a/src/legacy/ui/public/assets/images/kibana.svg b/src/core/server/core_app/assets/images/kibana.svg similarity index 100% rename from src/legacy/ui/public/assets/images/kibana.svg rename to src/core/server/core_app/assets/images/kibana.svg diff --git a/src/core/server/core_app/core_app.ts b/src/core/server/core_app/core_app.ts index 2f8c85f47a76e..5e1a3794632ee 100644 --- a/src/core/server/core_app/core_app.ts +++ b/src/core/server/core_app/core_app.ts @@ -16,6 +16,9 @@ * specific language governing permissions and limitations * under the License. */ +import Path from 'path'; +import { fromRoot } from '../../../core/server/utils'; + import { InternalCoreSetup } from '../internal_types'; import { CoreContext } from '../core_context'; import { Logger } from '../logging'; @@ -29,6 +32,7 @@ export class CoreApp { setup(coreSetup: InternalCoreSetup) { this.logger.debug('Setting up core app.'); this.registerDefaultRoutes(coreSetup); + this.registerStaticDirs(coreSetup); } private registerDefaultRoutes(coreSetup: InternalCoreSetup) { @@ -49,4 +53,12 @@ export class CoreApp { res.ok({ body: { version: '0.0.1' } }) ); } + private registerStaticDirs(coreSetup: InternalCoreSetup) { + coreSetup.http.registerStaticDir('/ui/{path*}', Path.resolve(__dirname, './assets')); + + coreSetup.http.registerStaticDir( + '/node_modules/@kbn/ui-framework/dist/{path*}', + fromRoot('node_modules/@kbn/ui-framework/dist') + ); + } } diff --git a/src/core/server/core_app/integration_tests/static_assets.test.ts b/src/core/server/core_app/integration_tests/static_assets.test.ts new file mode 100644 index 0000000000000..aad2510ef8c0e --- /dev/null +++ b/src/core/server/core_app/integration_tests/static_assets.test.ts @@ -0,0 +1,51 @@ +/* + * 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 * as kbnTestServer from '../../../../test_utils/kbn_server'; +import { Root } from '../../root'; + +describe('Platform assets', function() { + let root: Root; + + beforeAll(async function() { + root = kbnTestServer.createRoot(); + + await root.setup(); + await root.start(); + }); + + afterAll(async function() { + await root.shutdown(); + }); + + it('exposes static assets', async () => { + await kbnTestServer.request.get(root, '/ui/favicons/favicon.ico').expect(200); + }); + + it('returns 404 if not found', async function() { + await kbnTestServer.request.get(root, '/ui/favicons/not-a-favicon.ico').expect(404); + }); + + it('does not expose folder content', async function() { + await kbnTestServer.request.get(root, '/ui/favicons/').expect(403); + }); + + it('does not allow file tree traversing', async function() { + await kbnTestServer.request.get(root, '/ui/../../../../../README.md').expect(404); + }); +}); diff --git a/src/core/server/http/base_path_proxy_server.ts b/src/core/server/http/base_path_proxy_server.ts index e418726465efa..acefbd00ae2be 100644 --- a/src/core/server/http/base_path_proxy_server.ts +++ b/src/core/server/http/base_path_proxy_server.ts @@ -23,6 +23,7 @@ import { Agent as HttpsAgent, ServerOptions as TlsOptions } from 'https'; import apm from 'elastic-apm-node'; import { ByteSizeValue } from '@kbn/config-schema'; import { Server, Request, ResponseToolkit } from 'hapi'; +import HapiProxy from 'h2o2'; import { sample } from 'lodash'; import BrowserslistUserAgent from 'browserslist-useragent'; import * as Rx from 'rxjs'; @@ -102,7 +103,7 @@ export class BasePathProxyServer { // Register hapi plugin that adds proxying functionality. It can be configured // through the route configuration object (see { handler: { proxy: ... } }). - await this.server.register({ plugin: require('h2o2') }); + await this.server.register([HapiProxy]); if (this.httpConfig.ssl.enabled) { const tlsOptions = serverOptions.tls as TlsOptions; @@ -166,7 +167,8 @@ export class BasePathProxyServer { host: this.server.info.host, passThrough: true, port: this.devConfig.basePathProxyTargetPort, - protocol: this.server.info.protocol, + // typings mismatch. h2o2 doesn't support "socket" + protocol: this.server.info.protocol as HapiProxy.ProxyHandlerOptions['protocol'], xforward: true, }, }, @@ -195,7 +197,7 @@ export class BasePathProxyServer { agent: this.httpsAgent, passThrough: true, xforward: true, - mapUri: (request: Request) => ({ + mapUri: async (request: Request) => ({ uri: Url.format({ hostname: request.server.info.host, port: this.devConfig.basePathProxyTargetPort, diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index f898ed0ea1a99..77d3d99fb48cb 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -17,6 +17,7 @@ * under the License. */ import { Server } from 'hapi'; +import HapiStaticFiles from 'inert'; import url from 'url'; import { Logger, LoggerFactory } from '../logging'; @@ -44,6 +45,7 @@ export interface HttpServerSetup { * @param router {@link IRouter} - a router with registered route handlers. */ registerRouter: (router: IRouter) => void; + registerStaticDir: (path: string, dirPath: string) => void; basePath: HttpServiceSetup['basePath']; csp: HttpServiceSetup['csp']; createCookieSessionStorageFactory: HttpServiceSetup['createCookieSessionStorageFactory']; @@ -97,10 +99,11 @@ export class HttpServer { this.registeredRouters.add(router); } - public setup(config: HttpConfig): HttpServerSetup { + public async setup(config: HttpConfig): Promise<HttpServerSetup> { const serverOptions = getServerOptions(config); const listenerOptions = getListenerOptions(config); this.server = createServer(serverOptions, listenerOptions); + await this.server.register([HapiStaticFiles]); this.config = config; const basePathService = new BasePath(config.basePath); @@ -109,6 +112,7 @@ export class HttpServer { return { registerRouter: this.registerRouter.bind(this), + registerStaticDir: this.registerStaticDir.bind(this), registerOnPreAuth: this.registerOnPreAuth.bind(this), registerOnPostAuth: this.registerOnPostAuth.bind(this), registerOnPreResponse: this.registerOnPreResponse.bind(this), @@ -339,4 +343,23 @@ export class HttpServer { return t.next({ headers: authResponseHeaders }); }); } + + private registerStaticDir(path: string, dirPath: string) { + if (this.server === undefined) { + throw new Error('Http server is not setup up yet'); + } + + this.server.route({ + path, + method: 'GET', + handler: { + directory: { + path: dirPath, + listing: false, + lookupCompressed: true, + }, + }, + options: { auth: false }, + }); + } } diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index 442bc93190d86..0788a8f2af7a1 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -72,6 +72,7 @@ const createSetupContractMock = () => { registerRouteHandlerContext: jest.fn(), registerOnPreResponse: jest.fn(), createRouter: jest.fn().mockImplementation(() => mockRouter.create({})), + registerStaticDir: jest.fn(), basePath: createBasePathMock(), csp: CspConfig.DEFAULT, auth: createAuthMock(), diff --git a/src/core/server/http/http_tools.test.ts b/src/core/server/http/http_tools.test.ts index c1322a5aa94db..bdaab4f2999ed 100644 --- a/src/core/server/http/http_tools.test.ts +++ b/src/core/server/http/http_tools.test.ts @@ -18,6 +18,8 @@ */ jest.mock('fs', () => ({ + // Hapi Inert patches native methods + ...jest.requireActual('fs'), readFileSync: jest.fn(), })); diff --git a/src/core/server/http/types.ts b/src/core/server/http/types.ts index 6327844108055..4be7e59acb7b9 100644 --- a/src/core/server/http/types.ts +++ b/src/core/server/http/types.ts @@ -265,6 +265,7 @@ export interface InternalHttpServiceSetup auth: HttpServerSetup['auth']; server: HttpServerSetup['server']; createRouter: (path: string, plugin?: PluginOpaqueId) => IRouter; + registerStaticDir: (path: string, dirPath: string) => void; getAuthHeaders: GetAuthHeaders; registerRouteHandlerContext: <T extends keyof RequestHandlerContext>( pluginOpaqueId: PluginOpaqueId, diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index ef31be559b30b..c6860086e7784 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -91,7 +91,15 @@ beforeEach(() => { contracts: new Map([['plugin-id', 'plugin-value']]), uiPlugins: { public: new Map([['plugin-id', {} as DiscoveredPlugin]]), - internal: new Map([['plugin-id', { publicTargetDir: 'path/to/target/public' }]]), + internal: new Map([ + [ + 'plugin-id', + { + publicTargetDir: 'path/to/target/public', + publicAssetsDir: '/plugins/name/assets/', + }, + ], + ]), browserConfigs: new Map(), }, }, diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index 361fade6a4d0c..bb5f6d5617aae 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -334,6 +334,9 @@ export class LegacyService implements CoreService { plugins: startDeps.plugins, }, __internals: { + http: { + registerStaticDir: setupDeps.core.http.registerStaticDir, + }, hapiServer: setupDeps.core.http.server, kibanaMigrator: startDeps.core.savedObjects.migrator, uiPlugins: setupDeps.core.plugins.uiPlugins, diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index 4f69a2b4156be..14147ab9f2a8d 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -540,13 +540,15 @@ describe('PluginsService', () => { it('includes disabled plugins', async () => { config$.next({ plugins: { initialize: true }, plugin1: { enabled: false } }); await pluginsService.discover(); - const { uiPlugins } = await pluginsService.setup({} as any); + const { uiPlugins } = await pluginsService.setup(setupDeps); expect(uiPlugins.internal).toMatchInlineSnapshot(` Map { "plugin-1" => Object { + "publicAssetsDir": <absolute path>/path-1/public/assets, "publicTargetDir": <absolute path>/path-1/target/public, }, "plugin-2" => Object { + "publicAssetsDir": <absolute path>/path-2/public/assets, "publicTargetDir": <absolute path>/path-2/target/public, }, } @@ -558,7 +560,7 @@ describe('PluginsService', () => { it('does initialize if plugins.initialize is true', async () => { config$.next({ plugins: { initialize: true } }); await pluginsService.discover(); - const { initialized } = await pluginsService.setup({} as any); + const { initialized } = await pluginsService.setup(setupDeps); expect(mockPluginSystem.setupPlugins).toHaveBeenCalled(); expect(initialized).toBe(true); }); @@ -566,7 +568,7 @@ describe('PluginsService', () => { it('does not initialize if plugins.initialize is false', async () => { config$.next({ plugins: { initialize: false } }); await pluginsService.discover(); - const { initialized } = await pluginsService.setup({} as any); + const { initialized } = await pluginsService.setup(setupDeps); expect(mockPluginSystem.setupPlugins).not.toHaveBeenCalled(); expect(initialized).toBe(false); }); diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index 9987d1633c502..a0ecee47c675f 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -110,6 +110,7 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS const initialize = config.initialize && !this.coreContext.env.isDevClusterMaster; if (initialize) { contracts = await this.pluginsSystem.setupPlugins(deps); + this.registerPluginStaticDirs(deps); } else { this.log.info('Plugin initialization disabled.'); } @@ -223,6 +224,7 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS if (plugin.includesUiPlugin) { this.uiPluginInternalInfo.set(plugin.name, { publicTargetDir: Path.resolve(plugin.path, 'target/public'), + publicAssetsDir: Path.resolve(plugin.path, 'public/assets'), }); } @@ -262,4 +264,13 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS ) ); } + + private registerPluginStaticDirs(deps: PluginsServiceSetupDeps) { + for (const [pluginName, pluginInfo] of this.uiPluginInternalInfo) { + deps.http.registerStaticDir( + `/plugins/${pluginName}/assets/{path*}`, + pluginInfo.publicAssetsDir + ); + } + } } diff --git a/src/core/server/plugins/types.ts b/src/core/server/plugins/types.ts index 100e3c2288dbf..4fa4e1780e596 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -194,6 +194,10 @@ export interface InternalPluginInfo { * served */ readonly publicTargetDir: string; + /** + * Path to the plugin assets directory. + */ + readonly publicAssetsDir: string; } /** diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index a0a4c9e07b2a5..e4e2b8d7adbb7 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -2350,8 +2350,8 @@ export const validBodyOutput: readonly ["data", "stream"]; // src/core/server/legacy/types.ts:165:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts // src/core/server/legacy/types.ts:166:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts // src/core/server/plugins/plugins_service.ts:47:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:226:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:226:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:228:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:230:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:230:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:232:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts ``` diff --git a/src/dev/build/tasks/nodejs/__tests__/download.js b/src/dev/build/tasks/nodejs/__tests__/download.js index c76ff15b89289..81ed7a6195ae7 100644 --- a/src/dev/build/tasks/nodejs/__tests__/download.js +++ b/src/dev/build/tasks/nodejs/__tests__/download.js @@ -18,27 +18,39 @@ */ import { createServer } from 'http'; -import { resolve } from 'path'; -import { readFileSync } from 'fs'; +import { join } from 'path'; +import { tmpdir } from 'os'; +import { mkdirp, readFileSync } from 'fs-extra'; import del from 'del'; import sinon from 'sinon'; +import { CI_PARALLEL_PROCESS_PREFIX } from '@kbn/test'; import expect from '@kbn/expect'; import Wreck from '@hapi/wreck'; import { ToolingLog } from '@kbn/dev-utils'; import { download } from '../download'; -const TMP_DESTINATION = resolve(__dirname, '__tmp__'); -beforeEach(async () => { - await del(TMP_DESTINATION); -}); -after(async () => { - await del(TMP_DESTINATION); -}); +const getTempFolder = async () => { + const dir = join(tmpdir(), CI_PARALLEL_PROCESS_PREFIX, 'download-js-test-tmp-dir'); + console.log(dir); + await mkdirp(dir); + return dir; +}; describe('src/dev/build/tasks/nodejs/download', () => { const sandbox = sinon.createSandbox(); + let TMP_DESTINATION; + let TMP_DIR; + + beforeEach(async () => { + TMP_DIR = await getTempFolder(); + TMP_DESTINATION = join(TMP_DIR, '__tmp_download_js_test_file__'); + }); + + afterEach(async () => { + await del(TMP_DIR, { force: true }); + }); afterEach(() => sandbox.reset()); const onLogLine = sandbox.stub(); diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index ef114f51f3100..1b5110a61cbc4 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -41,7 +41,7 @@ export const IGNORE_FILE_GLOBS = [ '**/.*', '**/{webpackShims,__mocks__}/**/*', 'x-pack/docs/**/*', - 'src/legacy/ui/public/assets/fonts/**/*', + 'src/core/server/core_app/assets/fonts/**/*', 'packages/kbn-utility-types/test-d/**/*', '**/Jenkinsfile*', 'Dockerfile*', @@ -123,18 +123,18 @@ export const TEMPORARILY_IGNORED_PATHS = [ 'src/legacy/core_plugins/timelion/server/series_functions/__tests__/fixtures/tlConfig.js', 'src/fixtures/config_upgrade_from_4.0.0_to_4.0.1-snapshot.json', 'src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_seriesMultiple.js', - 'src/legacy/ui/public/assets/favicons/android-chrome-192x192.png', - 'src/legacy/ui/public/assets/favicons/android-chrome-256x256.png', - 'src/legacy/ui/public/assets/favicons/android-chrome-512x512.png', - 'src/legacy/ui/public/assets/favicons/apple-touch-icon.png', - 'src/legacy/ui/public/assets/favicons/favicon-16x16.png', - 'src/legacy/ui/public/assets/favicons/favicon-32x32.png', - 'src/legacy/ui/public/assets/favicons/mstile-70x70.png', - 'src/legacy/ui/public/assets/favicons/mstile-144x144.png', - 'src/legacy/ui/public/assets/favicons/mstile-150x150.png', - 'src/legacy/ui/public/assets/favicons/mstile-310x150.png', - 'src/legacy/ui/public/assets/favicons/mstile-310x310.png', - 'src/legacy/ui/public/assets/favicons/safari-pinned-tab.svg', + 'src/core/server/core_app/assets/favicons/android-chrome-192x192.png', + 'src/core/server/core_app/assets/favicons/android-chrome-256x256.png', + 'src/core/server/core_app/assets/favicons/android-chrome-512x512.png', + 'src/core/server/core_app/assets/favicons/apple-touch-icon.png', + 'src/core/server/core_app/assets/favicons/favicon-16x16.png', + 'src/core/server/core_app/assets/favicons/favicon-32x32.png', + 'src/core/server/core_app/assets/favicons/mstile-70x70.png', + 'src/core/server/core_app/assets/favicons/mstile-144x144.png', + 'src/core/server/core_app/assets/favicons/mstile-150x150.png', + 'src/core/server/core_app/assets/favicons/mstile-310x150.png', + 'src/core/server/core_app/assets/favicons/mstile-310x310.png', + 'src/core/server/core_app/assets/favicons/safari-pinned-tab.svg', 'src/legacy/ui/public/styles/bootstrap/component-animations.less', 'src/legacy/ui/public/styles/bootstrap/input-groups.less', 'src/legacy/ui/public/styles/bootstrap/list-group.less', diff --git a/src/legacy/core_plugins/embeddable_api/README.md b/src/legacy/core_plugins/embeddable_api/README.md deleted file mode 100644 index c2f67572df873..0000000000000 --- a/src/legacy/core_plugins/embeddable_api/README.md +++ /dev/null @@ -1,2 +0,0 @@ -- Embeddables have been moved to `/src/plugins/embeddable` NP plugin. -- This legacy plugin is still there to make necessary CSS working, but soon will be completely deleted. diff --git a/src/legacy/core_plugins/embeddable_api/package.json b/src/legacy/core_plugins/embeddable_api/package.json deleted file mode 100644 index f625408fe4c6c..0000000000000 --- a/src/legacy/core_plugins/embeddable_api/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "embeddable_api", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/input_control_vis/index.ts b/src/legacy/core_plugins/input_control_vis/index.ts index d67472ac4b95f..0529aa24dffd7 100644 --- a/src/legacy/core_plugins/input_control_vis/index.ts +++ b/src/legacy/core_plugins/input_control_vis/index.ts @@ -25,7 +25,7 @@ import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy const inputControlVisPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => new Plugin({ id: 'input_control_vis', - require: ['kibana', 'elasticsearch', 'visualizations', 'interpreter'], + require: ['kibana', 'elasticsearch', 'interpreter'], publicDir: resolve(__dirname, 'public'), uiExports: { styleSheetPaths: resolve(__dirname, 'public/index.scss'), diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.tsx b/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.tsx index d7a62e07b26f3..db2af742c70bc 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.tsx +++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.tsx @@ -23,7 +23,7 @@ import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; import { getDepsMock, getIndexPatternMock } from '../../test_utils'; import { ControlsTab, ControlsTabUiProps } from './controls_tab'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from '../../../../../../plugins/visualizations/public'; const indexPatternsMock = { get: getIndexPatternMock, diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.tsx b/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.tsx index 8c77f1b7c4b4f..639e3d2f68c75 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.tsx +++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from '../../../../../../plugins/visualizations/public'; import { OptionsTab, OptionsTabProps } from './options_tab'; describe('OptionsTab', () => { diff --git a/src/legacy/core_plugins/input_control_vis/public/legacy.ts b/src/legacy/core_plugins/input_control_vis/public/legacy.ts index 438cdffdb323a..67299068819e8 100644 --- a/src/legacy/core_plugins/input_control_vis/public/legacy.ts +++ b/src/legacy/core_plugins/input_control_vis/public/legacy.ts @@ -26,21 +26,17 @@ import { InputControlVisPluginSetupDependencies, InputControlVisPluginStartDependencies, } from './plugin'; -import { - setup as visualizationsSetup, - start as visualizationsStart, -} from '../../visualizations/public/np_ready/public/legacy'; const setupPlugins: Readonly<InputControlVisPluginSetupDependencies> = { expressions: npSetup.plugins.expressions, data: npSetup.plugins.data, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, }; const startPlugins: Readonly<InputControlVisPluginStartDependencies> = { expressions: npStart.plugins.expressions, data: npStart.plugins.data, - visualizations: visualizationsStart, + visualizations: npStart.plugins.visualizations, }; const pluginInstance = plugin({} as PluginInitializerContext); diff --git a/src/legacy/core_plugins/input_control_vis/public/plugin.ts b/src/legacy/core_plugins/input_control_vis/public/plugin.ts index c45e0d17872ec..b743468065430 100644 --- a/src/legacy/core_plugins/input_control_vis/public/plugin.ts +++ b/src/legacy/core_plugins/input_control_vis/public/plugin.ts @@ -20,7 +20,10 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/p import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup, VisualizationsStart } from '../../visualizations/public'; +import { + VisualizationsSetup, + VisualizationsStart, +} from '../../../../plugins/visualizations/public'; import { createInputControlVisFn } from './input_control_fn'; import { createInputControlVisTypeDefinition } from './input_control_vis_type'; diff --git a/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx b/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx index c0ab235c1b9d1..c4a7d286850e3 100644 --- a/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx +++ b/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx @@ -31,7 +31,7 @@ import { RangeControl } from './control/range_control_factory'; import { ListControl } from './control/list_control_factory'; import { InputControlVisDependencies } from './plugin'; import { FilterManager, Filter } from '../../../../plugins/data/public'; -import { VisParams, Vis } from '../../visualizations/public'; +import { VisParams, Vis } from '../../../../plugins/visualizations/public'; export const createInputControlVisController = (deps: InputControlVisDependencies) => { return class InputControlVisController { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts index 55e1475fcb03a..86bce5997cdd2 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts @@ -25,7 +25,6 @@ */ export { npSetup, npStart } from 'ui/new_platform'; -export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; export { configureAppAngularModule, migrateLegacyQuery, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx index e21033ffe10ec..cc7299b884890 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx @@ -21,7 +21,7 @@ import moment from 'moment'; import { Subscription } from 'rxjs'; import { History } from 'history'; -import { ViewMode } from '../../../../embeddable_api/public/np_ready/public'; +import { ViewMode } from '../../../../../../plugins/embeddable/public'; import { SavedObjectDashboard } from '../../../../../../plugins/dashboard/public'; import { DashboardAppState, SavedDashboardPanel } from './types'; import { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx index 0c6686c993371..a39266ecd8db3 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx @@ -58,7 +58,7 @@ import { isErrorEmbeddable, openAddPanelFlyout, ViewMode, -} from '../../../../embeddable_api/public/np_ready/public'; +} from '../../../../../../plugins/embeddable/public'; import { NavAction, SavedDashboardPanel } from './types'; import { showOptionsPopover } from './top_nav/show_options_popover'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/embeddable_saved_object_converters.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/embeddable_saved_object_converters.test.ts index b2a2f43b9152d..d3c3dc46c7057 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/embeddable_saved_object_converters.test.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/embeddable_saved_object_converters.test.ts @@ -24,7 +24,7 @@ import { } from './embeddable_saved_object_converters'; import { SavedDashboardPanel } from '../types'; import { DashboardPanelState } from 'src/plugins/dashboard/public'; -import { EmbeddableInput } from 'src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +import { EmbeddableInput } from 'src/plugins/embeddable/public'; interface CustomInput extends EmbeddableInput { something: string; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.test.ts deleted file mode 100644 index 60ca1b39d29d6..0000000000000 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.test.ts +++ /dev/null @@ -1,118 +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. - */ - -jest.mock('../', () => ({ - DashboardConstants: { - ADD_EMBEDDABLE_ID: 'addEmbeddableId', - ADD_EMBEDDABLE_TYPE: 'addEmbeddableType', - }, -})); - -jest.mock('../legacy_imports', () => { - return { - absoluteToParsedUrl: jest.fn(() => { - return { - basePath: '/pep', - appId: 'kibana', - appPath: '/dashboard?addEmbeddableType=lens&addEmbeddableId=123eb456cd&x=1&y=2&z=3', - hostname: 'localhost', - port: 5601, - protocol: 'http:', - addQueryParameter: () => {}, - getAbsoluteUrl: () => { - return 'http://localhost:5601/pep/app/kibana#/dashboard?addEmbeddableType=lens&addEmbeddableId=123eb456cd&x=1&y=2&z=3'; - }, - }; - }), - }; -}); - -import { - addEmbeddableToDashboardUrl, - getLensUrlFromDashboardAbsoluteUrl, - getUrlVars, -} from './url_helper'; - -describe('Dashboard URL Helper', () => { - beforeEach(() => { - jest.resetModules(); - }); - - it('addEmbeddableToDashboardUrl', () => { - const id = '123eb456cd'; - const type = 'lens'; - const urlVars = { - x: '1', - y: '2', - z: '3', - }; - const basePath = '/pep'; - const url = - "http://localhost:5601/pep/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; - expect(addEmbeddableToDashboardUrl(url, basePath, id, urlVars, type)).toEqual( - `http://localhost:5601/pep/app/kibana#/dashboard?addEmbeddableType=${type}&addEmbeddableId=${id}&x=1&y=2&z=3` - ); - }); - - it('getUrlVars', () => { - let url = - "http://localhost:5601/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; - expect(getUrlVars(url)).toEqual({ - _g: '(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))', - _a: "(description:'',filters:!()", - }); - url = 'http://mybusiness.mydomain.com/app/kibana#/dashboard?x=y&y=z'; - expect(getUrlVars(url)).toEqual({ - x: 'y', - y: 'z', - }); - url = 'http://localhost:5601/app/kibana#/dashboard/777182'; - expect(getUrlVars(url)).toEqual({}); - url = - 'http://localhost:5601/app/kibana#/dashboard/777182?title=Some%20Dashboard%20With%20Spaces'; - expect(getUrlVars(url)).toEqual({ title: 'Some Dashboard With Spaces' }); - }); - - it('getLensUrlFromDashboardAbsoluteUrl', () => { - const id = '1244'; - const basePath = '/wev'; - let url = - "http://localhost:5601/wev/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; - expect(getLensUrlFromDashboardAbsoluteUrl(url, basePath, id)).toEqual( - 'http://localhost:5601/wev/app/kibana#/lens/edit/1244' - ); - - url = - "http://localhost:5601/wev/app/kibana#/dashboard/625357282?_a=(description:'',filters:!()&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))"; - expect(getLensUrlFromDashboardAbsoluteUrl(url, basePath, id)).toEqual( - 'http://localhost:5601/wev/app/kibana#/lens/edit/1244' - ); - - url = 'http://myserver.mydomain.com:5601/wev/app/kibana#/dashboard/777182'; - expect(getLensUrlFromDashboardAbsoluteUrl(url, basePath, id)).toEqual( - 'http://myserver.mydomain.com:5601/wev/app/kibana#/lens/edit/1244' - ); - - url = - "http://localhost:5601/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; - expect(getLensUrlFromDashboardAbsoluteUrl(url, '', id)).toEqual( - 'http://localhost:5601/app/kibana#/lens/edit/1244' - ); - }); -}); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.ts deleted file mode 100644 index 73383f2ff3f68..0000000000000 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.ts +++ /dev/null @@ -1,100 +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 { parse } from 'url'; -import { absoluteToParsedUrl } from '../legacy_imports'; -import { DashboardConstants } from './dashboard_constants'; -/** - * Return query params from URL - * @param url given url - */ -export function getUrlVars(url: string): Record<string, string> { - const vars: Record<string, string> = {}; - for (const [, key, value] of url.matchAll(/[?&]+([^=&]+)=([^&]*)/gi)) { - vars[key] = decodeURIComponent(value); - } - return vars; -} - -/** * - * Returns dashboard URL with added embeddableType and embeddableId query params - * eg. - * input: url: http://localhost:5601/lib/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)), embeddableId: 12345, embeddableType: 'lens' - * output: http://localhost:5601/lib/app/kibana#dashboard?addEmbeddableType=lens&addEmbeddableId=12345&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)) - * @param url dasbhoard absolute url - * @param embeddableId id of the saved visualization - * @param basePath current base path - * @param urlVars url query params (optional) - * @param embeddableType 'lens' or 'visualization' (optional, default is 'lens') - */ -export function addEmbeddableToDashboardUrl( - url: string | undefined, - basePath: string, - embeddableId: string, - urlVars?: Record<string, string>, - embeddableType?: string -): string | null { - if (!url) { - return null; - } - const dashboardUrl = getUrlWithoutQueryParams(url); - const dashboardParsedUrl = absoluteToParsedUrl(dashboardUrl, basePath); - if (urlVars) { - const keys = Object.keys(urlVars).sort(); - keys.forEach(key => { - dashboardParsedUrl.addQueryParameter(key, urlVars[key]); - }); - } - dashboardParsedUrl.addQueryParameter( - DashboardConstants.ADD_EMBEDDABLE_TYPE, - embeddableType || 'lens' - ); - dashboardParsedUrl.addQueryParameter(DashboardConstants.ADD_EMBEDDABLE_ID, embeddableId); - return dashboardParsedUrl.getAbsoluteUrl(); -} - -/** - * Return Lens URL from dashboard absolute URL - * @param dashboardAbsoluteUrl - * @param basePath current base path - * @param id Lens id - */ -export function getLensUrlFromDashboardAbsoluteUrl( - dashboardAbsoluteUrl: string | undefined | null, - basePath: string | null | undefined, - id: string -): string | null { - if (!dashboardAbsoluteUrl || basePath === null || basePath === undefined) { - return null; - } - const { host, protocol } = parse(dashboardAbsoluteUrl); - return `${protocol}//${host}${basePath}/app/kibana#/lens/edit/${id}`; -} - -/** - * Returns the portion of the URL without query params - * eg. - * input: http://localhost:5601/lib/app/kibana#/dashboard?param1=x¶m2=y¶m3=z - * output:http://localhost:5601/lib/app/kibana#/dashboard - * input: http://localhost:5601/lib/app/kibana#/dashboard/39292992?param1=x¶m2=y¶m3=z - * output: http://localhost:5601/lib/app/kibana#/dashboard/39292992 - * @param url url to parse - */ -function getUrlWithoutQueryParams(url: string): string { - return url.split('?')[0]; -} diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/css_truncate.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/css_truncate.js deleted file mode 100644 index 8dea9c61475db..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/css_truncate.js +++ /dev/null @@ -1,81 +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 angular from 'angular'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { pluginInstance } from 'plugins/kibana/discover/legacy'; - -let $parentScope; - -let $scope; - -let $elem; - -const init = function(expandable) { - // Load the application - pluginInstance.initializeServices(); - pluginInstance.initializeInnerAngular(); - ngMock.module('app/discover'); - - // Create the scope - ngMock.inject(function($rootScope, $compile) { - // Give us a scope - $parentScope = $rootScope; - - // Create the element - $elem = angular.element( - '<span css-truncate ' + - (expandable ? 'css-truncate-expandable' : '') + - '>this isnt important</span>' - ); - - // And compile it - $compile($elem)($parentScope); - - // Fire a digest cycle - $elem.scope().$digest(); - - // Grab the isolate scope so we can test it - $scope = $elem.isolateScope(); - }); -}; - -describe('cssTruncate directive', function() { - describe('expandable', function() { - beforeEach(function() { - init(true); - }); - - it('should set text-overflow to ellipsis and whitespace to nowrap', function(done) { - expect($elem.css('text-overflow')).to.be('ellipsis'); - expect($elem.css('white-space')).to.be('nowrap'); - done(); - }); - - it('should set white-space to normal when clicked, and back to nowrap when clicked again', function(done) { - $scope.toggle(); - expect($elem.css('white-space')).to.be('normal'); - - $scope.toggle(); - expect($elem.css('white-space')).to.be('nowrap'); - done(); - }); - }); -}); diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/discover_field.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/discover_field.js deleted file mode 100644 index 6ffda87ac2be8..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/discover_field.js +++ /dev/null @@ -1,97 +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 angular from 'angular'; -import _ from 'lodash'; -import sinon from 'sinon'; -import ngMock from 'ng_mock'; -import expect from '@kbn/expect'; -import { pluginInstance } from 'plugins/kibana/discover/legacy'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; - -// Load the kibana app dependencies. - -describe('discoverField', function() { - let $scope; - let indexPattern; - let $elem; - beforeEach(() => pluginInstance.initializeServices()); - beforeEach(() => pluginInstance.initializeInnerAngular()); - beforeEach(ngMock.module('app/discover')); - beforeEach( - ngMock.inject(function(Private, $rootScope, $compile) { - $elem = angular.element(` - <discover-field - field="field" - on-add-field="addField" - on-remove-field="removeField" - on-show-details="showDetails" - ></discover-field> - `); - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - - _.assign($rootScope, { - field: indexPattern.fields.getByName('extension'), - addField: sinon.spy(() => ($rootScope.field.display = true)), - removeField: sinon.spy(() => ($rootScope.field.display = false)), - showDetails: sinon.spy(() => ($rootScope.field.details = { exists: true })), - }); - - $compile($elem)($rootScope); - - $scope = $elem.isolateScope(); - $scope.$digest(); - sinon.spy($scope, 'toggleDetails'); - }) - ); - - afterEach(function() { - $scope.toggleDetails.restore(); - $scope.$destroy(); - }); - - describe('toggleDisplay', function() { - it('should exist', function() { - expect($scope.toggleDisplay).to.be.a(Function); - }); - - it('should call onAddField or onRemoveField depending on the display state', function() { - $scope.toggleDisplay($scope.field); - expect($scope.onAddField.callCount).to.be(1); - expect($scope.onAddField.firstCall.args).to.eql([$scope.field.name]); - - $scope.toggleDisplay($scope.field); - expect($scope.onRemoveField.callCount).to.be(1); - expect($scope.onRemoveField.firstCall.args).to.eql([$scope.field.name]); - }); - - it('should call toggleDetails when currently showing the details', function() { - $scope.toggleDetails($scope.field); - $scope.toggleDisplay($scope.field); - expect($scope.toggleDetails.callCount).to.be(2); - }); - }); - - describe('toggleDetails', function() { - it('should notify the parent when showing the details', function() { - $scope.toggleDetails($scope.field); - expect($scope.onShowDetails.callCount).to.be(1); - }); - }); -}); diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js deleted file mode 100644 index 47392c541890e..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js +++ /dev/null @@ -1,257 +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 angular from 'angular'; -import ngMock from 'ng_mock'; -import _ from 'lodash'; -import sinon from 'sinon'; -import expect from '@kbn/expect'; -import $ from 'jquery'; -import { pluginInstance } from 'plugins/kibana/discover/legacy'; -import FixturesHitsProvider from 'fixtures/hits'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { SimpleSavedObject } from '../../../../../../../core/public'; - -// Load the kibana app dependencies. - -let $parentScope; -let $scope; -let hits; -let indexPattern; -let indexPatternList; - -// Sets up the directive, take an element, and a list of properties to attach to the parent scope. -const init = function($elem, props) { - ngMock.inject(function($rootScope, $compile, $timeout) { - $parentScope = $rootScope; - _.assign($parentScope, props); - $compile($elem)($parentScope); - - // Required for test to run solo. Sigh - $timeout(() => $elem.scope().$digest(), 0); - - $scope = $elem.isolateScope(); - }); -}; - -const destroy = function() { - $scope.$destroy(); - $parentScope.$destroy(); -}; - -describe('discover field chooser directives', function() { - const $elem = angular.element(` - <disc-field-chooser - columns="columns" - toggle="toggle" - hits="hits" - field-counts="fieldCounts" - index-pattern="indexPattern" - index-pattern-list="indexPatternList" - state="state" - on-add-field="addField" - on-add-filter="addFilter" - on-remove-field="removeField" - ></disc-field-chooser> - `); - beforeEach(() => pluginInstance.initializeServices()); - beforeEach(() => pluginInstance.initializeInnerAngular()); - - beforeEach(ngMock.module('app/discover')); - - beforeEach( - ngMock.inject(function(Private) { - hits = Private(FixturesHitsProvider); - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - indexPatternList = [ - new SimpleSavedObject(undefined, { id: '0', attributes: { title: 'b' } }), - new SimpleSavedObject(undefined, { id: '1', attributes: { title: 'a' } }), - new SimpleSavedObject(undefined, { id: '2', attributes: { title: 'c' } }), - ]; - - const fieldCounts = _.transform( - hits, - function(counts, hit) { - _.keys(indexPattern.flattenHit(hit)).forEach(function(key) { - counts[key] = (counts[key] || 0) + 1; - }); - }, - {} - ); - - init($elem, { - columns: [], - toggle: sinon.spy(), - hits: hits, - fieldCounts: fieldCounts, - addField: sinon.spy(), - addFilter: sinon.spy(), - indexPattern: indexPattern, - indexPatternList: indexPatternList, - removeField: sinon.spy(), - }); - - $scope.$digest(); - }) - ); - - afterEach(() => destroy()); - - const getSections = function(ctx) { - return { - selected: $('.dscFieldList--selected', ctx), - popular: $('.dscFieldList--popular', ctx), - unpopular: $('.dscFieldList--unpopular', ctx), - }; - }; - - describe('Field listing', function() { - it('should have Selected Fields, Fields and Popular Fields sections', function() { - const headers = $elem.find('.sidebar-list-header'); - expect(headers.length).to.be(3); - }); - - it('should have 2 popular fields, 1 unpopular field and no selected fields', function() { - const section = getSections($elem); - const popular = find('popular'); - const unpopular = find('unpopular'); - - expect(section.selected.find('li').length).to.be(0); - - expect(popular).to.contain('ssl'); - expect(popular).to.contain('@timestamp'); - expect(popular).to.not.contain('ip\n'); - - expect(unpopular).to.contain('extension'); - expect(unpopular).to.contain('machine.os'); - expect(unpopular).to.not.contain('ssl'); - - function find(popularity) { - return section[popularity] - .find('.dscFieldName') - .map((i, el) => $(el).text()) - .toArray(); - } - }); - - it('should show the popular fields header if there are popular fields', function() { - const section = getSections($elem); - expect(section.popular.hasClass('ng-hide')).to.be(false); - expect(section.popular.find('li:not(.sidebar-list-header)').length).to.be.above(0); - }); - - it('should not show the popular fields if there are not any', function() { - // Re-init - destroy(); - - _.each(indexPattern.fields, function(field) { - field.$$spec.count = 0; - }); // Reset the popular fields - init($elem, { - columns: [], - toggle: sinon.spy(), - hits: require('fixtures/hits'), - filter: sinon.spy(), - indexPattern: indexPattern, - }); - - const section = getSections($elem); - - $scope.$digest(); - expect(section.popular.hasClass('ng-hide')).to.be(true); - expect(section.popular.find('li:not(.sidebar-list-header)').length).to.be(0); - }); - - it('should move the field into selected when it is added to the columns array', function() { - const section = getSections($elem); - $scope.columns.push('bytes'); - $scope.$digest(); - - expect(section.selected.text()).to.contain('bytes'); - expect(section.popular.text()).to.not.contain('bytes'); - - $scope.columns.push('ip'); - $scope.$digest(); - expect(section.selected.text()).to.contain('ip\n'); - expect(section.unpopular.text()).to.not.contain('ip\n'); - - expect(section.popular.text()).to.contain('ssl'); - }); - }); - - describe('details processing', function() { - let field; - function getField() { - return _.find($scope.fields, { name: 'bytes' }); - } - - beforeEach(function() { - field = getField(); - }); - - it('should have a computeDetails function', function() { - expect($scope.computeDetails).to.be.a(Function); - }); - - it('should increase the field popularity when called', function() { - indexPattern.popularizeField = sinon.spy(); - $scope.computeDetails(field); - expect(indexPattern.popularizeField.called).to.be(true); - }); - - it('should append a details object to the field', function() { - $scope.computeDetails(field); - expect(field.details).to.not.be(undefined); - }); - - it('should delete the field details if they already exist', function() { - $scope.computeDetails(field); - expect(field.details).to.not.be(undefined); - $scope.computeDetails(field); - expect(field.details).to.be(undefined); - }); - - it('... unless recompute is true', function() { - $scope.computeDetails(field); - expect(field.details).to.not.be(undefined); - $scope.computeDetails(field, true); - expect(field.details).to.not.be(undefined); - }); - - it('should create buckets with formatted and raw values', function() { - $scope.computeDetails(field); - expect(field.details.buckets).to.not.be(undefined); - expect(field.details.buckets[0].value).to.be(40.141592); - }); - - it('should recalculate the details on open fields if the hits change', function() { - $scope.hits = [{ _source: { bytes: 1024 } }]; - $scope.$apply(); - - field = getField(); - $scope.computeDetails(field); - expect(getField().details.total).to.be(1); - - $scope.hits = [{ _source: { notbytes: 1024 } }]; - $scope.$apply(); - field = getField(); - expect(field.details).to.not.have.property('total'); - }); - }); -}); diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/doc_table.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/doc_table.js index 6b97da79fc589..9e74df08233da 100644 --- a/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/doc_table.js +++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/doc_table.js @@ -61,6 +61,7 @@ const destroy = function() { describe('docTable', function() { let $elem; beforeEach(() => pluginInstance.initializeInnerAngular()); + beforeEach(() => pluginInstance.initializeServices()); beforeEach(ngMock.module('app/discover')); beforeEach(function() { $elem = angular.element(` diff --git a/src/legacy/core_plugins/kibana/public/discover/build_services.ts b/src/legacy/core_plugins/kibana/public/discover/build_services.ts index f881eb96e4e81..180ff13cdddc0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/build_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/build_services.ts @@ -36,7 +36,7 @@ import { import { DiscoverStartPlugins } from './plugin'; import { SharePluginStart } from '../../../../../plugins/share/public'; import { ChartsPluginStart } from '../../../../../plugins/charts/public'; -import { VisualizationsStart } from '../../../visualizations/public'; +import { VisualizationsStart } from '../../../../../plugins/visualizations/public'; import { createSavedSearchesLoader, DocViewerComponent, diff --git a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts index 031e10e99289f..607d79b81618e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts +++ b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts @@ -27,7 +27,7 @@ import { CoreStart, LegacyCoreStart } from 'kibana/public'; import { DataPublicPluginStart } from '../../../../../plugins/data/public'; import { Storage } from '../../../../../plugins/kibana_utils/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; -import { createDocTableDirective } from './np_ready/angular/doc_table/doc_table'; +import { createDocTableDirective } from './np_ready/angular/doc_table'; import { createTableHeaderDirective } from './np_ready/angular/doc_table/components/table_header'; import { createToolBarPagerButtonsDirective, @@ -37,18 +37,8 @@ import { createTableRowDirective } from './np_ready/angular/doc_table/components import { createPagerFactory } from './np_ready/angular/doc_table/lib/pager/pager_factory'; import { createInfiniteScrollDirective } from './np_ready/angular/doc_table/infinite_scroll'; import { createDocViewerDirective } from './np_ready/angular/doc_viewer'; -import { createFieldSearchDirective } from './np_ready/components/field_chooser/discover_field_search_directive'; -import { createIndexPatternSelectDirective } from './np_ready/components/field_chooser/discover_index_pattern_directive'; -import { createStringFieldProgressBarDirective } from './np_ready/components/field_chooser/string_progress_bar'; -// @ts-ignore -import { FieldNameDirectiveProvider } from './np_ready/angular/directives/field_name'; -// @ts-ignore -import { createFieldChooserDirective } from './np_ready/components/field_chooser/field_chooser'; -// @ts-ignore -import { createDiscoverFieldDirective } from './np_ready/components/field_chooser/discover_field'; import { CollapsibleSidebarProvider } from './np_ready/angular/directives/collapsible_sidebar/collapsible_sidebar'; import { DiscoverStartPlugins } from './plugin'; -import { createCssTruncateDirective } from './np_ready/angular/directives/css_truncate'; // @ts-ignore import { FixedScrollProvider } from './np_ready/angular/directives/fixed_scroll'; // @ts-ignore @@ -65,6 +55,7 @@ import { createTopNavDirective, createTopNavHelper, } from '../../../../../plugins/kibana_legacy/public'; +import { createDiscoverSidebarDirective } from './np_ready/components/sidebar'; /** * returns the main inner angular module, it contains all the parts of Angular Discover @@ -125,7 +116,6 @@ export function initializeInnerAngularModule( ]) .config(watchMultiDecorator) .directive('icon', reactDirective => reactDirective(EuiIcon)) - .directive('fieldName', FieldNameDirectiveProvider) .directive('renderComplete', createRenderCompleteDirective) .service('debounce', ['$timeout', DebounceProviderTimeout]); } @@ -149,16 +139,10 @@ export function initializeInnerAngularModule( .run(registerListenEventListener) .directive('icon', reactDirective => reactDirective(EuiIcon)) .directive('kbnAccessibleClick', KbnAccessibleClickProvider) - .directive('fieldName', FieldNameDirectiveProvider) .directive('collapsibleSidebar', CollapsibleSidebarProvider) - .directive('cssTruncate', createCssTruncateDirective) .directive('fixedScroll', FixedScrollProvider) .directive('renderComplete', createRenderCompleteDirective) - .directive('discoverFieldSearch', createFieldSearchDirective) - .directive('discoverIndexPatternSelect', createIndexPatternSelectDirective) - .directive('stringFieldProgressBar', createStringFieldProgressBarDirective) - .directive('discoverField', createDiscoverFieldDirective) - .directive('discFieldChooser', createFieldChooserDirective) + .directive('discoverSidebar', createDiscoverSidebarDirective) .service('debounce', ['$timeout', DebounceProviderTimeout]); } diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index e1ff274911284..55f369eaecd2c 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import angular from 'angular'; // just used in embeddables and discover controller import { DiscoverServices } from './build_services'; let angularModule: any = null; @@ -47,10 +46,14 @@ export function setServices(newServices: any) { services = newServices; } +export const [getUrlTracker, setUrlTracker] = createGetterSetter<{ + setTrackedUrl: (url: string) => void; +}>('urlTracker'); + // EXPORT legacy static dependencies, should be migrated when available in a new version; -export { angular }; export { wrapInI18nContext } from 'ui/i18n'; import { search } from '../../../../../plugins/data/public'; +import { createGetterSetter } from '../../../../../plugins/kibana_utils/common'; export const { getRequestInspectorStats, getResponseInspectorStats, tabifyAggResponse } = search; export { unhashUrl, diff --git a/src/legacy/core_plugins/kibana/public/discover/legacy.ts b/src/legacy/core_plugins/kibana/public/discover/legacy.ts index a1ef646f4fe85..f08fd22c71850 100644 --- a/src/legacy/core_plugins/kibana/public/discover/legacy.ts +++ b/src/legacy/core_plugins/kibana/public/discover/legacy.ts @@ -20,18 +20,8 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; import { plugin } from './index'; -import { - setup as visualizationsSetup, - start as visualizationsStart, -} from '../../../../core_plugins/visualizations/public/np_ready/public/legacy'; // Legacy compatibility part - to be removed at cutover, replaced by a kibana.json file export const pluginInstance = plugin({} as PluginInitializerContext); -export const setup = pluginInstance.setup(npSetup.core, { - ...npSetup.plugins, - visualizations: visualizationsSetup, -}); -export const start = pluginInstance.start(npStart.core, { - ...npStart.plugins, - visualizations: visualizationsStart, -}); +export const setup = pluginInstance.setup(npSetup.core, npSetup.plugins); +export const start = pluginInstance.start(npStart.core, npStart.plugins); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/_discover.scss b/src/legacy/core_plugins/kibana/public/discover/np_ready/_discover.scss index 62e7a96ed80cf..8eaa66cf58624 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/_discover.scss +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/_discover.scss @@ -1,9 +1,5 @@ discover-app { flex-grow: 1; - - .sidebar-container { - background-color: transparent; - } } .dscHistogram { @@ -12,22 +8,6 @@ discover-app { padding: $euiSizeS; } -// SASSTODO: replace the margin-top value with a variable -.dscSidebar__listHeader { - margin-top: 5px; -} - -.dscFieldList--popular { - padding-top: $euiSizeS; -} - -.dscFieldList--selected, -.dscFieldList--unpopular, -.dscFieldList--popular { - padding-left: $euiSizeS; - padding-right: $euiSizeS; -} - // SASSTODO: replace the z-index value with a variable .dscWrapper { padding-right: $euiSizeS; @@ -109,107 +89,6 @@ discover-app { text-align: center; } -/** - * 1. Override sidebar-item-title styles. - */ -.dscSidebarItem { - position: relative; - display: flex; - align-items: center; - justify-content: space-between; - padding-top: 0 !important; /* 1 */ - padding-bottom: 0 !important; /* 1 */ - height: $euiSizeXL; - - &:hover, - &:focus { - .dscSidebarItem__action { - opacity: 1; - } - } -} - -.dscSidebarItem--active { - background: shade($euiColorLightestShade, 5%); - color: $euiColorFullShade; - font-weight: bold; -} - -/** - * 1. Truncate long text so it doesn't push the actions outside of the container. - */ -.dscSidebarItem__label { - overflow: hidden; /* 1 */ - text-overflow: ellipsis; /* 1 */ -} - -/** - * 1. Only visually hide the action, so that it's still accessible to screen readers. - * 2. When tabbed to, this element needs to be visible for keyboard accessibility. - */ -.dscSidebarItem__action { - opacity: 0; /* 1 */ - - &:focus { - opacity: 1; /* 2 */ - } -} - -.dscFieldSearch { - padding: $euiSizeS; -} - -.dscFieldFilter { - margin-top: $euiSizeS; -} - -.dscFieldDetails { - padding: $euiSizeS; - background-color: $euiColorLightestShade; - color: $euiTextColor; -} - -// SASSTODO: replace the padding and margin values with variables -.dscFieldDetails__progress { - background-color: $euiColorEmptyShade; - color: $euiColorDarkShade; - padding: $euiSizeXS; -} - -// SASSTODO: replace the margin-top value with a variable -.dscFieldDetailsItem { - margin-top: 5px; -} - -.dscFieldDetails__filter { - cursor: pointer; -} - -.dscFieldDetailsItem__title { - line-height: 1.5; - display: flex; - align-items: center; - justify-content: space-between; -} - -/** - * 1. If the field name is very long, don't let it squash the buttons. - */ -.dscFieldDetailsItem__buttonGroup { - flex: 0 0 auto; /* 1 */ -} - -.dscFieldDetailsItem__button { - appearance: none; - border: none; - padding: 0; - background-color: transparent; -} - -.dscFieldName--noResults { - color: $euiColorDarkShade; -} - .dscResults { h3 { margin: -20px 0 10px 0; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/css_truncate.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/css_truncate.ts deleted file mode 100644 index 6aa645ea9368e..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/css_truncate.ts +++ /dev/null @@ -1,61 +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. - */ -export function createCssTruncateDirective() { - return { - restrict: 'A', - scope: {}, - link: ($scope: any, $elem: any, attrs: any) => { - $elem.css({ - overflow: 'hidden', - 'white-space': 'nowrap', - 'text-overflow': 'ellipsis', - 'word-break': 'break-all', - }); - - if (attrs.cssTruncateExpandable != null) { - $scope.$watch( - function() { - return $elem.html(); - }, - function() { - if ($elem[0].offsetWidth < $elem[0].scrollWidth) { - $elem.css({ cursor: 'pointer' }); - $elem.bind('click', function() { - $scope.toggle(); - }); - } - } - ); - } - - $scope.toggle = function() { - if ($elem.css('white-space') !== 'normal') { - $elem.css({ 'white-space': 'normal' }); - } else { - $elem.css({ 'white-space': 'nowrap' }); - } - }; - - $scope.$on('$destroy', function() { - $elem.unbind('click'); - $elem.unbind('mouseenter'); - }); - }, - }; -} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name.js deleted file mode 100644 index 47e50f3cc3d4b..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name.js +++ /dev/null @@ -1,35 +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 { FieldName } from '../../../../../../../../plugins/discover/public'; -import { getServices, wrapInI18nContext } from '../../../kibana_services'; - -export function FieldNameDirectiveProvider(reactDirective) { - return reactDirective( - wrapInI18nContext(FieldName), - [ - ['field', { watchDepth: 'collection' }], - ['fieldName', { watchDepth: 'reference' }], - ['fieldType', { watchDepth: 'reference' }], - ], - { restrict: 'AE' }, - { - useShortDots: getServices().uiSettings.get('shortDots:enable'), - } - ); -} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html index 2334e33deadba..fb38f3e7d4c49 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html @@ -1,4 +1,4 @@ -<discover-app class="app-container"> +<discover-app class="app-container" data-fetch-counter="{{fetchCounter}}"> <h1 class="euiScreenReaderOnly">{{screenTitle}}</h1> <!-- Local nav. --> @@ -20,20 +20,21 @@ <h1 class="euiScreenReaderOnly">{{screenTitle}}</h1> <main class="container-fluid"> <div class="row"> - <div class="col-md-2 sidebar-container collapsible-sidebar" id="discover-sidebar" data-test-subj="discover-sidebar"> + <div class="col-md-2 dscSidebar__container collapsible-sidebar" id="discover-sidebar" data-test-subj="discover-sidebar"> <div class="dscFieldChooser"> - <disc-field-chooser + <discover-sidebar columns="state.columns" - hits="rows" field-counts="fieldCounts" - index-pattern="searchSource.getField('index')" + hits="rows" index-pattern-list="opts.indexPatternList" - state="state" on-add-field="addColumn" on-add-filter="filterQuery" on-remove-field="removeColumn" + selected-index-pattern="searchSource.getField('index')" + set-index-pattern="setIndexPattern" + state="state" > - </disc-field-chooser> + </discover-sidebar> </div> </div> diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js index 8e4e77b2d18a6..630ba22d4f3a6 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js @@ -44,6 +44,7 @@ import { getRequestInspectorStats, getResponseInspectorStats, getServices, + getUrlTracker, unhashUrl, subscribeWithScope, tabifyAggResponse, @@ -160,6 +161,9 @@ app.config($routeProvider => { '/management/kibana/objects/savedSearches/' + $route.current.params.id, }, toastNotifications, + onBeforeRedirect() { + getUrlTracker().setTrackedUrl('/discover'); + }, }) ), }); @@ -194,6 +198,8 @@ function discoverController( const savedSearch = $route.current.locals.savedObjects.savedSearch; $scope.searchSource = savedSearch.searchSource; $scope.indexPattern = resolveIndexPatternLoading(); + //used for functional testing + $scope.fetchCounter = 0; const getTimeField = () => { return isDefaultType($scope.indexPattern) ? $scope.indexPattern.timeFieldName : undefined; @@ -275,6 +281,7 @@ function discoverController( filterManager.getUpdates$(), { next: () => { + $scope.state.filters = filterManager.getAppFilters(); $scope.updateDataSource(); }, }, @@ -784,7 +791,7 @@ function discoverController( $scope.opts.fetch = $scope.fetch = function() { // ignore requests to fetch before the app inits if (!init.complete) return; - + $scope.fetchCounter++; $scope.fetchError = undefined; // Abort any in-progress requests before fetching again @@ -821,9 +828,11 @@ function discoverController( }); }; - $scope.updateQuery = function({ query }) { - setAppState({ query }); - $fetchObservable.next(); + $scope.updateQuery = function({ query }, isUpdate = true) { + if (!_.isEqual(query, appStateContainer.getState().query) || isUpdate === false) { + setAppState({ query }); + $fetchObservable.next(); + } }; $scope.updateSavedQueryId = newSavedQueryId => { diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts index 981855d1ee774..d9e1850cd6a24 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts @@ -28,7 +28,7 @@ import { import { esFilters, Filter, Query } from '../../../../../../../plugins/data/public'; import { migrateLegacyQuery } from '../../../../../../../plugins/kibana_legacy/public'; -interface AppState { +export interface AppState { /** * Columns displayed in the table */ diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/_index.scss b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/_index.scss index 7161560f8fda4..6ddd2e0eae8e3 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/_index.scss +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/_index.scss @@ -1,2 +1,2 @@ @import 'fetch_error/index'; -@import 'field_chooser/index'; +@import 'sidebar/index'; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/_field_chooser.scss b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/_field_chooser.scss deleted file mode 100644 index b05775c4ee95c..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/_field_chooser.scss +++ /dev/null @@ -1,36 +0,0 @@ -.dscFieldChooser { - padding-left: $euiSizeS !important; - padding-right: $euiSizeS !important; -} - -.dscFieldChooser__toggle { - color: $euiColorMediumShade; - margin-left: $euiSizeS !important; -} - -.dscFieldName { - color: $euiColorDarkShade; -} - - -/* - Fixes EUI known issue https://github.com/elastic/eui/issues/1749 -*/ -.dscProgressBarTooltip__anchor { - display: block; -} - -.dscToggleFieldFilterButton { - width: calc(100% - #{$euiSizeS}); - color: $euiColorPrimary; - padding-left: $euiSizeXS; - margin-left: $euiSizeXS; -} - -.dscFieldSearch__filterWrapper { - flex-grow: 0; -} - -.dscFieldSearch__formWrapper { - padding: $euiSizeM; -} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/_index.scss b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/_index.scss deleted file mode 100644 index 91daed8ea048e..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'field_chooser'; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field.html b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field.html deleted file mode 100644 index 06e7cc5075411..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field.html +++ /dev/null @@ -1,26 +0,0 @@ -<li - class="sidebar-item" - attr-field="{{::field.name}}" -> - <div - data-test-subj="field-{{::field.name}}" - ng-click="onClickToggleDetails($event, field)" - kbn-accessible-click - class="sidebar-item-title dscSidebarItem" - > - <div class="dscField dscSidebarItem__label"> - <field-name - field="field" - ></field-name> - </div> - - <button - ng-if="field.name !== '_source'" - ng-click="toggleDisplay(field)" - ng-class="::field.display ? 'kuiButton--danger' : 'kuiButton--primary'" - ng-bind="::addRemoveButtonLabel" - class="dscSidebarItem__action kuiButton kuiButton--small" - data-test-subj="fieldToggle-{{::field.name}}" - ></button> - </div> -</li> diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field.js deleted file mode 100644 index f7f219a287492..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field.js +++ /dev/null @@ -1,139 +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 $ from 'jquery'; -import _ from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { getServices } from '../../../kibana_services'; -import html from './discover_field.html'; -import './string_progress_bar'; -import detailsHtml from './lib/detail_views/string.html'; - -export function createDiscoverFieldDirective($compile) { - return { - restrict: 'E', - template: html, - replace: true, - scope: { - field: '=', - onAddField: '=', - onAddFilter: '=', - onRemoveField: '=', - onShowDetails: '=', - }, - link: function($scope, $elem) { - let detailsElem; - let detailScope; - - const init = function() { - if ($scope.field.details) { - $scope.toggleDetails($scope.field, true); - } - - $scope.addRemoveButtonLabel = $scope.field.display - ? i18n.translate('kbn.discover.fieldChooser.discoverField.removeButtonLabel', { - defaultMessage: 'remove', - }) - : i18n.translate('kbn.discover.fieldChooser.discoverField.addButtonLabel', { - defaultMessage: 'add', - }); - }; - - const getWarnings = function(field) { - let warnings = []; - - if (field.scripted) { - warnings.push( - i18n.translate( - 'kbn.discover.fieldChooser.discoverField.scriptedFieldsTakeLongExecuteDescription', - { - defaultMessage: 'Scripted fields can take a long time to execute.', - } - ) - ); - } - - if (warnings.length > 1) { - warnings = warnings.map(function(warning, i) { - return (i > 0 ? '\n' : '') + (i + 1) + ' - ' + warning; - }); - } - - return warnings; - }; - - $scope.canVisualize = getServices().capabilities.visualize.show; - - $scope.toggleDisplay = function(field) { - if (field.display) { - $scope.onRemoveField(field.name); - } else { - $scope.onAddField(field.name); - } - - if (field.details) { - $scope.toggleDetails(field); - } - }; - - $scope.onClickToggleDetails = function onClickToggleDetails($event, field) { - // Do nothing if the event originated from a child. - if ($event.currentTarget !== $event.target) { - $event.preventDefault(); - } - - $scope.toggleDetails(field); - }; - - $scope.toggleDetails = function(field, recompute) { - if (_.isUndefined(field.details) || recompute) { - $scope.onShowDetails(field, recompute); - detailScope = $scope.$new(); - detailScope.warnings = getWarnings(field); - detailScope.getBucketAriaLabel = bucket => { - return i18n.translate('kbn.discover.fieldChooser.discoverField.bucketAriaLabel', { - defaultMessage: 'Value: {value}', - values: { - value: - bucket.display === '' - ? i18n.translate('kbn.discover.fieldChooser.discoverField.emptyStringText', { - defaultMessage: 'Empty string', - }) - : bucket.display, - }, - }); - }; - - detailsElem = $(detailsHtml); - $compile(detailsElem)(detailScope); - $elem.append(detailsElem).addClass('active'); - $elem.find('.dscSidebarItem').addClass('dscSidebarItem--active'); - } else { - delete field.details; - detailScope.$destroy(); - detailsElem.remove(); - $elem.removeClass('active'); - $elem.find('.dscSidebarItem').removeClass('dscSidebarItem--active'); - } - }; - - init(); - }, - }; -} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field_search_directive.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field_search_directive.ts deleted file mode 100644 index 6d570349ee0c6..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field_search_directive.ts +++ /dev/null @@ -1,28 +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 { wrapInI18nContext } from '../../../kibana_services'; -import { DiscoverFieldSearch } from './discover_field_search'; - -export function createFieldSearchDirective(reactDirective: any) { - return reactDirective(wrapInI18nContext(DiscoverFieldSearch), [ - ['onChange', { watchDepth: 'reference' }], - ['value', { watchDepth: 'value' }], - ['types', { watchDepth: 'value' }], - ]); -} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/field_chooser.html b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/field_chooser.html deleted file mode 100644 index fd63c26aa2bb3..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/field_chooser.html +++ /dev/null @@ -1,99 +0,0 @@ -<section class="sidebar-list" aria-label="{{::'kbn.discover.fieldChooser.filter.indexAndFieldsSectionAriaLabel' | i18n: {defaultMessage: 'Index and fields'} }}"> - <discover-index-pattern-select - selected-index-pattern="$parent.indexPattern" - set-index-pattern="$parent.setIndexPattern" - index-pattern-list="indexPatternList" - > - </discover-index-pattern-select> - <div class="sidebar-item" > - <form> - <discover-field-search - on-change="setFilterValue" - value="filter.vals.name" - types="fieldTypes" - > - </discover-field-search> - </form> - </div> - - <div class="dscSidebar__listHeader sidebar-list-header" ng-if="fields.length"> - <h3 - class="euiFlexItem euiTitle euiTitle--xxxsmall sidebar-list-header-heading" - id="selected_fields" - tabindex="0" - i18n-id="kbn.discover.fieldChooser.filter.selectedFieldsTitle" - i18n-default-message="Selected fields" - ></h3> - </div> - <ul class="list-unstyled dscFieldList--selected" aria-labelledby="selected_fields"> - <discover-field - ng-repeat="field in fields|filter:filter.isFieldFilteredAndDisplayed" - field="field" - on-add-field="onAddField" - on-add-filter="onAddFilter" - on-remove-field="onRemoveField" - on-show-details="computeDetails" - > - </discover-field> - </ul> - - <div class="sidebar-list-header sidebar-item euiFlexGroup euiFlexGroup--gutterMedium" ng-if="fields.length"> - <h3 - class="euiFlexItem euiTitle euiTitle--xxxsmall sidebar-list-header-heading" - id="available_fields" - tabindex="0" - i18n-id="kbn.discover.fieldChooser.filter.availableFieldsTitle" - i18n-default-message="Available fields" - ></h3> - - <div class="euiFlexItem euiFlexItem--flexGrowZero"> - <button - ng-click="$parent.showFields = !$parent.showFields" - aria-hidden="true" - class="kuiButton kuiButton--small visible-xs visible-sm pull-right dscFieldChooser__toggle" - > - <span - aria-hidden="true" - class="kuiIcon" - ng-class="{ 'fa-chevron-right': !$parent.showFields, 'fa-chevron-down': $parent.showFields }" - ></span> - </button> - </div> - </div> - - <ul - ng-show="(popularFields | filter:filter.isFieldFilteredAndNotDisplayed).length > 0" - ng-class="{ 'hidden-sm': !showFields, 'hidden-xs': !showFields }" - class="list-unstyled sidebar-well dscFieldList--popular"> - <li class="sidebar-item sidebar-list-header"> - <h6 - i18n-id="kbn.discover.fieldChooser.filter.popularTitle" - i18n-default-message="Popular" - ></h6> - </li> - <discover-field - ng-repeat="field in popularFields | filter:filter.isFieldFilteredAndNotDisplayed" - field="field" - on-add-field="onAddField" - on-add-filter="onAddFilter" - on-remove-field="onRemoveField" - on-show-details="computeDetails" - > - </discover-field> - </ul> - - <ul - ng-class="{ 'hidden-sm': !showFields, 'hidden-xs': !showFields }" - class="list-unstyled dscFieldList--unpopular"> - <discover-field - ng-repeat="field in unpopularFields | filter:filter.isFieldFilteredAndNotDisplayed" - field="field" - on-add-field="onAddField" - on-add-filter="onAddFilter" - on-remove-field="onRemoveField" - on-show-details="computeDetails" - > - </discover-field> - </ul> - -</section> diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/field_chooser.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/field_chooser.js deleted file mode 100644 index 398728e51862f..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/field_chooser.js +++ /dev/null @@ -1,296 +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 _ from 'lodash'; -import $ from 'jquery'; -import rison from 'rison-node'; -import { fieldCalculator } from './lib/field_calculator'; -import './discover_field'; -import './discover_field_search_directive'; -import './discover_index_pattern_directive'; -import fieldChooserTemplate from './field_chooser.html'; -import { - IndexPatternFieldList, - KBN_FIELD_TYPES, -} from '../../../../../../../../plugins/data/public'; -import { getMapsAppUrl, isFieldVisualizable, isMapsAppRegistered } from './lib/visualize_url_utils'; -import { getServices } from '../../../kibana_services'; - -export function createFieldChooserDirective($location) { - return { - restrict: 'E', - scope: { - columns: '=', - hits: '=', - fieldCounts: '=', - state: '=', - indexPattern: '=', - indexPatternList: '=', - onAddField: '=', - onAddFilter: '=', - onRemoveField: '=', - }, - template: fieldChooserTemplate, - link: function($scope) { - $scope.showFilter = false; - $scope.toggleShowFilter = () => ($scope.showFilter = !$scope.showFilter); - $scope.indexPatternList = _.sortBy($scope.indexPatternList, o => o.get('title')); - const config = getServices().uiSettings; - - const filter = ($scope.filter = { - props: ['type', 'aggregatable', 'searchable', 'missing', 'name'], - defaults: { - missing: true, - type: 'any', - name: '', - }, - boolOpts: [ - { label: 'any', value: undefined }, - { label: 'yes', value: true }, - { label: 'no', value: false }, - ], - reset: function() { - filter.vals = _.clone(filter.defaults); - }, - /** - * filter for fields that are displayed / selected for the data table - */ - isFieldFilteredAndDisplayed: function(field) { - return field.display && isFieldFiltered(field); - }, - /** - * filter for fields that are not displayed / selected for the data table - */ - isFieldFilteredAndNotDisplayed: function(field) { - return !field.display && isFieldFiltered(field) && field.type !== '_source'; - }, - getActive: function() { - return _.some(filter.props, function(prop) { - return filter.vals[prop] !== filter.defaults[prop]; - }); - }, - }); - - function isFieldFiltered(field) { - const matchFilter = filter.vals.type === 'any' || field.type === filter.vals.type; - const isAggregatable = - filter.vals.aggregatable == null || field.aggregatable === filter.vals.aggregatable; - const isSearchable = - filter.vals.searchable == null || field.searchable === filter.vals.searchable; - const scriptedOrMissing = - !filter.vals.missing || field.type === '_source' || field.scripted || field.rowCount > 0; - const matchName = !filter.vals.name || field.name.indexOf(filter.vals.name) !== -1; - - return matchFilter && isAggregatable && isSearchable && scriptedOrMissing && matchName; - } - - $scope.setFilterValue = (name, value) => { - filter.vals[name] = value; - }; - - $scope.filtersActive = 0; - - // set the initial values to the defaults - filter.reset(); - - $scope.$watchCollection('filter.vals', function() { - filter.active = filter.getActive(); - if (filter.vals) { - let count = 0; - Object.keys(filter.vals).forEach(key => { - if (key === 'missing' || key === 'name') { - return; - } - const value = filter.vals[key]; - if ((value && value !== 'any') || value === false) { - count++; - } - }); - $scope.filtersActive = count; - } - }); - - $scope.$watchMulti(['[]fieldCounts', '[]columns', '[]hits'], function(cur, prev) { - const newHits = cur[2] !== prev[2]; - let fields = $scope.fields; - const columns = $scope.columns || []; - const fieldCounts = $scope.fieldCounts; - - if (!fields || newHits) { - $scope.fields = fields = getFields(); - } - - if (!fields) return; - - // group the fields into popular and up-popular lists - _.chain(fields) - .each(function(field) { - field.displayOrder = _.indexOf(columns, field.name) + 1; - field.display = !!field.displayOrder; - field.rowCount = fieldCounts[field.name]; - }) - .sortBy(function(field) { - return (field.count || 0) * -1; - }) - .groupBy(function(field) { - if (field.display) return 'selected'; - return field.count > 0 ? 'popular' : 'unpopular'; - }) - .tap(function(groups) { - groups.selected = _.sortBy(groups.selected || [], 'displayOrder'); - - groups.popular = groups.popular || []; - groups.unpopular = groups.unpopular || []; - - // move excess popular fields to un-popular list - const extras = groups.popular.splice(config.get('fields:popularLimit')); - groups.unpopular = extras.concat(groups.unpopular); - }) - .each(function(group, name) { - $scope[name + 'Fields'] = _.sortBy(group, name === 'selected' ? 'display' : 'name'); - }) - .commit(); - - // include undefined so the user can clear the filter - $scope.fieldTypes = _.union(['any'], _.pluck(fields, 'type')); - }); - - $scope.increaseFieldCounter = function(fieldName) { - $scope.indexPattern.popularizeField(fieldName, 1); - }; - - function getVisualizeUrl(field) { - if (!$scope.state) { - return ''; - } - - if ( - (field.type === KBN_FIELD_TYPES.GEO_POINT || field.type === KBN_FIELD_TYPES.GEO_SHAPE) && - isMapsAppRegistered() - ) { - return getMapsAppUrl(field, $scope.indexPattern, $scope.state, $scope.columns); - } - - let agg = {}; - const isGeoPoint = field.type === KBN_FIELD_TYPES.GEO_POINT; - const type = isGeoPoint ? 'tile_map' : 'histogram'; - // If we're visualizing a date field, and our index is time based (and thus has a time filter), - // then run a date histogram - if (field.type === 'date' && $scope.indexPattern.timeFieldName === field.name) { - agg = { - type: 'date_histogram', - schema: 'segment', - params: { - field: field.name, - interval: 'auto', - }, - }; - } else if (isGeoPoint) { - agg = { - type: 'geohash_grid', - schema: 'segment', - params: { - field: field.name, - precision: 3, - }, - }; - } else { - agg = { - type: 'terms', - schema: 'segment', - params: { - field: field.name, - size: parseInt(config.get('discover:aggs:terms:size'), 10), - orderBy: '2', - }, - }; - } - - return ( - '#/visualize/create?' + - $.param( - _.assign(_.clone($location.search()), { - indexPattern: $scope.state.index, - type: type, - _a: rison.encode({ - filters: $scope.state.filters || [], - query: $scope.state.query || undefined, - vis: { - type: type, - aggs: [{ schema: 'metric', type: 'count', id: '2' }, agg], - }, - }), - }) - ) - ); - } - - $scope.computeDetails = function(field, recompute) { - if (_.isUndefined(field.details) || recompute) { - field.details = { - visualizeUrl: isFieldVisualizable(field) ? getVisualizeUrl(field) : null, - ...fieldCalculator.getFieldValueCounts({ - hits: $scope.hits, - field: field, - count: 5, - grouped: false, - }), - }; - _.each(field.details.buckets, function(bucket) { - bucket.display = field.format.convert(bucket.value); - }); - $scope.increaseFieldCounter(field, 1); - } else { - delete field.details; - } - }; - - function getFields() { - const prevFields = $scope.fields; - const indexPattern = $scope.indexPattern; - const hits = $scope.hits; - const fieldCounts = $scope.fieldCounts; - - if (!indexPattern || !hits || !fieldCounts) return; - - const fieldSpecs = indexPattern.fields.slice(0); - const fieldNamesInDocs = _.keys(fieldCounts); - const fieldNamesInIndexPattern = _.map(indexPattern.fields, 'name'); - - _.difference(fieldNamesInDocs, fieldNamesInIndexPattern).forEach(function( - unknownFieldName - ) { - fieldSpecs.push({ - name: unknownFieldName, - type: 'unknown', - }); - }); - - const fields = new IndexPatternFieldList(indexPattern, fieldSpecs); - - if (prevFields) { - fields.forEach(function(field) { - field.details = (prevFields.getByName(field.name) || {}).details; - }); - } - - return fields; - } - }, - }; -} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/lib/detail_views/string.html b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/lib/detail_views/string.html deleted file mode 100644 index 333dc472e956d..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/lib/detail_views/string.html +++ /dev/null @@ -1,106 +0,0 @@ -<div class="dscFieldDetails"> - <div class="kuiVerticalRhythmSmall"> - <p class="euiText euiText--extraSmall euiTextColor--subdued" ng-show="!field.details.error"> - <span - i18n-id="kbn.discover.fieldChooser.detailViews.topValuesInRecordsDescription" - i18n-default-message="Top 5 values in" - ></span> - <span ng-if="!field.details.error"> - <a - class="kuiLink" - kbn-accessible-click - ng-show="!field.indexPattern.metaFields.includes(field.name) && !field.scripted" - ng-click="onAddFilter('_exists_', field.name, '+')"> - {{::field.details.exists}} - </a> - <span - ng-show="field.indexPattern.metaFields.includes(field.name) || field.scripted"> - {{::field.details.exists}} - </span> - / {{::field.details.total}} - <span - i18n-id="kbn.discover.fieldChooser.detailViews.recordsText" - i18n-default-message="records" - ></span> - </span> - </p> - - <div class="clearfix"></div> - - <div ng-if="field.details.error" class="euiText euiText--extraSmall euiTextColor--subdued">{{field.details.error}}</div> - - <div ng-if="!field.details.error"> - <div ng-repeat="bucket in ::field.details.buckets" class="dscFieldDetailsItem"> - <div class="dscFieldDetailsItem__title"> - <!-- Field value --> - <div - css-truncate - css-truncate-expandable="true" - class="dscFieldDetails__value" - aria-label="{{::getBucketAriaLabel(bucket)}}" - > - {{::bucket.display}} - <em - ng-show="bucket.display === ''" - i18n-id="kbn.discover.fieldChooser.detailViews.emptyStringText" - i18n-default-message="Empty string" - ></em> - </div> - - <!-- Add/remove filter buttons --> - <div - class="dscFieldDetailsItem__buttonGroup" - ng-show="field.filterable" - > - <button - class="dscFieldDetailsItem__button" - ng-click="onAddFilter(field, bucket.value, '+')" - aria-label="{{::'kbn.discover.fieldChooser.detailViews.filterValueButtonAriaLabel' | i18n: {defaultMessage: 'Filter for this value'} }}" - data-test-subj="plus-{{::field.name}}-{{::bucket.display}}" - > - <span - aria-hidden="true" - class="kuiIcon fa-search-plus dscFieldDetails__filter" - ></span> - </button> - - <button - class="dscFieldDetailsItem__button" - ng-click="onAddFilter(field, bucket.value, '-')" - aria-label="{{::'kbn.discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel' | i18n: {defaultMessage: 'Filter out this value'} }}" - data-test-subj="minus-{{::field.name}}-{{::bucket.display}}" - > - <span - aria-hidden="true" - class="kuiIcon fa-search-minus dscFieldDetails__filter" - ></span> - </button> - </div> - </div> - <string-field-progress-bar - percent="{{bucket.percent}}" - count="{{::bucket.count}}" - ></string-field-progress-bar> - </div> - </div> - </div> - - <a - ng-href="{{field.details.visualizeUrl}}" - ng-show="field.details.visualizeUrl && canVisualize" - class="kuiButton kuiButton--secondary kuiButton--small kuiVerticalRhythmSmall" - data-test-subj="fieldVisualize-{{::field.name}}" - > - <span - i18n-id="kbn.discover.fieldChooser.detailViews.visualizeLinkText" - i18n-default-message="Visualize" - ></span> - <span class="discover-field-vis-warning" ng-show="warnings.length" tooltip="{{warnings.join(' ')}}"> - ( <span - i18n-id="kbn.discover.fieldChooser.detailViews.warningsText" - i18n-default-message="{warningsLength, plural, one {# warning} other {# warnings}}" - i18n-values="{ warningsLength: warnings.length }" - ></span> <i aria-hidden="true" class="fa fa-warning"></i> ) - </span> - </a> -</div> diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/lib/visualize_url_utils.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/lib/visualize_url_utils.ts deleted file mode 100644 index 21e8e9f5e39a0..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/lib/visualize_url_utils.ts +++ /dev/null @@ -1,110 +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 uuid from 'uuid/v4'; -// @ts-ignore -import rison from 'rison-node'; -import { - IFieldType, - IIndexPattern, - KBN_FIELD_TYPES, -} from '../../../../../../../../../plugins/data/public'; -import { AppState } from '../../../angular/context_state'; -import { getServices } from '../../../../kibana_services'; - -function getMapsAppBaseUrl() { - const mapsAppVisAlias = getServices() - .visualizations.getAliases() - .find(({ name }) => { - return name === 'maps'; - }); - return mapsAppVisAlias ? mapsAppVisAlias.aliasUrl : null; -} - -export function isMapsAppRegistered() { - return getServices() - .visualizations.getAliases() - .some(({ name }) => { - return name === 'maps'; - }); -} - -export function isFieldVisualizable(field: IFieldType) { - if ( - (field.type === KBN_FIELD_TYPES.GEO_POINT || field.type === KBN_FIELD_TYPES.GEO_SHAPE) && - isMapsAppRegistered() - ) { - return true; - } - return field.visualizable; -} - -export function getMapsAppUrl( - field: IFieldType, - indexPattern: IIndexPattern, - appState: AppState, - columns: string[] -) { - const mapAppParams = new URLSearchParams(); - - // Copy global state - const locationSplit = window.location.href.split('discover?'); - if (locationSplit.length > 1) { - const discoverParams = new URLSearchParams(locationSplit[1]); - const globalStateUrlValue = discoverParams.get('_g'); - if (globalStateUrlValue) { - mapAppParams.set('_g', globalStateUrlValue); - } - } - - // Copy filters and query in app state - const mapsAppState: any = { - filters: appState.filters || [], - }; - if (appState.query) { - mapsAppState.query = appState.query; - } - // @ts-ignore - mapAppParams.set('_a', rison.encode(mapsAppState)); - - // create initial layer descriptor - const hasColumns = columns && columns.length && columns[0] !== '_source'; - const supportsClustering = field.aggregatable; - mapAppParams.set( - 'initialLayers', - // @ts-ignore - rison.encode_array([ - { - id: uuid(), - label: indexPattern.title, - sourceDescriptor: { - id: uuid(), - type: 'ES_SEARCH', - geoField: field.name, - tooltipProperties: hasColumns ? columns : [], - indexPatternId: indexPattern.id, - scalingType: supportsClustering ? 'CLUSTERS' : 'LIMIT', - }, - visible: true, - type: supportsClustering ? 'BLENDED_VECTOR' : 'VECTOR', - }, - ]) - ); - - return getServices().addBasePath(`${getMapsAppBaseUrl()}?${mapAppParams.toString()}`); -} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/__snapshots__/discover_index_pattern.test.tsx.snap b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/__snapshots__/discover_index_pattern.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/__snapshots__/discover_index_pattern.test.tsx.snap rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/__snapshots__/discover_index_pattern.test.tsx.snap diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/_index.scss b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/_index.scss new file mode 100644 index 0000000000000..17b0a6c9cfe4e --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/_index.scss @@ -0,0 +1 @@ +@import './_sidebar'; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/_sidebar.scss b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/_sidebar.scss new file mode 100644 index 0000000000000..fe04d42d614ff --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/_sidebar.scss @@ -0,0 +1,155 @@ +.dscSidebar__container { + padding-left: 0 !important; + padding-right: 0 !important; + background-color: transparent; + border-right-color: transparent; + border-bottom-color: transparent; +} + +.dscIndexPattern__container { + display: flex; + align-items: center; + height: $euiSize * 3; + margin-top: -$euiSizeS; +} + +.dscIndexPattern__triggerButton { + @include euiTitle('xs'); + line-height: $euiSizeXXL; +} + +.dscFieldList { + list-style: none; + margin-bottom: 0; +} + +.dscFieldList--selected, +.dscFieldList--unpopular, +.dscFieldList--popular { + padding-left: $euiSizeS; + padding-right: $euiSizeS; +} + +.dscFieldListHeader { + padding: $euiSizeS $euiSizeS 0 $euiSizeS; + background-color: lightOrDarkTheme(tint($euiColorPrimary, 90%), $euiColorLightShade); +} + +.dscFieldList--popular { + background-color: lightOrDarkTheme(tint($euiColorPrimary, 90%), $euiColorLightShade); +} + +.dscFieldChooser { + padding-left: $euiSizeS !important; + padding-right: $euiSizeS !important; +} + +.dscFieldChooser__toggle { + color: $euiColorMediumShade; + margin-left: $euiSizeS !important; +} + +.dscSidebarItem { + border-top: 1px solid transparent; + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 2px; + cursor: pointer; + font-size: $euiFontSizeXS; + border-top: solid 1px transparent; + border-bottom: solid 1px transparent; + line-height: normal; + + &:hover, + &:focus { + .dscSidebarItem__action { + opacity: 1; + } + } +} + +.dscSidebarItem--active { + border-top: 1px solid $euiColorLightShade; + background: shade($euiColorLightestShade, 5%); + color: $euiColorFullShade; + .euiText { + font-weight: bold; + } +} + +.dscSidebarField { + padding: $euiSizeXS 0; + display: flex; + align-items: flex-start; + max-width: 100%; + margin: 0; + width: 100%; + border: none; + border-radius: 0; + text-align: left; +} + +.dscSidebarField__name { + margin-left: $euiSizeS; + flex-grow: 1; +} + +.dscSidebarField__fieldIcon { + margin-top: $euiSizeXS / 2; + margin-right: $euiSizeXS / 2; +} + +/** + * 1. Only visually hide the action, so that it's still accessible to screen readers. + * 2. When tabbed to, this element needs to be visible for keyboard accessibility. + */ +.dscSidebarItem__action { + opacity: 0; /* 1 */ + + &:focus { + opacity: 1; /* 2 */ + } + font-size: 12px; + padding: 2px 6px !important; + height: 22px !important; + min-width: auto !important; + .euiButton__content { + padding: 0 4px; + } +} + +/* + Fixes EUI known issue https://github.com/elastic/eui/issues/1749 +*/ +.dscProgressBarTooltip__anchor { + display: block; +} + + +.dscFieldSearch { + padding: $euiSizeS; +} + +.dscFieldSearch__toggleButton { + width: calc(100% - #{$euiSizeS}); + color: $euiColorPrimary; + padding-left: $euiSizeXS; + margin-left: $euiSizeXS; +} + +.dscFieldSearch__filterWrapper { + flex-grow: 0; +} + +.dscFieldSearch__formWrapper { + padding: $euiSizeM; +} + +.dscFieldDetails { + padding: $euiSizeS; + background-color: $euiColorLightestShade; + color: $euiTextColor; + margin-bottom: $euiSizeS; +} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/change_indexpattern.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/change_indexpattern.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/change_indexpattern.tsx rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/change_indexpattern.tsx diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field.test.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field.test.tsx new file mode 100644 index 0000000000000..9a6bd65813d18 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field.test.tsx @@ -0,0 +1,111 @@ +/* + * 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 React from 'react'; +// @ts-ignore +import { findTestSubject } from '@elastic/eui/lib/test'; +// @ts-ignore +import StubIndexPattern from 'test_utils/stub_index_pattern'; +// @ts-ignore +import stubbedLogstashFields from 'fixtures/logstash_fields'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { DiscoverField } from './discover_field'; +import { coreMock } from '../../../../../../../../core/public/mocks'; +import { IndexPatternField } from '../../../../../../../../plugins/data/public'; + +jest.mock('../../../kibana_services', () => ({ + getServices: () => ({ + history: { + location: { + search: '', + }, + }, + capabilities: { + visualize: { + show: true, + }, + }, + uiSettings: { + get: (key: string) => { + if (key === 'fields:popularLimit') { + return 5; + } else if (key === 'shortDots:enable') { + return false; + } + }, + }, + }), +})); + +function getComponent(selected = false, showDetails = false, useShortDots = false) { + const indexPattern = new StubIndexPattern( + 'logstash-*', + (cfg: any) => cfg, + 'time', + stubbedLogstashFields(), + coreMock.createStart() + ); + + const field = { + name: 'bytes', + type: 'number', + esTypes: ['long'], + count: 10, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + format: null, + routes: {}, + $$spec: {}, + } as IndexPatternField; + + const props = { + indexPattern, + field, + getDetails: jest.fn(), + onAddFilter: jest.fn(), + onAddField: jest.fn(), + onRemoveField: jest.fn(), + onShowDetails: jest.fn(), + showDetails, + selected, + useShortDots, + }; + const comp = mountWithIntl(<DiscoverField {...props} />); + return { comp, props }; +} + +describe('discover sidebar field', function() { + it('should allow selecting fields', function() { + const { comp, props } = getComponent(); + findTestSubject(comp, 'fieldToggle-bytes').simulate('click'); + expect(props.onAddField).toHaveBeenCalledWith('bytes'); + }); + it('should allow deselecting fields', function() { + const { comp, props } = getComponent(true); + findTestSubject(comp, 'fieldToggle-bytes').simulate('click'); + expect(props.onRemoveField).toHaveBeenCalledWith('bytes'); + }); + it('should trigger onShowDetails', function() { + const { comp, props } = getComponent(); + findTestSubject(comp, 'field-bytes-showDetails').simulate('click'); + expect(props.onShowDetails).toHaveBeenCalledWith(true, props.field); + }); +}); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field.tsx new file mode 100644 index 0000000000000..f7acb751c487b --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field.tsx @@ -0,0 +1,186 @@ +/* + * 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 React from 'react'; +import { EuiButton, EuiToolTip, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { DiscoverFieldDetails } from './discover_field_details'; +import { FieldIcon } from '../../../../../../../../plugins/kibana_react/public'; +import { FieldDetails } from './types'; +import { IndexPatternField, IndexPattern } from '../../../../../../../../plugins/data/public'; +import { shortenDottedString } from '../../helpers'; +import { getFieldTypeName } from './lib/get_field_type_name'; + +export interface DiscoverFieldProps { + /** + * The displayed field + */ + field: IndexPatternField; + /** + * The currently selected index pattern + */ + indexPattern: IndexPattern; + /** + * Callback to add/select the field + */ + onAddField: (fieldName: string) => void; + /** + * Callback to add a filter to filter bar + */ + onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; + /** + * Callback to remove/deselect a the field + * @param fieldName + */ + onRemoveField: (fieldName: string) => void; + /** + * Callback to hide/show details, buckets of the field + */ + onShowDetails: (show: boolean, field: IndexPatternField) => void; + /** + * Determines, whether details of the field are displayed + */ + showDetails: boolean; + /** + * Retrieve details data for the field + */ + getDetails: (field: IndexPatternField) => FieldDetails; + /** + * Determines whether the field is selected + */ + selected?: boolean; + /** + * Determines whether the field name is shortened test.sub1.sub2 = t.s.sub2 + */ + useShortDots?: boolean; +} + +export function DiscoverField({ + field, + indexPattern, + onAddField, + onRemoveField, + onAddFilter, + onShowDetails, + showDetails, + getDetails, + selected, + useShortDots, +}: DiscoverFieldProps) { + const addLabel = i18n.translate('kbn.discover.fieldChooser.discoverField.addButtonLabel', { + defaultMessage: 'Add', + }); + const addLabelAria = i18n.translate( + 'kbn.discover.fieldChooser.discoverField.addButtonAriaLabel', + { + defaultMessage: 'Add {field} to table', + values: { field: field.name }, + } + ); + const removeLabel = i18n.translate('kbn.discover.fieldChooser.discoverField.removeButtonLabel', { + defaultMessage: 'Remove', + }); + const removeLabelAria = i18n.translate( + 'kbn.discover.fieldChooser.discoverField.removeButtonAriaLabel', + { + defaultMessage: 'Remove {field} from table', + values: { field: field.name }, + } + ); + + const toggleDisplay = (f: IndexPatternField) => { + if (selected) { + onRemoveField(f.name); + } else { + onAddField(f.name); + } + }; + + return ( + <> + <div + className={`dscSidebarField dscSidebarItem ${showDetails ? 'dscSidebarItem--active' : ''}`} + tabIndex={0} + onClick={() => onShowDetails(!showDetails, field)} + onKeyPress={() => onShowDetails(!showDetails, field)} + data-test-subj={`field-${field.name}-showDetails`} + > + <span className="dscSidebarField__fieldIcon"> + <FieldIcon + type={field.type} + label={getFieldTypeName(field.type)} + scripted={field.scripted} + /> + </span> + <span className="dscSidebarField__name eui-textTruncate"> + <EuiToolTip + position="top" + content={field.name} + delay="long" + anchorClassName="eui-textTruncate" + > + <EuiText size="xs" data-test-subj={`field-${field.name}`} className="eui-textTruncate"> + {useShortDots ? shortenDottedString(field.name) : field.displayName} + </EuiText> + </EuiToolTip> + </span> + <span> + {field.name !== '_source' && !selected && ( + <EuiButton + fill + size="s" + className="dscSidebarItem__action" + onClick={(ev: React.MouseEvent<HTMLButtonElement>) => { + ev.preventDefault(); + ev.stopPropagation(); + toggleDisplay(field); + }} + data-test-subj={`fieldToggle-${field.name}`} + arial-label={addLabelAria} + > + {addLabel} + </EuiButton> + )} + {field.name !== '_source' && selected && ( + <EuiButton + color="danger" + className="dscSidebarItem__action" + onClick={(ev: React.MouseEvent<HTMLButtonElement>) => { + ev.preventDefault(); + ev.stopPropagation(); + toggleDisplay(field); + }} + data-test-subj={`fieldToggle-${field.name}`} + arial-label={removeLabelAria} + > + {removeLabel} + </EuiButton> + )} + </span> + </div> + {showDetails && ( + <DiscoverFieldDetails + indexPattern={indexPattern} + field={field} + details={getDetails(field)} + onAddFilter={onAddFilter} + /> + )} + </> + ); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_bucket.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_bucket.tsx new file mode 100644 index 0000000000000..5a2b855828f53 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_bucket.tsx @@ -0,0 +1,99 @@ +/* + * 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 React from 'react'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { StringFieldProgressBar } from './string_progress_bar'; +import { Bucket } from './types'; +import { IndexPatternField } from '../../../../../../../../plugins/data/public'; + +interface Props { + bucket: Bucket; + field: IndexPatternField; + onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; +} + +export function DiscoverFieldBucket({ field, bucket, onAddFilter }: Props) { + const emptyTxt = i18n.translate('kbn.discover.fieldChooser.detailViews.emptyStringText', { + defaultMessage: 'Empty string', + }); + const addLabel = i18n.translate( + 'kbn.discover.fieldChooser.detailViews.filterValueButtonAriaLabel', + { + defaultMessage: 'Filter for {field}: "{value}"', + values: { value: bucket.value, field: field.name }, + } + ); + const removeLabel = i18n.translate( + 'kbn.discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel', + { + defaultMessage: 'Filter out {field}: "{value}"', + values: { value: bucket.value, field: field.name }, + } + ); + + return ( + <> + <EuiFlexGroup gutterSize="xs" responsive={false}> + <EuiFlexItem className="eui-textTruncate"> + <EuiText size="xs" className="eui-textTruncate"> + {bucket.display === '' ? emptyTxt : bucket.display} + </EuiText> + </EuiFlexItem> + {field.filterable && ( + <EuiFlexItem grow={false}> + <div> + <EuiButtonIcon + iconSize="s" + iconType="magnifyWithPlus" + onClick={() => onAddFilter(field, bucket.value, '+')} + aria-label={addLabel} + data-test-subj={`plus-${field.name}-${bucket.value}`} + style={{ + minHeight: 'auto', + minWidth: 'auto', + paddingRight: 2, + paddingLeft: 2, + paddingTop: 0, + paddingBottom: 0, + }} + /> + <EuiButtonIcon + iconSize="s" + iconType="magnifyWithMinus" + onClick={() => onAddFilter(field, bucket.value, '-')} + aria-label={removeLabel} + data-test-subj={`minus-${field.name}-${bucket.value}`} + style={{ + minHeight: 'auto', + minWidth: 'auto', + paddingTop: 0, + paddingBottom: 0, + paddingRight: 2, + paddingLeft: 2, + }} + /> + </div> + </EuiFlexItem> + )} + </EuiFlexGroup> + <StringFieldProgressBar percent={bucket.percent} count={bucket.count} /> + </> + ); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_details.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_details.tsx new file mode 100644 index 0000000000000..6266c29745718 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_details.tsx @@ -0,0 +1,98 @@ +/* + * 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 React from 'react'; +import { EuiLink, EuiSpacer, EuiIconTip, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { DiscoverFieldBucket } from './discover_field_bucket'; +import { getWarnings } from './lib/get_warnings'; +import { Bucket, FieldDetails } from './types'; +import { IndexPatternField, IndexPattern } from '../../../../../../../../plugins/data/public'; + +interface DiscoverFieldDetailsProps { + field: IndexPatternField; + indexPattern: IndexPattern; + details: FieldDetails; + onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; +} + +export function DiscoverFieldDetails({ + field, + indexPattern, + details, + onAddFilter, +}: DiscoverFieldDetailsProps) { + const warnings = getWarnings(field); + + return ( + <div className="dscFieldDetails"> + {!details.error && ( + <EuiText size="xs"> + <FormattedMessage + id="kbn.discover.fieldChooser.detailViews.topValuesInRecordsDescription" + defaultMessage="Top 5 values in" + />{' '} + {!indexPattern.metaFields.includes(field.name) && !field.scripted ? ( + <EuiLink onClick={() => onAddFilter('_exists_', field.name, '+')}> + {details.exists} + </EuiLink> + ) : ( + <span>{details.exists}</span> + )}{' '} + / {details.total}{' '} + <FormattedMessage + id="kbn.discover.fieldChooser.detailViews.recordsText" + defaultMessage="records" + /> + </EuiText> + )} + {details.error && <EuiText size="xs">{details.error}</EuiText>} + {!details.error && ( + <div style={{ marginTop: '4px' }}> + {details.buckets.map((bucket: Bucket, idx: number) => ( + <DiscoverFieldBucket + key={`bucket${idx}`} + bucket={bucket} + field={field} + onAddFilter={onAddFilter} + /> + ))} + </div> + )} + + {details.visualizeUrl && ( + <> + <EuiSpacer size={'s'} /> + <EuiLink + href={details.visualizeUrl} + className="kuiButton kuiButton--secondary kuiButton--small kuiVerticalRhythmSmall" + data-test-subj={`fieldVisualize-${field.name}`} + > + <FormattedMessage + id="kbn.discover.fieldChooser.detailViews.visualizeLinkText" + defaultMessage="Visualize" + /> + {warnings.length > 0 && ( + <EuiIconTip type="alert" color="warning" content={warnings.join(' ')} /> + )} + </EuiLink> + </> + )} + </div> + ); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field_search.test.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_search.test.tsx similarity index 98% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field_search.test.tsx rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_search.test.tsx index 5054f7b4bdad1..654df5bfa9ee9 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field_search.test.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_search.test.tsx @@ -34,8 +34,7 @@ describe('DiscoverFieldSearch', () => { function mountComponent(props?: Props) { const compProps = props || defaultProps; - const comp = mountWithIntl(<DiscoverFieldSearch {...compProps} />); - return comp; + return mountWithIntl(<DiscoverFieldSearch {...compProps} />); } function findButtonGroup(component: ReactWrapper, id: string) { diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field_search.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_search.tsx similarity index 99% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field_search.tsx rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_search.tsx index 2910ff2825fe7..57f69693cb544 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_field_search.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field_search.tsx @@ -165,7 +165,7 @@ export function DiscoverFieldSearch({ onChange, value, types }: Props) { <EuiFacetButton aria-label={filterBtnAriaLabel} data-test-subj="toggleFieldFilterButton" - className="dscToggleFieldFilterButton" + className="dscFieldSearch__toggleButton" icon={<EuiIcon type="filter" />} isSelected={activeFiltersCount > 0} quantity={activeFiltersCount} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.test.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_index_pattern.test.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.test.tsx rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_index_pattern.test.tsx diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_index_pattern.tsx similarity index 94% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.tsx rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_index_pattern.tsx index fd2f96ca83a2f..3b01d5e7a9e62 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_index_pattern.tsx @@ -17,7 +17,7 @@ * under the License. */ import React, { useState, useEffect } from 'react'; -import { SavedObject } from 'kibana/server'; +import { SavedObject } from 'kibana/public'; import { IIndexPattern, IndexPatternAttributes } from 'src/plugins/data/public'; import { I18nProvider } from '@kbn/i18n/react'; @@ -65,14 +65,14 @@ export function DiscoverIndexPattern({ } return ( - <div className="indexPattern__container"> + <div className="dscIndexPattern__container"> <I18nProvider> <ChangeIndexPattern trigger={{ label: selected.title, title: selected.title, 'data-test-subj': 'indexPattern-switch-link', - className: 'indexPattern__triggerButton', + className: 'dscIndexPattern__triggerButton', }} indexPatternId={selected.id} indexPatternRefs={options} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.test.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.test.tsx new file mode 100644 index 0000000000000..0df14515adc6d --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.test.tsx @@ -0,0 +1,131 @@ +/* + * 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 _ from 'lodash'; +import { ReactWrapper } from 'enzyme'; +// @ts-ignore +import { findTestSubject } from '@elastic/eui/lib/test'; +// @ts-ignore +import StubIndexPattern from 'test_utils/stub_index_pattern'; +// @ts-ignore +import realHits from 'fixtures/real_hits.js'; +// @ts-ignore +import stubbedLogstashFields from 'fixtures/logstash_fields'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { DiscoverSidebar, DiscoverSidebarProps } from './discover_sidebar'; +import { coreMock } from '../../../../../../../../core/public/mocks'; +import { IndexPatternAttributes } from '../../../../../../../../plugins/data/common'; +import { SavedObject } from '../../../../../../../../core/types'; + +jest.mock('../../../kibana_services', () => ({ + getServices: () => ({ + history: { + location: { + search: '', + }, + }, + capabilities: { + visualize: { + show: true, + }, + }, + uiSettings: { + get: (key: string) => { + if (key === 'fields:popularLimit') { + return 5; + } else if (key === 'shortDots:enable') { + return false; + } + }, + }, + }), +})); + +function getCompProps() { + const indexPattern = new StubIndexPattern( + 'logstash-*', + (cfg: any) => cfg, + 'time', + stubbedLogstashFields(), + coreMock.createStart() + ); + + const hits = _.each(_.cloneDeep(realHits), indexPattern.flattenHit) as Array< + Record<string, unknown> + >; + + const indexPatternList = [ + { id: '0', attributes: { title: 'b' } } as SavedObject<IndexPatternAttributes>, + { id: '1', attributes: { title: 'a' } } as SavedObject<IndexPatternAttributes>, + { id: '2', attributes: { title: 'c' } } as SavedObject<IndexPatternAttributes>, + ]; + + const fieldCounts: Record<string, number> = {}; + + for (const hit of hits) { + for (const key of Object.keys(indexPattern.flattenHit(hit))) { + fieldCounts[key] = (fieldCounts[key] || 0) + 1; + } + } + return { + columns: ['extension'], + fieldCounts, + hits, + indexPatternList, + onAddFilter: jest.fn(), + onAddField: jest.fn(), + onRemoveField: jest.fn(), + selectedIndexPattern: indexPattern, + setIndexPattern: jest.fn(), + state: {}, + }; +} + +describe('discover sidebar', function() { + let props: DiscoverSidebarProps; + let comp: ReactWrapper<DiscoverSidebarProps>; + + beforeAll(() => { + props = getCompProps(); + comp = mountWithIntl(<DiscoverSidebar {...props} />); + }); + + it('should have Selected Fields and Available Fields with Popular Fields sections', function() { + const popular = findTestSubject(comp, 'fieldList-popular'); + const selected = findTestSubject(comp, 'fieldList-selected'); + const unpopular = findTestSubject(comp, 'fieldList-unpopular'); + expect(popular.children().length).toBe(1); + expect(unpopular.children().length).toBe(7); + expect(selected.children().length).toBe(1); + }); + it('should allow selecting fields', function() { + findTestSubject(comp, 'fieldToggle-bytes').simulate('click'); + expect(props.onAddField).toHaveBeenCalledWith('bytes'); + }); + it('should allow deselecting fields', function() { + findTestSubject(comp, 'fieldToggle-extension').simulate('click'); + expect(props.onRemoveField).toHaveBeenCalledWith('extension'); + }); + it('should allow adding filters', function() { + findTestSubject(comp, 'field-extension-showDetails').simulate('click'); + findTestSubject(comp, 'plus-extension-gif').simulate('click'); + expect(props.onAddFilter).toHaveBeenCalled(); + }); +}); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.tsx new file mode 100644 index 0000000000000..5984df9c76e61 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.tsx @@ -0,0 +1,326 @@ +/* + * 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 React, { useCallback, useEffect, useState, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButtonIcon, EuiTitle } from '@elastic/eui'; +import { sortBy } from 'lodash'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { DiscoverField } from './discover_field'; +import { DiscoverIndexPattern } from './discover_index_pattern'; +import { DiscoverFieldSearch } from './discover_field_search'; +import { IndexPatternAttributes } from '../../../../../../../../plugins/data/common'; +import { SavedObject } from '../../../../../../../../core/types'; +import { groupFields } from './lib/group_fields'; +import { + IndexPatternFieldList, + IndexPatternField, + IndexPattern, +} from '../../../../../../../../plugins/data/public'; +import { AppState } from '../../angular/discover_state'; +import { getDetails } from './lib/get_details'; +import { getDefaultFieldFilter, setFieldFilterProp } from './lib/field_filter'; +import { getIndexPatternFieldList } from './lib/get_index_pattern_field_list'; +import { getServices } from '../../../kibana_services'; + +export interface DiscoverSidebarProps { + /** + * the selected columns displayed in the doc table in discover + */ + columns: string[]; + /** + * a statistics of the distribution of fields in the given hits + */ + fieldCounts: Record<string, number>; + /** + * hits fetched from ES, displayed in the doc table + */ + hits: Array<Record<string, unknown>>; + /** + * List of available index patterns + */ + indexPatternList: Array<SavedObject<IndexPatternAttributes>>; + /** + * Callback function when selecting a field + */ + onAddField: (fieldName: string) => void; + /** + * Callback function when adding a filter from sidebar + */ + onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; + /** + * Callback function when removing a field + * @param fieldName + */ + onRemoveField: (fieldName: string) => void; + /** + * Currently selected index pattern + */ + selectedIndexPattern: IndexPattern; + /** + * Callback function to select another index pattern + */ + setIndexPattern: (id: string) => void; + /** + * Current app state, used for generating a link to visualize + */ + state: AppState; +} + +export function DiscoverSidebar({ + columns, + fieldCounts, + hits, + indexPatternList, + onAddField, + onAddFilter, + onRemoveField, + selectedIndexPattern, + setIndexPattern, + state, +}: DiscoverSidebarProps) { + const [openFieldMap, setOpenFieldMap] = useState(new Map()); + const [showFields, setShowFields] = useState(false); + const [fields, setFields] = useState<IndexPatternFieldList | null>(null); + const [fieldFilterState, setFieldFilterState] = useState(getDefaultFieldFilter()); + const services = getServices(); + + useEffect(() => { + const newFields = getIndexPatternFieldList(selectedIndexPattern, fieldCounts); + setFields(newFields); + }, [selectedIndexPattern, fieldCounts, hits]); + + const onShowDetails = useCallback( + (show: boolean, field: IndexPatternField) => { + if (!show) { + setOpenFieldMap(new Map(openFieldMap.set(field.name, false))); + } else { + setOpenFieldMap(new Map(openFieldMap.set(field.name, true))); + selectedIndexPattern.popularizeField(field.name, 1); + } + }, + [openFieldMap, selectedIndexPattern] + ); + const onChangeFieldSearch = useCallback( + (field: string, value: string | boolean | undefined) => { + const newState = setFieldFilterProp(fieldFilterState, field, value); + setFieldFilterState(newState); + }, + [fieldFilterState] + ); + + const getDetailsByField = useCallback( + (ipField: IndexPatternField) => + getDetails(ipField, selectedIndexPattern, state, columns, hits, services), + [selectedIndexPattern, state, columns, hits, services] + ); + + const popularLimit = services.uiSettings.get('fields:popularLimit'); + const useShortDots = services.uiSettings.get('shortDots:enable'); + + const { + selected: selectedFields, + popular: popularFields, + unpopular: unpopularFields, + } = useMemo(() => groupFields(fields, columns, popularLimit, fieldCounts, fieldFilterState), [ + fields, + columns, + popularLimit, + fieldCounts, + fieldFilterState, + ]); + + const fieldTypes = useMemo(() => { + const result = ['any']; + if (Array.isArray(fields)) { + for (const field of fields) { + if (result.indexOf(field.type) === -1) { + result.push(field.type); + } + } + } + return result; + }, [fields]); + + if (!selectedIndexPattern || !fields) { + return null; + } + + return ( + <section + className="sidebar-list" + aria-label={i18n.translate( + 'kbn.discover.fieldChooser.filter.indexAndFieldsSectionAriaLabel', + { + defaultMessage: 'Index and fields', + } + )} + > + <DiscoverIndexPattern + selectedIndexPattern={selectedIndexPattern} + setIndexPattern={setIndexPattern} + indexPatternList={sortBy(indexPatternList, o => o.attributes.title)} + /> + <div className="dscSidebar__item"> + <form> + <DiscoverFieldSearch + onChange={onChangeFieldSearch} + value={fieldFilterState.name} + types={fieldTypes} + /> + </form> + </div> + <div className="sidebar-list"> + {fields.length > 0 && ( + <> + <EuiTitle size="xxxs" id="selected_fields"> + <h3> + <FormattedMessage + id="kbn.discover.fieldChooser.filter.selectedFieldsTitle" + defaultMessage="Selected fields" + /> + </h3> + </EuiTitle> + <ul + className="dscSidebarList dscFieldList--selected" + aria-labelledby="selected_fields" + data-test-subj={`fieldList-selected`} + > + {selectedFields.map((field: IndexPatternField, idx: number) => { + return ( + <li key={`field${idx}`} data-attr-field={field.name} className="dscSidebar__item"> + <DiscoverField + field={field} + indexPattern={selectedIndexPattern} + onAddField={onAddField} + onRemoveField={onRemoveField} + onAddFilter={onAddFilter} + onShowDetails={onShowDetails} + getDetails={getDetailsByField} + showDetails={openFieldMap.get(field.name) || false} + selected={true} + useShortDots={useShortDots} + /> + </li> + ); + })} + </ul> + <div className="euiFlexGroup euiFlexGroup--gutterMedium"> + <EuiTitle size="xxxs" id="available_fields" className="euiFlexItem"> + <h3> + <FormattedMessage + id="kbn.discover.fieldChooser.filter.availableFieldsTitle" + defaultMessage="Available fields" + /> + </h3> + </EuiTitle> + <div className="euiFlexItem euiFlexItem--flexGrowZero"> + <EuiButtonIcon + className={'visible-xs visible-sm dscFieldChooser__toggle'} + iconType={showFields ? 'arrowDown' : 'arrowRight'} + onClick={() => setShowFields(!showFields)} + aria-label={ + showFields + ? i18n.translate( + 'kbn.discover.fieldChooser.filter.indexAndFieldsSectionHideAriaLabel', + { + defaultMessage: 'Hide fields', + } + ) + : i18n.translate( + 'kbn.discover.fieldChooser.filter.indexAndFieldsSectionShowAriaLabel', + { + defaultMessage: 'Show fields', + } + ) + } + /> + </div> + </div> + </> + )} + {popularFields.length > 0 && ( + <div> + <EuiTitle + size="xxxs" + className={`dscFieldListHeader ${!showFields ? 'hidden-sm hidden-xs' : ''}`} + > + <h4 style={{ fontWeight: 'normal' }} id="available_fields_popular"> + <FormattedMessage + id="kbn.discover.fieldChooser.filter.popularTitle" + defaultMessage="Popular" + /> + </h4> + </EuiTitle> + <ul + className={`dscFieldList dscFieldList--popular ${ + !showFields ? 'hidden-sm hidden-xs' : '' + }`} + aria-labelledby="available_fields available_fields_popular" + data-test-subj={`fieldList-popular`} + > + {popularFields.map((field: IndexPatternField, idx: number) => { + return ( + <li key={`field${idx}`} data-attr-field={field.name} className="dscSidebar__item"> + <DiscoverField + field={field} + indexPattern={selectedIndexPattern} + onAddField={onAddField} + onRemoveField={onRemoveField} + onAddFilter={onAddFilter} + onShowDetails={onShowDetails} + getDetails={getDetailsByField} + showDetails={openFieldMap.get(field.name) || false} + useShortDots={useShortDots} + /> + </li> + ); + })} + </ul> + </div> + )} + + <ul + className={`dscFieldList dscFieldList--unpopular ${ + !showFields ? 'hidden-sm hidden-xs' : '' + }`} + aria-labelledby="available_fields" + data-test-subj={`fieldList-unpopular`} + > + {unpopularFields.map((field: IndexPatternField, idx: number) => { + return ( + <li key={`field${idx}`} data-attr-field={field.name} className="dscSidebar__item"> + <DiscoverField + field={field} + indexPattern={selectedIndexPattern} + onAddField={onAddField} + onRemoveField={onRemoveField} + onAddFilter={onAddFilter} + onShowDetails={onShowDetails} + getDetails={getDetailsByField} + showDetails={openFieldMap.get(field.name) || false} + useShortDots={useShortDots} + /> + </li> + ); + })} + </ul> + </div> + </section> + ); +} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern_directive.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar_directive.ts similarity index 61% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern_directive.tsx rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar_directive.ts index d6527b0d7beed..9dcb459f83613 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern_directive.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar_directive.ts @@ -16,25 +16,20 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; import { wrapInI18nContext } from '../../../kibana_services'; -import { DiscoverIndexPattern, DiscoverIndexPatternProps } from './discover_index_pattern'; +import { DiscoverSidebar } from './discover_sidebar'; -/** - * At initial rendering the angular directive the selectedIndexPattern prop is undefined - * This wrapper catches this, had to be introduced to satisfy eslint - */ -export function DiscoverIndexPatternWrapper(props: DiscoverIndexPatternProps) { - if (!props.selectedIndexPattern || !Array.isArray(props.indexPatternList)) { - return null; - } - return <DiscoverIndexPattern {...props} />; -} - -export function createIndexPatternSelectDirective(reactDirective: any) { - return reactDirective(wrapInI18nContext(DiscoverIndexPatternWrapper), [ +export function createDiscoverSidebarDirective(reactDirective: any) { + return reactDirective(wrapInI18nContext(DiscoverSidebar), [ + ['columns', { watchDepth: 'reference' }], + ['fieldCounts', { watchDepth: 'reference' }], + ['hits', { watchDepth: 'reference' }], ['indexPatternList', { watchDepth: 'reference' }], + ['onAddField', { watchDepth: 'reference' }], + ['onAddFilter', { watchDepth: 'reference' }], + ['onRemoveField', { watchDepth: 'reference' }], ['selectedIndexPattern', { watchDepth: 'reference' }], ['setIndexPattern', { watchDepth: 'reference' }], + ['state', { watchDepth: 'reference' }], ]); } diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/index.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/index.ts similarity index 85% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/index.ts rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/index.ts index 4f0537aff5dc2..1b837840b52f6 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/index.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/index.ts @@ -17,5 +17,5 @@ * under the License. */ -// eslint-disable-next-line -export * from '../../../../../../../../plugins/embeddable/public/lib/test_samples'; +export { DiscoverSidebar } from './discover_sidebar'; +export { createDiscoverSidebarDirective } from './discover_sidebar_directive'; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/lib/field_calculator.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/field_calculator.js similarity index 100% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/lib/field_calculator.js rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/field_calculator.js diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_calculator.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/field_calculator.test.ts similarity index 64% rename from src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_calculator.js rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/field_calculator.test.ts index f302d684135f6..98763937e888f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_calculator.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/field_calculator.test.ts @@ -18,25 +18,29 @@ */ import _ from 'lodash'; -import { pluginInstance } from 'plugins/kibana/discover/legacy'; -import ngMock from 'ng_mock'; -import { fieldCalculator } from '../../np_ready/components/field_chooser/lib/field_calculator'; -import expect from '@kbn/expect'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; - -// Load the kibana app dependencies. - -let indexPattern; +// @ts-ignore +import realHits from 'fixtures/real_hits.js'; +// @ts-ignore +import StubIndexPattern from 'test_utils/stub_index_pattern'; +// @ts-ignore +import stubbedLogstashFields from 'fixtures/logstash_fields'; +import { coreMock } from '../../../../../../../../../core/public/mocks'; +import { IndexPattern } from '../../../../../../../../../plugins/data/public'; +// @ts-ignore +import { fieldCalculator } from './field_calculator'; + +let indexPattern: IndexPattern; describe('fieldCalculator', function() { - beforeEach(() => pluginInstance.initializeInnerAngular()); - beforeEach(ngMock.module('app/discover')); - beforeEach( - ngMock.inject(function(Private) { - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - }) - ); - + beforeEach(function() { + indexPattern = new StubIndexPattern( + 'logstash-*', + (cfg: any) => cfg, + 'time', + stubbedLogstashFields(), + coreMock.createStart() + ); + }); it('should have a _countMissing that counts nulls & undefineds in an array', function() { const values = [ ['foo', 'bar'], @@ -52,13 +56,13 @@ describe('fieldCalculator', function() { 'foo', undefined, ]; - expect(fieldCalculator._countMissing(values)).to.be(5); + expect(fieldCalculator._countMissing(values)).toBe(5); }); describe('_groupValues', function() { - let groups; - let params; - let values; + let groups: Record<string, any>; + let params: any; + let values: any; beforeEach(function() { values = [ ['foo', 'bar'], @@ -79,36 +83,36 @@ describe('fieldCalculator', function() { }); it('should have a _groupValues that counts values', function() { - expect(groups).to.be.an(Object); + expect(groups).toBeInstanceOf(Object); }); it('should throw an error if any value is a plain object', function() { expect(function() { fieldCalculator._groupValues([{}, true, false], params); - }).to.throwError(); + }).toThrowError(); }); it('should handle values with dots in them', function() { values = ['0', '0.........', '0.......,.....']; params = {}; groups = fieldCalculator._groupValues(values, params); - expect(groups[values[0]].count).to.be(1); - expect(groups[values[1]].count).to.be(1); - expect(groups[values[2]].count).to.be(1); + expect(groups[values[0]].count).toBe(1); + expect(groups[values[1]].count).toBe(1); + expect(groups[values[2]].count).toBe(1); }); it('should have a a key for value in the array when not grouping array terms', function() { - expect(_.keys(groups).length).to.be(3); - expect(groups.foo).to.be.a(Object); - expect(groups.bar).to.be.a(Object); - expect(groups.baz).to.be.a(Object); + expect(_.keys(groups).length).toBe(3); + expect(groups.foo).toBeInstanceOf(Object); + expect(groups.bar).toBeInstanceOf(Object); + expect(groups.baz).toBeInstanceOf(Object); }); it('should count array terms independently', function() { - expect(groups['foo,bar']).to.be(undefined); - expect(groups.foo.count).to.be(5); - expect(groups.bar.count).to.be(3); - expect(groups.baz.count).to.be(1); + expect(groups['foo,bar']).toBe(undefined); + expect(groups.foo.count).toBe(5); + expect(groups.bar.count).toBe(3); + expect(groups.baz.count).toBe(1); }); describe('grouped array terms', function() { @@ -118,27 +122,27 @@ describe('fieldCalculator', function() { }); it('should group array terms when passed params.grouped', function() { - expect(_.keys(groups).length).to.be(4); - expect(groups['foo,bar']).to.be.a(Object); + expect(_.keys(groups).length).toBe(4); + expect(groups['foo,bar']).toBeInstanceOf(Object); }); it('should contain the original array as the value', function() { - expect(groups['foo,bar'].value).to.eql(['foo', 'bar']); + expect(groups['foo,bar'].value).toEqual(['foo', 'bar']); }); it('should count the pairs separately from the values they contain', function() { - expect(groups['foo,bar'].count).to.be(2); - expect(groups.foo.count).to.be(3); - expect(groups.bar.count).to.be(1); + expect(groups['foo,bar'].count).toBe(2); + expect(groups.foo.count).toBe(3); + expect(groups.bar.count).toBe(1); }); }); }); describe('getFieldValues', function() { - let hits; + let hits: any; beforeEach(function() { - hits = _.each(require('fixtures/real_hits.js'), indexPattern.flattenHit); + hits = _.each(_.cloneDeep(realHits), indexPattern.flattenHit); }); it('Should return an array of values for _source fields', function() { @@ -146,32 +150,32 @@ describe('fieldCalculator', function() { hits, indexPattern.fields.getByName('extension') ); - expect(extensions).to.be.an(Array); + expect(extensions).toBeInstanceOf(Array); expect( _.filter(extensions, function(v) { return v === 'html'; }).length - ).to.be(8); - expect(_.uniq(_.clone(extensions)).sort()).to.eql(['gif', 'html', 'php', 'png']); + ).toBe(8); + expect(_.uniq(_.clone(extensions)).sort()).toEqual(['gif', 'html', 'php', 'png']); }); it('Should return an array of values for core meta fields', function() { const types = fieldCalculator.getFieldValues(hits, indexPattern.fields.getByName('_type')); - expect(types).to.be.an(Array); + expect(types).toBeInstanceOf(Array); expect( _.filter(types, function(v) { return v === 'apache'; }).length - ).to.be(18); - expect(_.uniq(_.clone(types)).sort()).to.eql(['apache', 'nginx']); + ).toBe(18); + expect(_.uniq(_.clone(types)).sort()).toEqual(['apache', 'nginx']); }); }); describe('getFieldValueCounts', function() { - let params; + let params: { hits: any; field: any; count: number }; beforeEach(function() { params = { - hits: require('fixtures/real_hits.js'), + hits: _.cloneDeep(realHits), field: indexPattern.fields.getByName('extension'), count: 3, }; @@ -179,36 +183,36 @@ describe('fieldCalculator', function() { it('counts the top 3 values', function() { const extensions = fieldCalculator.getFieldValueCounts(params); - expect(extensions).to.be.an(Object); - expect(extensions.buckets).to.be.an(Array); - expect(extensions.buckets.length).to.be(3); - expect(_.pluck(extensions.buckets, 'value')).to.eql(['html', 'php', 'gif']); - expect(extensions.error).to.be(undefined); + expect(extensions).toBeInstanceOf(Object); + expect(extensions.buckets).toBeInstanceOf(Array); + expect(extensions.buckets.length).toBe(3); + expect(_.pluck(extensions.buckets, 'value')).toEqual(['html', 'php', 'gif']); + expect(extensions.error).toBe(undefined); }); it('fails to analyze geo and attachment types', function() { params.field = indexPattern.fields.getByName('point'); - expect(fieldCalculator.getFieldValueCounts(params).error).to.not.be(undefined); + expect(fieldCalculator.getFieldValueCounts(params).error).not.toBe(undefined); params.field = indexPattern.fields.getByName('area'); - expect(fieldCalculator.getFieldValueCounts(params).error).to.not.be(undefined); + expect(fieldCalculator.getFieldValueCounts(params).error).not.toBe(undefined); params.field = indexPattern.fields.getByName('request_body'); - expect(fieldCalculator.getFieldValueCounts(params).error).to.not.be(undefined); + expect(fieldCalculator.getFieldValueCounts(params).error).not.toBe(undefined); }); it('fails to analyze fields that are in the mapping, but not the hits', function() { params.field = indexPattern.fields.getByName('ip'); - expect(fieldCalculator.getFieldValueCounts(params).error).to.not.be(undefined); + expect(fieldCalculator.getFieldValueCounts(params).error).not.toBe(undefined); }); it('counts the total hits', function() { - expect(fieldCalculator.getFieldValueCounts(params).total).to.be(params.hits.length); + expect(fieldCalculator.getFieldValueCounts(params).total).toBe(params.hits.length); }); it('counts the hits the field exists in', function() { params.field = indexPattern.fields.getByName('phpmemory'); - expect(fieldCalculator.getFieldValueCounts(params).exists).to.be(5); + expect(fieldCalculator.getFieldValueCounts(params).exists).toBe(5); }); }); }); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/field_filter.test.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/field_filter.test.ts new file mode 100644 index 0000000000000..ca0fcfc846362 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/field_filter.test.ts @@ -0,0 +1,96 @@ +/* + * 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 { getDefaultFieldFilter, setFieldFilterProp, isFieldFiltered } from './field_filter'; +import { IndexPatternField } from '../../../../../../../../../plugins/data/public'; + +describe('field_filter', function() { + it('getDefaultFieldFilter should return default filter state', function() { + expect(getDefaultFieldFilter()).toMatchInlineSnapshot(` + Object { + "aggregatable": null, + "missing": true, + "name": "", + "searchable": null, + "type": "any", + } + `); + }); + it('setFieldFilterProp should return allow filter changes', function() { + const state = getDefaultFieldFilter(); + const targetState = { + aggregatable: true, + missing: true, + name: 'test', + searchable: true, + type: 'string', + }; + const actualState = Object.entries(targetState).reduce((acc, kv) => { + return setFieldFilterProp(acc, kv[0], kv[1]); + }, state); + expect(actualState).toMatchInlineSnapshot(` + Object { + "aggregatable": true, + "missing": true, + "name": "test", + "searchable": true, + "type": "string", + } + `); + }); + it('filters a given list', () => { + const defaultState = getDefaultFieldFilter(); + const fieldList = [ + { + name: 'bytes', + type: 'number', + esTypes: ['long'], + count: 10, + scripted: false, + searchable: false, + aggregatable: false, + }, + { + name: 'extension', + type: 'string', + esTypes: ['text'], + count: 10, + scripted: true, + searchable: true, + aggregatable: true, + }, + ] as IndexPatternField[]; + + [ + { filter: {}, result: ['bytes', 'extension'] }, + { filter: { name: 'by' }, result: ['bytes'] }, + { filter: { aggregatable: true }, result: ['extension'] }, + { filter: { aggregatable: true, searchable: false }, result: [] }, + { filter: { type: 'string' }, result: ['extension'] }, + ].forEach(test => { + const filtered = fieldList + .filter(field => + isFieldFiltered(field, { ...defaultState, ...test.filter }, { bytes: 1, extension: 1 }) + ) + .map(field => field.name); + + expect(filtered).toEqual(test.result); + }); + }); +}); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/field_filter.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/field_filter.ts new file mode 100644 index 0000000000000..ed7ad1a43fa8c --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/field_filter.ts @@ -0,0 +1,78 @@ +/* + * 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 { IndexPatternField } from '../../../../../../../../../plugins/data/public'; + +export interface FieldFilterState { + missing: boolean; + type: string; + name: string; + aggregatable: null | boolean; + searchable: null | boolean; +} + +export function getDefaultFieldFilter(): FieldFilterState { + return { + missing: true, + type: 'any', + name: '', + aggregatable: null, + searchable: null, + }; +} + +export function setFieldFilterProp( + state: FieldFilterState, + name: string, + value: string | boolean | null | undefined +): FieldFilterState { + const newState = { ...state }; + if (name === 'missing') { + newState.missing = Boolean(value); + } else if (name === 'aggregatable') { + newState.aggregatable = typeof value !== 'boolean' ? null : value; + } else if (name === 'searchable') { + newState.searchable = typeof value !== 'boolean' ? null : value; + } else if (name === 'name') { + newState.name = String(value); + } else if (name === 'type') { + newState.type = String(value); + } + return newState; +} + +export function isFieldFiltered( + field: IndexPatternField, + filterState: FieldFilterState, + fieldCounts: Record<string, number> +): boolean { + const matchFilter = filterState.type === 'any' || field.type === filterState.type; + const isAggregatable = + filterState.aggregatable === null || field.aggregatable === filterState.aggregatable; + const isSearchable = + filterState.searchable === null || field.searchable === filterState.searchable; + const scriptedOrMissing = + !filterState.missing || + field.type === '_source' || + field.scripted || + fieldCounts[field.name] > 0; + const matchName = !filterState.name || field.name.indexOf(filterState.name) !== -1; + + return matchFilter && isAggregatable && isSearchable && scriptedOrMissing && matchName; +} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_details.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_details.ts new file mode 100644 index 0000000000000..9999b108c5cc1 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_details.ts @@ -0,0 +1,52 @@ +/* + * 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 { getVisualizeUrl, isFieldVisualizable } from './visualize_url_utils'; +import { AppState } from '../../../angular/discover_state'; +// @ts-ignore +import { fieldCalculator } from './field_calculator'; +import { IndexPatternField, IndexPattern } from '../../../../../../../../../plugins/data/public'; +import { DiscoverServices } from '../../../../build_services'; + +export function getDetails( + field: IndexPatternField, + indexPattern: IndexPattern, + state: AppState, + columns: string[], + hits: Array<Record<string, unknown>>, + services: DiscoverServices +) { + const details = { + visualizeUrl: + services.capabilities.visualize.show && isFieldVisualizable(field, services.visualizations) + ? getVisualizeUrl(field, indexPattern, state, columns, services) + : null, + ...fieldCalculator.getFieldValueCounts({ + hits, + field, + count: 5, + grouped: false, + }), + }; + if (details.buckets) { + for (const bucket of details.buckets) { + bucket.display = field.format.convert(bucket.value); + } + } + return details; +} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_field_type_name.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_field_type_name.ts new file mode 100644 index 0000000000000..0cf428ee48b9d --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_field_type_name.ts @@ -0,0 +1,73 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export function getFieldTypeName(type: string) { + switch (type) { + case 'boolean': + return i18n.translate('kbn.discover.fieldNameIcons.booleanAriaLabel', { + defaultMessage: 'Boolean field', + }); + case 'conflict': + return i18n.translate('kbn.discover.fieldNameIcons.conflictFieldAriaLabel', { + defaultMessage: 'Conflicting field', + }); + case 'date': + return i18n.translate('kbn.discover.fieldNameIcons.dateFieldAriaLabel', { + defaultMessage: 'Date field', + }); + case 'geo_point': + return i18n.translate('kbn.discover.fieldNameIcons.geoPointFieldAriaLabel', { + defaultMessage: 'Geo point field', + }); + case 'geo_shape': + return i18n.translate('kbn.discover.fieldNameIcons.geoShapeFieldAriaLabel', { + defaultMessage: 'Geo shape field', + }); + case 'ip': + return i18n.translate('kbn.discover.fieldNameIcons.ipAddressFieldAriaLabel', { + defaultMessage: 'IP address field', + }); + case 'murmur3': + return i18n.translate('kbn.discover.fieldNameIcons.murmur3FieldAriaLabel', { + defaultMessage: 'Murmur3 field', + }); + case 'number': + return i18n.translate('kbn.discover.fieldNameIcons.numberFieldAriaLabel', { + defaultMessage: 'Number field', + }); + case 'source': + // Note that this type is currently not provided, type for _source is undefined + return i18n.translate('kbn.discover.fieldNameIcons.sourceFieldAriaLabel', { + defaultMessage: 'Source field', + }); + case 'string': + return i18n.translate('kbn.discover.fieldNameIcons.stringFieldAriaLabel', { + defaultMessage: 'String field', + }); + case 'nested': + return i18n.translate('kbn.discover.fieldNameIcons.nestedFieldAriaLabel', { + defaultMessage: 'Nested field', + }); + default: + return i18n.translate('kbn.discover.fieldNameIcons.unknownFieldAriaLabel', { + defaultMessage: 'Unknown field', + }); + } +} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_index_pattern_field_list.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_index_pattern_field_list.ts new file mode 100644 index 0000000000000..1b906501c6fe7 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_index_pattern_field_list.ts @@ -0,0 +1,44 @@ +/* + * 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 { difference, map } from 'lodash'; +import { + IndexPatternFieldList, + IndexPattern, + IndexPatternField, +} from '../../../../../../../../../plugins/data/public'; + +export function getIndexPatternFieldList( + indexPattern: IndexPattern, + fieldCounts: Record<string, number> +): IndexPatternFieldList { + if (!indexPattern || !fieldCounts) return new IndexPatternFieldList(indexPattern, []); + + const fieldSpecs = indexPattern.fields.slice(0); + const fieldNamesInDocs = Object.keys(fieldCounts); + const fieldNamesInIndexPattern = map(indexPattern.fields, 'name'); + + difference(fieldNamesInDocs, fieldNamesInIndexPattern).forEach(unknownFieldName => { + fieldSpecs.push({ + name: String(unknownFieldName), + type: 'unknown', + } as IndexPatternField); + }); + + return new IndexPatternFieldList(indexPattern, fieldSpecs); +} diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_warnings.ts similarity index 56% rename from test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.js rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_warnings.ts index c5b074db43a1b..51d18c03888a7 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/get_warnings.ts @@ -16,37 +16,28 @@ * specific language governing permissions and limitations * under the License. */ +import { i18n } from '@kbn/i18n'; +import { IndexPatternField } from '../../../../../../../../../plugins/data/public'; -import React from 'react'; +export function getWarnings(field: IndexPatternField) { + let warnings = []; -import { EuiBadge } from '@elastic/eui'; - -export class SelfChangingComponent extends React.Component { - onClick = () => { - this.props.vis.params.counter++; - this.props.vis.updateState(); - }; - - render() { - return ( - <div> - <EuiBadge - data-test-subj="counter" - onClick={this.onClick} - onClickAriaLabel="Increase counter" - color="primary" - > - {this.props.vis.params.counter} - </EuiBadge> - </div> + if (field.scripted) { + warnings.push( + i18n.translate( + 'kbn.discover.fieldChooser.discoverField.scriptedFieldsTakeLongExecuteDescription', + { + defaultMessage: 'Scripted fields can take a long time to execute.', + } + ) ); } - componentDidMount() { - this.props.renderComplete(); + if (warnings.length > 1) { + warnings = warnings.map(function(warning, i) { + return (i > 0 ? '\n' : '') + (i + 1) + ' - ' + warning; + }); } - componentDidUpdate() { - this.props.renderComplete(); - } + return warnings; } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/group_fields.test.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/group_fields.test.ts new file mode 100644 index 0000000000000..e83287a139dd0 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/group_fields.test.ts @@ -0,0 +1,114 @@ +/* + * 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 { groupFields } from './group_fields'; +import { getDefaultFieldFilter } from './field_filter'; + +describe('group_fields', function() { + it('should group fields in selected, popular, unpopular group', function() { + const fields = [ + { + name: 'category', + type: 'string', + esTypes: ['text'], + count: 1, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'currency', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'customer_birth_date', + type: 'date', + esTypes: ['date'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + ]; + + const fieldCounts = { + category: 1, + currency: 1, + customer_birth_date: 1, + }; + + const fieldFilterState = getDefaultFieldFilter(); + + const actual = groupFields(fields as any, ['currency'], 5, fieldCounts, fieldFilterState); + expect(actual).toMatchInlineSnapshot(` + Object { + "popular": Array [ + Object { + "aggregatable": true, + "count": 1, + "esTypes": Array [ + "text", + ], + "name": "category", + "readFromDocValues": true, + "scripted": false, + "searchable": true, + "type": "string", + }, + ], + "selected": Array [ + Object { + "aggregatable": true, + "count": 0, + "esTypes": Array [ + "keyword", + ], + "name": "currency", + "readFromDocValues": true, + "scripted": false, + "searchable": true, + "type": "string", + }, + ], + "unpopular": Array [ + Object { + "aggregatable": true, + "count": 0, + "esTypes": Array [ + "date", + ], + "name": "customer_birth_date", + "readFromDocValues": true, + "scripted": false, + "searchable": true, + "type": "date", + }, + ], + } + `); + }); +}); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/group_fields.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/group_fields.tsx new file mode 100644 index 0000000000000..85ca8d6a4e15e --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/group_fields.tsx @@ -0,0 +1,78 @@ +/* + * 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 { + IndexPatternFieldList, + IndexPatternField, +} from '../../../../../../../../../plugins/data/public'; +import { FieldFilterState, isFieldFiltered } from './field_filter'; + +interface GroupedFields { + selected: IndexPatternField[]; + popular: IndexPatternField[]; + unpopular: IndexPatternField[]; +} + +/** + * group the fields into selected, popular and unpopular, filter by fieldFilterState + */ +export function groupFields( + fields: IndexPatternFieldList | null, + columns: string[], + popularLimit: number, + fieldCounts: Record<string, number>, + fieldFilterState: FieldFilterState +): GroupedFields { + const result: GroupedFields = { + selected: [], + popular: [], + unpopular: [], + }; + if (!Array.isArray(fields) || !Array.isArray(columns) || typeof fieldCounts !== 'object') { + return result; + } + + const popular = fields + .filter(field => !columns.includes(field.name) && field.count) + .sort((a: IndexPatternField, b: IndexPatternField) => (b.count || 0) - (a.count || 0)) + .map(field => field.name) + .slice(0, popularLimit); + + const compareFn = (a: IndexPatternField, b: IndexPatternField) => { + if (!a.displayName) { + return 0; + } + return a.displayName.localeCompare(b.displayName || ''); + }; + const fieldsSorted = fields.sort(compareFn); + + for (const field of fieldsSorted) { + if (!isFieldFiltered(field, fieldFilterState, fieldCounts)) { + continue; + } + if (columns.includes(field.name)) { + result.selected.push(field); + } else if (popular.includes(field.name) && field.type !== '_source') { + result.popular.push(field); + } else if (field.type !== '_source') { + result.unpopular.push(field); + } + } + + return result; +} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/visualize_url_utils.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/visualize_url_utils.ts new file mode 100644 index 0000000000000..d146d212055b7 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/visualize_url_utils.ts @@ -0,0 +1,188 @@ +/* + * 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 uuid from 'uuid/v4'; +import rison from 'rison-node'; +import { parse, stringify } from 'query-string'; +import { + IFieldType, + IIndexPattern, + IndexPatternField, + KBN_FIELD_TYPES, +} from '../../../../../../../../../plugins/data/public'; +import { AppState } from '../../../angular/discover_state'; +import { DiscoverServices } from '../../../../build_services'; +import { + VisualizationsStart, + VisTypeAlias, +} from '../../../../../../../../../plugins/visualizations/public'; + +function getMapsAppBaseUrl(visualizations: VisualizationsStart) { + const mapsAppVisAlias = visualizations.getAliases().find(({ name }) => { + return name === 'maps'; + }); + return mapsAppVisAlias ? mapsAppVisAlias.aliasUrl : null; +} + +export function isMapsAppRegistered(visualizations: VisualizationsStart) { + return visualizations.getAliases().some(({ name }: VisTypeAlias) => { + return name === 'maps'; + }); +} + +export function isFieldVisualizable(field: IFieldType, visualizations: VisualizationsStart) { + if (field.name === '_id') { + // Else you'd get a 'Fielddata access on the _id field is disallowed' error on ES side. + return false; + } + if ( + (field.type === KBN_FIELD_TYPES.GEO_POINT || field.type === KBN_FIELD_TYPES.GEO_SHAPE) && + isMapsAppRegistered(visualizations) + ) { + return true; + } + return field.visualizable; +} + +export function getMapsAppUrl( + field: IFieldType, + indexPattern: IIndexPattern, + appState: AppState, + columns: string[], + services: DiscoverServices +) { + const mapAppParams = new URLSearchParams(); + + // Copy global state + const locationSplit = window.location.href.split('discover?'); + if (locationSplit.length > 1) { + const discoverParams = new URLSearchParams(locationSplit[1]); + const globalStateUrlValue = discoverParams.get('_g'); + if (globalStateUrlValue) { + mapAppParams.set('_g', globalStateUrlValue); + } + } + + // Copy filters and query in app state + const mapsAppState: any = { + filters: appState.filters || [], + }; + if (appState.query) { + mapsAppState.query = appState.query; + } + // @ts-ignore + mapAppParams.set('_a', rison.encode(mapsAppState)); + + // create initial layer descriptor + const hasColumns = columns && columns.length && columns[0] !== '_source'; + const supportsClustering = field.aggregatable; + mapAppParams.set( + 'initialLayers', + // @ts-ignore + rison.encode_array([ + { + id: uuid(), + label: indexPattern.title, + sourceDescriptor: { + id: uuid(), + type: 'ES_SEARCH', + geoField: field.name, + tooltipProperties: hasColumns ? columns : [], + indexPatternId: indexPattern.id, + scalingType: supportsClustering ? 'CLUSTERS' : 'LIMIT', + }, + visible: true, + type: supportsClustering ? 'BLENDED_VECTOR' : 'VECTOR', + }, + ]) + ); + + return services.addBasePath( + `${getMapsAppBaseUrl(services.visualizations)}?${mapAppParams.toString()}` + ); +} + +export function getVisualizeUrl( + field: IndexPatternField, + indexPattern: IIndexPattern, + state: AppState, + columns: string[], + services: DiscoverServices +) { + const aggsTermSize = services.uiSettings.get('discover:aggs:terms:size'); + const urlParams = parse(services.history.location.search) as Record<string, string>; + + if ( + (field.type === KBN_FIELD_TYPES.GEO_POINT || field.type === KBN_FIELD_TYPES.GEO_SHAPE) && + isMapsAppRegistered(services.visualizations) + ) { + return getMapsAppUrl(field, indexPattern, state, columns, services); + } + + let agg; + const isGeoPoint = field.type === KBN_FIELD_TYPES.GEO_POINT; + const type = isGeoPoint ? 'tile_map' : 'histogram'; + // If we're visualizing a date field, and our index is time based (and thus has a time filter), + // then run a date histogram + if (field.type === 'date' && indexPattern.timeFieldName === field.name) { + agg = { + type: 'date_histogram', + schema: 'segment', + params: { + field: field.name, + interval: 'auto', + }, + }; + } else if (isGeoPoint) { + agg = { + type: 'geohash_grid', + schema: 'segment', + params: { + field: field.name, + precision: 3, + }, + }; + } else { + agg = { + type: 'terms', + schema: 'segment', + params: { + field: field.name, + size: parseInt(aggsTermSize, 10), + orderBy: '2', + }, + }; + } + const linkUrlParams = { + ...urlParams, + ...{ + indexPattern: state.index!, + type, + _a: rison.encode({ + filters: state.filters || [], + query: state.query, + vis: { + type, + aggs: [{ schema: 'metric', type: 'count', id: '2' }, agg], + }, + } as any), + }, + }; + + return `#/visualize/create?${stringify(linkUrlParams)}`; +} diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/string_progress_bar.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/string_progress_bar.tsx similarity index 80% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/string_progress_bar.tsx rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/string_progress_bar.tsx index 0c5e7fa69357d..7ea41aa4bf270 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/string_progress_bar.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/string_progress_bar.tsx @@ -18,14 +18,13 @@ */ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiProgress, EuiText, EuiToolTip } from '@elastic/eui'; -import { wrapInI18nContext } from '../../../kibana_services'; interface Props { percent: number; count: number; } -function StringFieldProgressBar(props: Props) { +export function StringFieldProgressBar(props: Props) { return ( <EuiToolTip anchorClassName="dscProgressBarTooltip__anchor" @@ -33,13 +32,13 @@ function StringFieldProgressBar(props: Props) { delay="regular" position="right" > - <EuiFlexGroup alignItems="center"> + <EuiFlexGroup alignItems="center" responsive={false}> <EuiFlexItem> <EuiProgress value={props.percent} max={100} color="secondary" - aria-labelledby="CanvasAssetManagerLabel" + aria-hidden={true} size="l" /> </EuiFlexItem> @@ -50,7 +49,3 @@ function StringFieldProgressBar(props: Props) { </EuiToolTip> ); } - -export function createStringFieldProgressBarDirective(reactDirective: any) { - return reactDirective(wrapInI18nContext(StringFieldProgressBar)); -} diff --git a/src/legacy/core_plugins/embeddable_api/index.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/types.ts similarity index 72% rename from src/legacy/core_plugins/embeddable_api/index.ts rename to src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/types.ts index 52206e3d0f105..a1d71b4d3447d 100644 --- a/src/legacy/core_plugins/embeddable_api/index.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/types.ts @@ -17,9 +17,22 @@ * under the License. */ -import { LegacyPluginApi, LegacyPluginSpec, ArrayOrItem } from 'src/legacy/plugin_discovery/types'; +export interface IndexPatternRef { + id: string; + title: string; +} + +export interface FieldDetails { + error: string; + exists: number; + total: boolean; + buckets: Bucket[]; + visualizeUrl: string; +} -// eslint-disable-next-line import/no-default-export -export default function(kibana: LegacyPluginApi): ArrayOrItem<LegacyPluginSpec> { - return new kibana.Plugin({}); +export interface Bucket { + display: string; + value: string; + percent: number; + count: number; } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable.ts index d09b7612af49c..f8e769d837447 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import angular from 'angular'; import _ from 'lodash'; import * as Rx from 'rxjs'; import { Subscription } from 'rxjs'; @@ -23,7 +24,7 @@ import { i18n } from '@kbn/i18n'; import { UiActionsStart, APPLY_FILTER_TRIGGER, -} from '../../../../../../..//plugins/ui_actions/public'; +} from '../../../../../../../plugins/ui_actions/public'; import { RequestAdapter, Adapters } from '../../../../../../../plugins/inspector/public'; import { esFilters, @@ -34,14 +35,13 @@ import { Query, IFieldType, } from '../../../../../../../plugins/data/public'; -import { Container, Embeddable } from '../../../../../embeddable_api/public/np_ready/public'; +import { Container, Embeddable } from '../../../../../../../plugins/embeddable/public'; import * as columnActions from '../angular/doc_table/actions/columns'; import searchTemplate from './search_template.html'; import { ISearchEmbeddable, SearchInput, SearchOutput } from './types'; import { SortOrder } from '../angular/doc_table/components/table_header/helpers'; import { getSortForSearchSource } from '../angular/doc_table/lib/get_sort_for_search_source'; import { - angular, getRequestInspectorStats, getResponseInspectorStats, getServices, diff --git a/src/legacy/core_plugins/kibana/public/discover/plugin.ts b/src/legacy/core_plugins/kibana/public/discover/plugin.ts index 42f3ec6726097..fcac7aa74f54a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/discover/plugin.ts @@ -31,7 +31,7 @@ import { registerFeature } from './np_ready/register_feature'; import './kibana_services'; import { EmbeddableStart, EmbeddableSetup } from '../../../../../plugins/embeddable/public'; import { getInnerAngularModule, getInnerAngularModuleEmbeddable } from './get_inner_angular'; -import { setAngularModule, setServices } from './kibana_services'; +import { setAngularModule, setServices, setUrlTracker } from './kibana_services'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; import { ChartsPluginStart } from '../../../../../plugins/charts/public'; import { buildServices } from './build_services'; @@ -45,7 +45,7 @@ import { HomePublicPluginSetup } from '../../../../../plugins/home/public'; import { VisualizationsStart, VisualizationsSetup, -} from '../../../visualizations/public/np_ready/public'; +} from '../../../../../plugins/visualizations/public'; import { createKbnUrlTracker } from '../../../../../plugins/kibana_utils/public'; export interface DiscoverSetupPlugins { @@ -92,7 +92,12 @@ export class DiscoverPlugin implements Plugin<void, void> { public initializeServices?: () => Promise<{ core: CoreStart; plugins: DiscoverStartPlugins }>; setup(core: CoreSetup<DiscoverStartPlugins, void>, plugins: DiscoverSetupPlugins) { - const { appMounted, appUnMounted, stop: stopUrlTracker } = createKbnUrlTracker({ + const { + appMounted, + appUnMounted, + stop: stopUrlTracker, + setActiveUrl: setTrackedUrl, + } = createKbnUrlTracker({ baseUrl: core.http.basePath.prepend('/app/kibana'), defaultSubUrl: '#/discover', storageKey: 'lastUrl:discover', @@ -113,6 +118,7 @@ export class DiscoverPlugin implements Plugin<void, void> { }, ], }); + setUrlTracker({ setTrackedUrl }); this.stopUrlTracking = () => { stopUrlTracker(); }; diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/activemq.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/activemq.svg index 20694ba6e62c7..8d525c23e6801 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/activemq.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/activemq.svg @@ -1,31 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 128 128"> - <defs> - <filter id="a" color-interpolation-filters="sRGB"> - <feFlood flood-opacity=".498" flood-color="#000" result="flood" /> - <feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1" /> - <feGaussianBlur in="composite1" stdDeviation=".2" result="blur" /> - <feOffset dx="1" dy="1" result="offset" /> - <feComposite in="SourceGraphic" in2="offset" result="composite2" /> - </filter> - </defs> - <g filter="url(#a)" fill="#fff" transform="matrix(2.8 0 0 2.8 -90 -305)"> - <path d="M64.697 153.552l-8.352-.05-4.133-7.257 4.218-7.208 8.352.049 4.134 7.257z" /> - <path d="M52.632 146.553l-8.352-.049-4.133-7.257 4.218-7.209 8.352.05 4.134 7.257z" /> - <path d="M64.771 139.589l-8.352-.05-4.133-7.257 4.218-7.208 8.352.05 4.134 7.257z" /> - <path d="M64.765 125.702l-8.351-.049-4.134-7.257 4.219-7.209 8.352.05 4.133 7.257z" /> - <path d="M52.696 132.676l-8.352-.049-4.133-7.257 4.218-7.209 8.352.05 4.133 7.257z" /> - </g> - <g transform="matrix(2.8 0 0 2.8 -90 -305)"> - <path d="M64.266 138.661l-7.296-.022-3.628-6.33L57.009 126l7.297.023 3.628 6.33z" fill="#c12766" /> - <path d="M52.127 145.626l-7.296-.023-3.628-6.33 3.667-6.307 7.296.022 3.629 6.33z" fill="#3e489f" /> - <path d="M64.19 152.624l-7.295-.023-3.629-6.33 3.668-6.307 7.296.023 3.629 6.33z" fill="#714099" /> - <path d="M52.191 131.749l-7.296-.023-3.629-6.33 3.668-6.307 7.296.022 3.629 6.33z" fill="#78932c" /> - <path d="M64.26 124.775l-7.296-.023-3.628-6.33 3.668-6.307 7.296.022 3.628 6.33z" fill="#cf242a" /> - <circle cy="146.294" cx="60.564" r="1.6" fill="#fff" /> - <path d="M61.748 117.71l-14.267 8.174M48.514 124.197l.048 16.442M47.525 138.715l14.136 8.397M60.59 117.133l.048 16.442M60.657 131.093l.048 16.442M61.766 131.708l-14.267 8.174M47.564 124.84l14.136 8.398" fill="none" stroke="#fff" stroke-width=".524" /> - <circle cy="139.242" cx="48.334" r="1.6" fill="#fff" /> - <circle cy="132.486" cx="60.901" r="1.6" fill="#fff" /> - <circle cy="125.419" cx="48.563" r="1.6" fill="#fff" /> - <circle cy="118.445" cx="60.632" r="1.6" fill="#fff" /> - </g> -</svg> +<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 128 128"><defs><filter id="a" color-interpolation-filters="sRGB"><feFlood flood-opacity=".498" flood-color="#000" result="flood"/><feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1"/><feGaussianBlur in="composite1" stdDeviation=".2" result="blur"/><feOffset dx="1" dy="1" result="offset"/><feComposite in="SourceGraphic" in2="offset" result="composite2"/></filter></defs><g filter="url(#a)" fill="#fff" transform="matrix(2.8 0 0 2.8 -90 -305)"><path d="M64.697 153.552l-8.352-.05-4.133-7.257 4.218-7.208 8.352.049 4.134 7.257z"/><path d="M52.632 146.553l-8.352-.049-4.133-7.257 4.218-7.209 8.352.05 4.134 7.257z"/><path d="M64.771 139.589l-8.352-.05-4.133-7.257 4.218-7.208 8.352.05 4.134 7.257z"/><path d="M64.765 125.702l-8.351-.049-4.134-7.257 4.219-7.209 8.352.05 4.133 7.257z"/><path d="M52.696 132.676l-8.352-.049-4.133-7.257 4.218-7.209 8.352.05 4.133 7.257z"/></g><g transform="matrix(2.8 0 0 2.8 -90 -305)"><path d="M64.266 138.661l-7.296-.022-3.628-6.33L57.009 126l7.297.023 3.628 6.33z" fill="#c12766"/><path d="M52.127 145.626l-7.296-.023-3.628-6.33 3.667-6.307 7.296.022 3.629 6.33z" fill="#3e489f"/><path d="M64.19 152.624l-7.295-.023-3.629-6.33 3.668-6.307 7.296.023 3.629 6.33z" fill="#714099"/><path d="M52.191 131.749l-7.296-.023-3.629-6.33 3.668-6.307 7.296.022 3.629 6.33z" fill="#78932c"/><path d="M64.26 124.775l-7.296-.023-3.628-6.33 3.668-6.307 7.296.022 3.628 6.33z" fill="#cf242a"/><circle cy="146.294" cx="60.564" r="1.6" fill="#fff"/><path d="M61.748 117.71l-14.267 8.174m1.033-1.687l.048 16.442m-1.037-1.924l14.136 8.397m-1.071-29.979l.048 16.442m.019-2.482l.048 16.442m1.061-15.827l-14.267 8.174m.065-15.042l14.136 8.398" fill="none" stroke="#fff" stroke-width=".524"/><circle cy="139.242" cx="48.334" r="1.6" fill="#fff"/><circle cy="132.486" cx="60.901" r="1.6" fill="#fff"/><circle cy="125.419" cx="48.563" r="1.6" fill="#fff"/><circle cy="118.445" cx="60.632" r="1.6" fill="#fff"/></g></svg> \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/azure.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/azure.svg index f8df12ba05c50..a93c83b4b4ae0 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/azure.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/azure.svg @@ -1,17 +1 @@ -<?xml version="1.0" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" - "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> -<svg version="1.0" xmlns="http://www.w3.org/2000/svg" - width="251.000000pt" height="200.000000pt" viewBox="0 0 251.000000 200.000000" - preserveAspectRatio="xMidYMid meet"> - -<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)" -fill="#000000" stroke="none"> -<path d="M993 1670 l-358 -311 -309 -552 c-169 -304 -310 -558 -313 -564 -4 --10 55 -13 275 -13 l279 0 363 803 c200 441 378 835 397 875 18 39 31 72 29 -72 -2 0 -165 -140 -363 -310z"/> -<path d="M1462 1819 c-5 -13 -76 -212 -157 -441 l-147 -417 300 -378 c192 --241 297 -380 289 -384 -7 -3 -260 -48 -564 -100 l-552 -94 931 -3 c512 -1 -933 0 936 2 2 3 -228 417 -512 922 -450 800 -517 914 -524 893z"/> -</g> -</svg> +<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="334.667" height="266.667" viewBox="0 0 251 200"><path d="M99.3 33L63.5 64.1l-30.9 55.2c-16.9 30.4-31 55.8-31.3 56.4-.4 1 5.5 1.3 27.5 1.3h27.9L93 96.7c20-44.1 37.8-83.5 39.7-87.5 1.8-3.9 3.1-7.2 2.9-7.2-.2 0-16.5 14-36.3 31zM146.2 18.1c-.5 1.3-7.6 21.2-15.7 44.1l-14.7 41.7 30 37.8c19.2 24.1 29.7 38 28.9 38.4-.7.3-26 4.8-56.4 10l-55.2 9.4 93.1.3c51.2.1 93.3 0 93.6-.2.2-.3-22.8-41.7-51.2-92.2-45-80-51.7-91.4-52.4-89.3z"/></svg> \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/cisco.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/cisco.svg new file mode 100644 index 0000000000000..20ebebf19741e --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/cisco.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="216" height="216" fill="#049fd9"><g transform="translate(0 50.059)"><path d="M106.48 76.238c-.282-.077-4.621-1.196-9.232-1.196-8.73 0-13.986 4.714-13.986 11.734 0 6.214 4.397 9.313 9.674 10.98.585.193 1.447.463 2.021.653 2.349.739 4.224 1.837 4.224 3.739 0 2.127-2.167 3.504-6.878 3.504-4.14 0-8.109-1.184-8.945-1.395v8.637c.466.099 5.183 1.025 10.222 1.025 7.248 0 15.539-3.167 15.539-12.595 0-4.573-2.8-8.783-8.947-10.737l-2.613-.832c-1.559-.492-4.342-1.289-4.342-3.574 0-1.805 2.062-3.076 5.859-3.076 3.276 0 7.263 1.101 7.404 1.145zm80.041 18.243c0 5.461-4.183 9.879-9.796 9.879-5.619 0-9.791-4.418-9.791-9.879 0-5.45 4.172-9.87 9.791-9.87 5.613 0 9.796 4.42 9.796 9.87m-9.796-19.427c-11.544 0-19.823 8.707-19.823 19.427 0 10.737 8.279 19.438 19.823 19.438 11.543 0 19.834-8.701 19.834-19.438 0-10.72-8.291-19.427-19.834-19.427M70.561 113.251h-9.472V75.719h9.472"/><path id="a" d="M48.07 76.399c-.89-.264-4.18-1.345-8.636-1.345-11.526 0-19.987 8.218-19.987 19.427 0 12.093 9.34 19.438 19.987 19.438 4.23 0 7.459-1.002 8.636-1.336v-10.075c-.407.226-3.503 1.992-7.957 1.992-6.31 0-10.38-4.441-10.38-10.019 0-5.748 4.246-10.011 10.38-10.011 4.53 0 7.576 1.805 7.957 2.004"/><use transform="translate(98.86)" xlink:href="#a" width="100%" height="100%"/><g id="c"><path id="b" d="M61.061 4.759c0-2.587-2.113-4.685-4.703-4.685-2.589 0-4.702 2.098-4.702 4.685v49.84a4.701 4.701 0 004.702 4.699 4.701 4.701 0 004.703-4.699zM35.232 22.451c0-2.586-2.112-4.687-4.702-4.687s-4.702 2.101-4.702 4.687v22.785a4.701 4.701 0 004.702 4.699 4.701 4.701 0 004.702-4.699zM9.404 35.383c0-2.587-2.112-4.684-4.702-4.684C2.115 30.699 0 32.796 0 35.383v9.853a4.703 4.703 0 009.404 0"/><use transform="matrix(-1 0 0 1 112.717 0)" xlink:href="#b" width="100%" height="100%"/></g><use transform="matrix(-1 0 0 1 216 0)" xlink:href="#c" width="100%" height="100%"/></g></svg> \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/cockroachdb.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/cockroachdb.svg index 72f0958f52824..08a4bdabb380f 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/cockroachdb.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/cockroachdb.svg @@ -1,666 +1 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.1" - id="Layer_1" - x="0px" - y="0px" - viewBox="-12.06 13.62 35.251247 35.398594" - xml:space="preserve" - sodipodi:docname="cockroachdb-logo.svg" - width="35.251247" - height="35.398594" - inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata - id="metadata32"><rdf:RDF><cc:Work - rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs - id="defs30"> - - <defs - id="defs9"> - <filter - height="71.5" - width="56.200001" - y="41.400002" - x="555.70001" - filterUnits="userSpaceOnUse" - id="Adobe_OpacityMaskFilter"> - <feColorMatrix - id="feColorMatrix6" - values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0" - type="matrix" /> - </filter> - </defs> - <mask - id="b_1_" - height="71.5" - width="56.2" - y="41.4" - x="555.7" - maskUnits="userSpaceOnUse"> - <g - style="filter:url(#Adobe_OpacityMaskFilter)" - id="g12" - class="st549"> - <path - style="fill:#ffffff" - inkscape:connector-curvature="0" - d="M 306.3,12.4 H 0.8 V 133.3 H 611.9 V 12.4 Z" - class="st550" - id="a_1_" /> - </g> - </mask> - - - - - -</defs><sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="1920" - inkscape:window-height="1025" - id="namedview28" - showgrid="false" - inkscape:zoom="3.2474375" - inkscape:cx="122.58339" - inkscape:cy="-49.295899" - inkscape:window-x="6" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:current-layer="Layer_1" - fit-margin-top="1" - fit-margin-left="1" - fit-margin-right="1" - fit-margin-bottom="1" /> -<style - type="text/css" - id="style2"> - .st0{fill:#4A8FE1;} - .st1{fill:#FEFEFE;} - .st2{fill:#716F73;} - .st3{fill:#4D8951;} - .st4{fill:#A6D3EA;} - .st5{fill:#3A3868;} - .st6{fill:#717073;} - .st7{fill:#1B995A;} - .st8{fill:#717B83;} - .st9{fill:#2BA3A4;} - .st10{fill:#717074;} - .st11{fill:#717173;} - .st12{fill:#707B83;} - .st13{fill:#67A3D1;} - .st14{fill:#2B5B68;} - .st15{fill:#B9DCF3;} - .st16{fill:#2A4B38;} - .st17{fill:#83939F;} - .st18{fill:#9DCCE8;} - .st19{fill:#85939C;} - .st20{fill:#82929C;} - .st21{fill:#40A6D5;} - .st22{fill:#2B4B39;} - .st23{fill:#28493A;} - .st24{fill:#2EA3A7;} - .st25{fill:#F8FCFC;} - .st26{fill:#568F52;} - .st27{fill:#94C453;} - .st28{fill:#206289;} - .st29{fill:#F1FBFD;} - .st30{fill:#F6FDFE;} - .st31{fill:#F8FCFB;} - .st32{fill:#FAFDFB;} - .st33{fill:#F4FCFD;} - .st34{fill:#65AC62;} - .st35{fill:#F8FAF3;} - .st36{fill:#5EAD5A;} - .st37{fill:#F6FCFA;} - .st38{fill:#85C5B8;} - .st39{fill:#3CA0D5;} - .st40{fill:#727A80;} - .st41{fill:#F6FCFD;} - .st42{fill:#FEFEFA;} - .st43{fill:#459ED2;} - .st44{fill:#5DAE5B;} - .st45{fill:#5E7F47;} - .st46{fill:#FCFCFC;} - .st47{fill:#C1DBED;} - .st48{fill:#FBFDF8;} - .st49{fill:#EBF5F7;} - .st50{fill:#294949;} - .st51{fill:#8FC8E5;} - .st52{fill:#F7FCFC;} - .st53{fill:#FCFEFA;} - .st54{fill:#4E8A56;} - .st55{fill:#64BDE8;} - .st56{fill:#FEFDFA;} - .st57{fill:#FBFDFC;} - .st58{fill:#449DD5;} - .st59{fill:#F6FBF7;} - .st60{fill:#279A5B;} - .st61{fill:#30493D;} - .st62{fill:#F5FBF9;} - .st63{fill:#35583B;} - .st64{fill:#323C62;} - .st65{fill:#F8FBF9;} - .st66{fill:#E7EFF2;} - .st67{fill:#8DC1B6;} - .st68{fill:#FAFCFA;} - .st69{fill:#F1FBF9;} - .st70{fill:#F7FEFE;} - .st71{fill:#FDFEFA;} - .st72{fill:#31A1A1;} - .st73{fill:#F9FEFD;} - .st74{fill:#FAFAFC;} - .st75{fill:#FDFCF8;} - .st76{fill:#2D5C60;} - .st77{fill:#FDFCF9;} - .st78{fill:#F9FDFB;} - .st79{fill:#FBFBFB;} - .st80{fill:#518B58;} - .st81{fill:#5A82AF;} - .st82{fill:#FDFEF8;} - .st83{fill:#244C40;} - .st84{fill:#266495;} - .st85{fill:#42A0D3;} - .st86{fill:#5B80A9;} - .st87{fill:#2E5B63;} - .st88{fill:#FDFBF9;} - .st89{fill:#F8FDFB;} - .st90{fill:#FDFDF7;} - .st91{fill:#F3F9FB;} - .st92{fill:#309F96;} - .st93{fill:#93C367;} - .st94{fill:#3B3C68;} - .st95{fill:#E0F1F9;} - .st96{fill:#E4F6F9;} - .st97{fill:#FEFEF9;} - .st98{fill:#224C38;} - .st99{fill:#498AB2;} - .st100{fill:#FDFDF9;} - .st101{fill:#F9FEFE;} - .st102{fill:#F6FCF2;} - .st103{fill:#FEFCF9;} - .st104{fill:#EEFCFE;} - .st105{fill:#FDFDF8;} - .st106{fill:#FBFDFA;} - .st107{fill:#275962;} - .st108{fill:#FDFEF9;} - .st109{fill:#F2F8F2;} - .st110{fill:#F8FBFB;} - .st111{fill:#8A8A8A;} - .st112{fill:#F7FEFD;} - .st113{fill:#EFF8FB;} - .st114{fill:#FAFCFB;} - .st115{fill:#E5F3F8;} - .st116{fill:#ECF7FA;} - .st117{fill:#FDFDF6;} - .st118{fill:#F5FDFD;} - .st119{fill:#7799BF;} - .st120{fill:#5C8055;} - .st121{fill:#6EAECC;} - .st122{fill:#FEFEF7;} - .st123{fill:#3282B5;} - .st124{fill:#F3FCF5;} - .st125{fill:#F4FAFB;} - .st126{fill:#FEF8FA;} - .st127{fill:#F5FCF9;} - .st128{fill:#3C9392;} - .st129{fill:#EBF6F6;} - .st130{fill:#98BFD6;} - .st131{fill:#FEFDF7;} - .st132{fill:#658796;} - .st133{fill:#EEF7F6;} - .st134{fill:#FAFBF5;} - .st135{fill:#F8FCF9;} - .st136{fill:#FDFDF4;} - .st137{fill:#FDFBF8;} - .st138{fill:#FDFDF5;} - .st139{fill:#EFF9FD;} - .st140{fill:#FDFEF6;} - .st141{fill:#37586D;} - .st142{fill:#FDFEF5;} - .st143{fill:#FDFBF6;} - .st144{fill:#324A49;} - .st145{fill:#F8F8F9;} - .st146{fill:#CDF5CD;} - .st147{fill:#FEFDF3;} - .st148{fill:#ECFBFE;} - .st149{fill:#858585;} - .st150{fill:#C5C5C5;} - .st151{fill:#6C6C6C;} - .st152{fill:#498B4F;} - .st153{fill:#478A4D;} - .st154{fill:#4A884D;} - .st155{fill:#429169;} - .st156{fill:#3CA29D;} - .st157{fill:#518654;} - .st158{fill:#51855F;} - .st159{fill:#289FA0;} - .st160{fill:#448E49;} - .st161{fill:#458B55;} - .st162{fill:#568356;} - .st163{fill:#89C9ED;} - .st164{fill:#246081;} - .st165{fill:#206186;} - .st166{fill:#8EC8E9;} - .st167{fill:#2B5D7D;} - .st168{fill:#215F80;} - .st169{fill:#A0D4EF;} - .st170{fill:#A4D3F2;} - .st171{fill:#245E84;} - .st172{fill:#69BADA;} - .st173{fill:#9AC9D1;} - .st174{fill:#9AC7DD;} - .st175{fill:#3B396C;} - .st176{fill:#3A376D;} - .st177{fill:#3D366C;} - .st178{fill:#6A9BC9;} - .st179{fill:#57B156;} - .st180{fill:#2A9762;} - .st181{fill:#139C59;} - .st182{fill:#169A5E;} - .st183{fill:#139C5D;} - .st184{fill:#129652;} - .st185{fill:#61839B;} - .st186{fill:#637F92;} - .st187{fill:#289193;} - .st188{fill:#288F8D;} - .st189{fill:#34A3A8;} - .st190{fill:#23A7A5;} - .st191{fill:#36A2A8;} - .st192{fill:#32A1A0;} - .st193{fill:#23A7A2;} - .st194{fill:#249A96;} - .st195{fill:#FCFCFD;} - .st196{fill:#FDFDFD;} - .st197{fill:#6B7B8C;} - .st198{fill:#4C84B0;} - .st199{fill:#4F81BA;} - .st200{fill:#5280B6;} - .st201{fill:#6CA1CE;} - .st202{fill:#6CA0CF;} - .st203{fill:#7AA4CC;} - .st204{fill:#71A3D5;} - .st205{fill:#6AA3C9;} - .st206{fill:#6EA5D6;} - .st207{fill:#6C97B9;} - .st208{fill:#4F6684;} - .st209{fill:#2D5D62;} - .st210{fill:#275C5E;} - .st211{fill:#2B5859;} - .st212{fill:#285C5E;} - .st213{fill:#9FCCE5;} - .st214{fill:#BCDBEF;} - .st215{fill:#B5DDF9;} - .st216{fill:#234D37;} - .st217{fill:#2E483A;} - .st218{fill:#8294A2;} - .st219{fill:#256286;} - .st220{fill:#8095A3;} - .st221{fill:#3FA6DA;} - .st222{fill:#62BEEC;} - .st223{fill:#40A7D6;} - .st224{fill:#43A3DF;} - .st225{fill:#274C3B;} - .st226{fill:#4C9FC8;} - .st227{fill:#234D32;} - .st228{fill:#2C473D;} - .st229{fill:#27572D;} - .st230{fill:#2D473F;} - .st231{fill:#22A69E;} - .st232{fill:#22A1A1;} - .st233{fill:#378E8D;} - .st234{fill:#268C8A;} - .st235{fill:#40817E;} - .st236{fill:#528E4D;} - .st237{fill:#94C15C;} - .st238{fill:#518D4F;} - .st239{fill:#4B954A;} - .st240{fill:#95C350;} - .st241{fill:#93C451;} - .st242{fill:#90C147;} - .st243{fill:#90B254;} - .st244{fill:#1A6292;} - .st245{fill:#2F82B5;} - .st246{fill:#3FA7DA;} - .st247{fill:#43A5D7;} - .st248{fill:#51ADD8;} - .st249{fill:#57B0E2;} - .st250{fill:#4C95B8;} - .st251{fill:#7BC2EB;} - .st252{fill:#7BC2E8;} - .st253{fill:#439DCE;} - .st254{fill:#1A66A0;} - .st255{fill:#429FD0;} - .st256{fill:#22669E;} - .st257{fill:#42A1D2;} - .st258{fill:#42A6D7;} - .st259{fill:#4EA6CD;} - .st260{fill:#90B15F;} - .st261{fill:#5EAF59;} - .st262{fill:#1568A3;} - .st263{fill:#44A4D8;} - .st264{fill:#2D5F86;} - .st265{fill:#82C6B7;} - .st266{fill:#93C6B3;} - .st267{fill:#81C7BD;} - .st268{fill:#8CC3BB;} - .st269{fill:#3EA0D1;} - .st270{fill:#429ED2;} - .st271{fill:#449DD7;} - .st272{fill:#3AA1DA;} - .st273{fill:#3D9FD7;} - .st274{fill:#439DD3;} - .st275{fill:#69A46E;} - .st276{fill:#74B273;} - .st277{fill:#58AB52;} - .st278{fill:#325946;} - .st279{fill:#214A49;} - .st280{fill:#244B3F;} - .st281{fill:#3181B4;} - .st282{fill:#1DA3A4;} - .st283{fill:#346E9F;} - .st284{fill:#869197;} - .st285{fill:#53B0EA;} - .st286{fill:#5181C0;} - .st287{fill:#1767A6;} - .st288{fill:#3F9FD5;} - .st289{fill:#399291;} - .st290{fill:#27A2A7;} - .st291{fill:#81969C;} - .st292{fill:#46A4D7;} - .st293{fill:#C7DCB8;} - .st294{fill:#B5B5B5;} - .st295{fill:#4F9A83;} - .st296{fill:#63BDE8;} - .st297{fill:#95C3E7;} - .st298{fill:#1D618B;} - .st299{fill:#1D6286;} - .st300{fill:#3181B5;} - .st301{fill:#249190;} - .st302{fill:#2C9096;} - .st303{fill:#23918E;} - .st304{fill:#2E939A;} - .st305{fill:#469DD6;} - .st306{fill:#40A0D2;} - .st307{fill:#4B82C0;} - .st308{fill:#809398;} - .st309{fill:#69BCE2;} - .st310{fill:#5BBFEB;} - .st311{fill:#5DBEF5;} - .st312{fill:#42A6D8;} - .st313{fill:#39A4DC;} - .st314{fill:#94C356;} - .st315{fill:#95C446;} - .st316{fill:#2B83B5;} - .st317{fill:#2A83B8;} - .st318{fill:#3EA7DE;} - .st319{fill:#37A8DF;} - .st320{fill:#7CC9F7;} - .st321{fill:#1568A7;} - .st322{fill:#3EA7DD;} - .st323{fill:#3AA9DA;} - .st324{fill:#55B0E2;} - .st325{fill:#5FAF60;} - .st326{fill:#16639A;} - .st327{fill:#16689E;} - .st328{fill:#419FCE;} - .st329{fill:#216186;} - .st330{fill:#21607D;} - .st331{fill:#3BA7DD;} - .st332{fill:#6E408A;} - .st333{fill:#3A3A3C;} - .st334{fill:none;} - .st335{fill:#44546A;} - .st336{fill:#E67C0C;} - .st337{fill:#922A50;} - .st338{fill:#E67C0D;} - .st339{fill:#4471CD;} - .st340{fill:#922B50;} - .st341{fill:#45556B;} - .st342{fill:#922B51;} - .st343{fill:#4571CD;} - .st344{fill:#46556B;} - .st345{fill:#45556A;} - .st346{fill:#F0B272;} - .st347{fill:#1D94D2;} - .st348{fill:#3C3F3F;} - .st349{fill:#080809;} - .st350{fill:#048DC9;} - .st351{fill:#7496A2;} - .st352{fill:#9DAAC1;} - .st353{fill:#868689;} - .st354{fill:#052C50;} - .st355{fill:#101214;} - .st356{fill:#191C1E;} - .st357{fill:#242C35;} - .st358{fill:#343C42;} - .st359{fill:#343B44;} - .st360{fill:#3D4A5B;} - .st361{fill:#73AAFE;} - .st362{fill:#4CCAFE;} - .st363{fill:#CFE1FD;} - .st364{fill:#87DCFE;} - .st365{fill:#0090CC;} - .st366{fill:#008FCC;} - .st367{fill:none;stroke:#407DCC;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;} - .st368{fill:#407DCC;} - .st369{fill:#15191E;} - .st370{fill:#10191F;} - .st371{fill:#12161B;} - .st372{fill:#0E181E;} - .st373{fill:#121D24;} - .st374{fill:#3F7BC7;} - .st375{fill:#222932;} - .st376{fill:#22404E;} - .st377{fill:#202831;} - .st378{fill:#010101;} - .st379{fill:#161A1F;} - .st380{fill:#323C47;} - .st381{fill:#303B48;} - .st382{fill:#14181B;} - .st383{fill:#353F4A;} - .st384{fill:#194691;} - .st385{fill:#164C96;} - .st386{fill:#184893;} - .st387{fill:#174A94;} - .st388{fill:#184792;} - .st389{fill:#184892;} - .st390{fill:#0090CD;} - .st391{fill:#018CCA;} - .st392{fill:#FDBB87;} - .st393{fill:#FE9545;} - .st394{fill:#FE8B00;} - .st395{fill:#DC5900;} - .st396{fill:#DE6D45;} - .st397{fill:#292925;} - .st398{fill:#63A1BF;} - .st399{fill:#61A1BF;} - .st400{fill:#8DC53F;} - .st401{fill:#8EC642;} - .st402{fill:#61853A;} - .st403{fill:#424243;} - .st404{fill:#8EC641;} - .st405{fill:#8DC640;} - .st406{fill:#414142;} - .st407{fill:#94C94C;} - .st408{fill:#4C4C4D;} - .st409{fill:#62863C;} - .st410{fill:#8DC540;} - .st411{fill:#577138;} - .st412{fill:#A2B24F;} - .st413{fill:#DCDF72;} - .st414{fill:#5E3108;} - .st415{fill:#074354;} - .st416{fill:#04C594;} - .st417{fill:#95AEB6;} - .st418{fill:#4B4B4B;} - .st419{fill:#5C5B5B;} - .st420{fill:#ABABAB;} - .st421{fill:#474646;} - .st422{fill:#454444;} - .st423{fill:#8C8C8C;} - .st424{fill:#292828;} - .st425{fill:#818181;} - .st426{fill:#C1C1C1;} - .st427{fill:#AAAAAA;} - .st428{fill:#A1A0A0;} - .st429{fill:#7A7879;} - .st430{fill:#C9C9C9;} - .st431{fill:#787878;} - .st432{fill:#474747;} - .st433{fill:#939393;} - .st434{fill:#9D9D9D;} - .st435{fill:#323131;} - .st436{fill:#646464;} - .st437{fill:#5F5F5F;} - .st438{fill:#454545;} - .st439{fill:#565555;} - .st440{fill:#504F4F;} - .st441{fill:#464646;} - .st442{fill:#807F7F;} - .st443{fill:#515050;} - .st444{fill:#444242;} - .st445{fill:#A8A8A8;} - .st446{fill:#B6B6B6;} - .st447{fill:#545454;} - .st448{fill:#6E6E6E;} - .st449{fill:#9C9C9C;} - .st450{fill:#A9A9A9;} - .st451{fill:#949494;} - .st452{fill:#353434;} - .st453{fill:#727272;} - .st454{fill:#585858;} - .st455{fill:#585757;} - .st456{fill:#7E7D7D;} - .st457{fill:#4C4B4B;} - .st458{fill:#3A3A3A;} - .st459{fill:#989898;} - .st460{fill:#484848;} - .st461{fill:#817F80;} - .st462{fill:#7A7A7A;} - .st463{fill:#5B5A5B;} - .st464{fill:#ADADAD;} - .st465{fill:#737373;} - .st466{fill:#2E2D2D;} - .st467{fill:#757575;} - .st468{fill:#5A5959;} - .st469{fill:#717171;} - .st470{fill:#595859;} - .st471{fill:#989797;} - .st472{fill:#393939;} - .st473{fill:#818080;} - .st474{fill:#929292;} - .st475{fill:#3F3F3F;} - .st476{fill:#BEBEBE;} - .st477{fill:#494949;} - .st478{fill:#494848;} - .st479{fill:#696868;} - .st480{fill:#828181;} - .st481{fill:#C8C8C8;} - .st482{fill:#616060;} - .st483{fill:#615F60;} - .st484{fill:#686767;} - .st485{fill:#7F7E7E;} - .st486{fill:#B3B3B3;} - .st487{fill:#A09F9F;} - .st488{fill:#4D4C4C;} - .st489{fill:#E2E2E2;} - .st490{fill:#D8D8D8;} - .st491{fill:#3E3E3E;} - .st492{fill:#A4A4A4;} - .st493{fill:#8B898A;} - .st494{fill:#4C4C4C;} - .st495{fill:#A3A3A3;} - .st496{fill:#333232;} - .st497{fill:#6C6B6B;} - .st498{fill:#7B7A7A;} - .st499{fill:#C0C0C0;} - .st500{fill:#313131;} - .st501{fill:#7C7B7B;} - .st502{fill:#393838;} - .st503{fill:#414141;} - .st504{fill:#676666;} - .st505{fill:#6F6E6E;} - .st506{fill:#414040;} - .st507{fill:#7F7F7F;} - .st508{fill:#4D4D4D;} - .st509{fill:#6A6969;} - .st510{fill:#6B6A6A;} - .st511{fill:#7E7E7E;} - .st512{fill:#787777;} - .st513{fill:#777777;} - .st514{fill:#D5D5D5;} - .st515{fill:#A1A1A1;} - .st516{fill:#848384;} - .st517{fill:#595959;} - .st518{fill:#575757;} - .st519{fill:#ACACAC;} - .st520{fill:#8A8889;} - .st521{fill:#CFCFCF;} - .st522{fill:#CACACA;} - .st523{fill:#8D8D8D;} - .st524{fill:none;stroke:#A3A3A3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;} - .st525{fill:#4D4C4D;} - .st526{fill:#9F9F9F;} - .st527{fill:#8B8A8A;} - .st528{fill:#BBBBBB;} - .st529{fill:#5B5A5A;} - .st530{fill:#C2C2C2;} - .st531{fill:#A6A6A6;} - .st532{fill:#B4B4B4;} - .st533{fill:#C6C6C6;} - .st534{fill:#A3A2A2;} - .st535{fill:#B7B7B7;} - .st536{fill:#828081;} - .st537{fill:#AFAFAF;} - .st538{fill:#D1D1D1;} - .st539{fill:#D3D3D3;} - .st540{fill:#CECECE;} - .st541{fill:#BABABA;} - .st542{fill:#A4A3A3;} - .st543{fill:#E0E0E0;} - .st544{fill:#D7D7D7;} - .st545{fill:#B1B1B1;} - .st546{fill:#A7A7A7;} - .st547{fill:#D2D2D2;} - .st548{fill:#151F34;} - .st549{filter:url(#Adobe_OpacityMaskFilter);} - .st550{fill:#FFFFFF;} - .st551{mask:url(#b_1_);fill:#151F34;} - .st552{fill:#348540;} - .st553{fill:#7DBC42;} -</style> -<g - id="g859" - transform="matrix(1.7707274,0,0,1.7707274,0.85183359,-43.009452)"><path - class="st548" - d="m 4.7276613,37.955902 c -0.680236,0.165912 -1.3770631,0.232276 -2.0738902,0.232276 -0.7134182,0 -1.4102454,-0.08296 -2.07389025,-0.232276 0.54750705,-0.879329 1.26092525,-1.659112 2.07389025,-2.306166 0.812965,0.647054 1.5263832,1.426837 2.0738902,2.306166 z m 6.2548527,-5.076883 c -0.862738,-0.232276 -1.7420675,-0.348414 -2.6213969,-0.331822 -2.0241169,0 -4.0150516,0.59728 -5.707346,1.725476 -1.69229445,-1.128196 -3.666638,-1.742068 -5.707346,-1.725476 -0.9125117,0 -1.7918411,0.116137 -2.6213972,0.331822 -0.3484136,0.08296 -0.6968272,0.199093 -1.0286496,0.331822 -0.016591,0.01659 -0.033182,0.03318 -0.016591,0.04977 0,0 0,0 0,0 0.1493201,0.315231 0.3152313,0.630462 0.4811425,0.929103 0.016591,0.01659 0.033182,0.01659 0.049773,0.01659 1.0120584,-0.365005 2.0738902,-0.547507 3.135722,-0.547507 1.7420678,0 3.3679978,0.481142 4.761652,1.327289 -0.87932942,0.746601 -1.64252103,1.642522 -2.2232103,2.637989 -0.31523132,0.514325 -0.5640982,1.061832 -0.7797827,1.62593 -0.4313692,1.144787 -0.6470538,2.37253 -0.6470538,3.583682 0,2.920038 1.22774303,5.558026 3.1854954,7.432823 0.1493201,0.14932 0.3152313,0.29864 0.4811426,0.431369 0.1327289,0.116138 0.2654579,0.215685 0.3981869,0.315231 0.1659112,0.132729 0.3484136,0.265458 0.5309159,0.381596 0.016591,0.01659 0.033182,0.01659 0.049773,0 C 2.8860461,51.278568 3.0519573,51.145839 3.2344597,51.01311 3.3671886,50.91356 3.4999176,50.814017 3.6326466,50.697879 3.7985578,50.56515 3.964469,50.41583 4.1137891,50.26651 c 1.9577524,-1.874797 3.1689043,-4.512785 3.1689043,-7.432823 0,-1.460019 -0.3152313,-2.903446 -0.9125117,-4.230736 C 6.3038179,38.470227 6.2374534,38.320907 6.171089,38.188178 6.0715422,37.989085 5.9554044,37.789991 5.8392665,37.607489 5.2419861,36.612021 4.4953856,35.716101 3.6160562,34.9695 c 1.4434276,-0.862738 3.0859487,-1.327289 4.761652,-1.327289 1.0618318,0 2.1402548,0.182502 3.1357218,0.547507 0.01659,0 0.03318,0 0.04977,-0.01659 0.182502,-0.29864 0.331822,-0.613871 0.481142,-0.912511 0.01659,-0.01659 0,-0.04977 -0.01659,-0.04977 0,0 0,0 0,0 -0.348414,-0.132729 -0.696827,-0.248866 -1.045241,-0.331822 z" - id="path19" - inkscape:connector-curvature="0" - style="fill:#151f34;stroke-width:0.16591121" /><path - class="st552" - d="m 6.1379067,42.850283 c 0,2.438895 -0.9456939,4.662106 -2.5052594,6.304627 -0.2820491,-0.895921 -0.4147781,-1.825024 -0.4147781,-2.754127 0,-2.438895 0.945694,-4.662105 2.5052595,-6.304626 0.2654579,0.862738 0.414778,1.791841 0.414778,2.754126" - id="path21" - inkscape:connector-curvature="0" - style="fill:#348540;stroke-width:0.16591121" /><path - class="st553" - d="m 2.1062641,46.384192 c 0,0.962285 -0.1493201,1.891388 -0.4147781,2.754127 -1.60933881,-1.708886 -2.5052594,-3.965279 -2.5052594,-6.304627 0,-0.962285 0.1493201,-1.891388 0.43136917,-2.754126 1.59274773,1.708885 2.48866833,3.965278 2.48866833,6.304626" - id="path23" - inkscape:connector-curvature="0" - style="fill:#7dbc42;stroke-width:0.16591121" /></g> -</svg> +<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="Layer_1" x="0" y="0" viewBox="-12.06 13.62 35.251 35.399" xml:space="preserve" width="35.251" height="35.399"><defs id="defs30"><mask id="b_1_" height="71.5" width="56.2" y="41.4" x="555.7" maskUnits="userSpaceOnUse"><g id="g12" filter="url(#Adobe_OpacityMaskFilter)"><path d="M306.3 12.4H.8v120.9h611.1V12.4z" id="a_1_" fill="#fff"/></g></mask><defs id="defs9"><filter height="71.5" width="56.2" y="41.4" x="555.7" filterUnits="userSpaceOnUse" id="Adobe_OpacityMaskFilter"><feColorMatrix id="feColorMatrix6" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0" type="matrix"/></filter></defs></defs><style type="text/css" id="style2"></style><g id="g859" transform="matrix(1.77073 0 0 1.77073 .852 -43.01)" stroke-width=".166"><path d="M4.728 37.956a8.738 8.738 0 01-2.074.232c-.714 0-1.41-.083-2.074-.232a9.157 9.157 0 012.074-2.306 9.157 9.157 0 012.074 2.306zm6.255-5.077a9.394 9.394 0 00-2.622-.332c-2.024 0-4.015.597-5.707 1.726a10.127 10.127 0 00-5.708-1.726c-.912 0-1.791.116-2.621.332-.348.083-.697.2-1.029.332-.016.016-.033.033-.016.05.15.315.315.63.48.929.018.016.034.016.05.016a9.244 9.244 0 013.136-.547c1.742 0 3.368.48 4.762 1.327a10.278 10.278 0 00-2.223 2.638 10.214 10.214 0 00-1.427 5.21 10.27 10.27 0 003.186 7.433c.149.149.315.298.48.43.133.117.266.217.399.316.166.133.348.266.53.382.017.016.034.016.05 0 .183-.116.349-.25.531-.382.133-.1.266-.199.399-.315.166-.133.331-.282.48-.431a10.255 10.255 0 003.17-7.433c0-1.46-.316-2.904-.913-4.231-.066-.133-.133-.282-.199-.415a6.59 6.59 0 00-.332-.58 10.62 10.62 0 00-2.223-2.639 9.284 9.284 0 014.762-1.327c1.062 0 2.14.183 3.135.548.017 0 .034 0 .05-.017.183-.299.332-.614.481-.912.017-.017 0-.05-.016-.05a8.458 8.458 0 00-1.045-.332z" id="path19" fill="#151f34"/><path d="M6.138 42.85a9.124 9.124 0 01-2.505 6.305 9.163 9.163 0 01-.415-2.754c0-2.44.946-4.662 2.505-6.305.266.863.415 1.792.415 2.754" id="path21" fill="#348540"/><path d="M2.106 46.384c0 .962-.15 1.892-.415 2.754a9.2 9.2 0 01-2.505-6.304c0-.963.15-1.892.432-2.754a9.261 9.261 0 012.488 6.304" id="path23" fill="#7dbc42"/></g></svg> \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/consul.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/consul.svg index 28bbadd24c8a6..5ef8feaded689 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/consul.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/consul.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 79.97 113.78"><defs><style>.cls-1{fill:#961d59;}.cls-1,.cls-2{fill-rule:evenodd;}.cls-2,.cls-3{fill:#d62783;}</style></defs><title>Asset 1 \ No newline at end of file + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/coredns.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/coredns.svg index 863a81e0f40e7..e38616a15a11c 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/coredns.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/coredns.svg @@ -1 +1 @@ -CoreDNS_Colour_Icon + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/couchdb.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/couchdb.svg index 2512c2eb2a011..3c3ecb66d8c87 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/couchdb.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/couchdb.svg @@ -1,86 +1 @@ - - - -image/svg+xml \ No newline at end of file + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/envoyproxy.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/envoyproxy.svg index 45c995b19603b..a22132d415c40 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/envoyproxy.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/envoyproxy.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/ibmmq.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/ibmmq.svg index e474d93359beb..396d5a67a20eb 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/ibmmq.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/ibmmq.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/iis.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/iis.svg new file mode 100644 index 0000000000000..99964448d33c5 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/iis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/mssql.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/mssql.svg new file mode 100644 index 0000000000000..738507c5809c0 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/mssql.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/munin.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/munin.svg new file mode 100644 index 0000000000000..b35a39498c9ec --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/munin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/nats.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/nats.svg index f7593c51a4c42..5a1d6e9a52f17 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/nats.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/nats.svg @@ -1,102 +1 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/openmetrics.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/openmetrics.svg index feccb88a3f34b..1a00be6ab30a2 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/openmetrics.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/openmetrics.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/statsd.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/statsd.svg index f4458439fceb4..a745cd71efafe 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/statsd.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/statsd.svg @@ -1,41 +1 @@ - - - - - - - - - - + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/suricata.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/suricata.svg new file mode 100644 index 0000000000000..06e627a7e4ba7 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/suricata.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/system.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/system.svg new file mode 100644 index 0000000000000..0aba96275e24e --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/system.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/traefik.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/traefik.svg index 8ee3448f5626e..10b38261eca5e 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/traefik.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/traefik.svg @@ -1,342 +1 @@ - - - -image/svg+xml \ No newline at end of file + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/ubiquiti.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/ubiquiti.svg new file mode 100644 index 0000000000000..99a911e32863e --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/ubiquiti.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/uwsgi.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/uwsgi.svg new file mode 100644 index 0000000000000..43818ab211ee3 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/uwsgi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/vsphere.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/vsphere.svg new file mode 100644 index 0000000000000..2a04026e46432 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/vsphere.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/zeek.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/zeek.svg new file mode 100644 index 0000000000000..0e346f98c7238 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/zeek.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/zookeeper.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/zookeeper.svg new file mode 100644 index 0000000000000..80db65f670d1d --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/zookeeper.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts b/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts index f3a37e2b7348f..7261b2ba03372 100644 --- a/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts +++ b/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts @@ -21,7 +21,6 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import { npStart } from 'ui/new_platform'; import { SavedObjectLoader } from '../../../../../plugins/saved_objects/public'; -import { start as visualizations } from '../../../visualizations/public/np_ready/public/legacy'; import { createSavedSearchesLoader } from '../../../../../plugins/discover/public'; /** @@ -63,7 +62,7 @@ const services = { savedObjectManagementRegistry.register({ id: 'savedVisualizations', - service: visualizations.savedVisualizationsLoader, + service: npStart.plugins.visualizations.savedVisualizationsLoader, title: 'visualizations', }); diff --git a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts index 7e96d7bde6e13..2ed7e3d43168c 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts @@ -32,7 +32,7 @@ import { Storage } from '../../../../../plugins/kibana_utils/public'; import { EmbeddableStart } from '../../../../../plugins/embeddable/public'; import { SharePluginStart } from '../../../../../plugins/share/public'; import { DataPublicPluginStart, IndexPatternsContract } from '../../../../../plugins/data/public'; -import { VisualizationsStart } from '../../../visualizations/public'; +import { VisualizationsStart } from '../../../../../plugins/visualizations/public'; import { SavedVisualizations } from './np_ready/types'; import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public'; import { KibanaLegacyStart } from '../../../../../plugins/kibana_legacy/public'; @@ -45,7 +45,6 @@ export interface VisualizeKibanaServices { core: CoreStart; data: DataPublicPluginStart; embeddable: EmbeddableStart; - getBasePath: () => string; indexPatterns: IndexPatternsContract; localStorage: Storage; navigation: NavigationStart; diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy.ts index fbbc7ab944daf..4ef2c93689714 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/legacy.ts @@ -19,14 +19,10 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; -import { start as visualizations } from '../../../visualizations/public/np_ready/public/legacy'; import { plugin } from './index'; const instance = plugin({ env: npSetup.plugins.kibanaLegacy.env, } as PluginInitializerContext); instance.setup(npSetup.core, npSetup.plugins); -instance.start(npStart.core, { - ...npStart.plugins, - visualizations, -}); +instance.start(npStart.core, npStart.plugins); diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts index a2e2ba3543104..6a2034d9a62e4 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts @@ -24,11 +24,11 @@ * directly where they are needed. */ -export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; -export { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; -export { wrapInI18nContext } from 'ui/i18n'; export { DashboardConstants } from '../dashboard/np_ready/dashboard_constants'; -export { VisSavedObject, VISUALIZE_EMBEDDABLE_TYPE } from '../../../visualizations/public/'; +export { + VisSavedObject, + VISUALIZE_EMBEDDABLE_TYPE, +} from '../../../../../plugins/visualizations/public/'; export { configureAppAngularModule, migrateLegacyQuery, diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js index c5325ca3108b4..9ccd45dfc1b45 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js @@ -26,14 +26,14 @@ import { EventEmitter } from 'events'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { makeStateful, useVisualizeAppState } from './lib'; +import { makeStateful, useVisualizeAppState, addEmbeddableToDashboardUrl } from './lib'; import { VisualizeConstants } from '../visualize_constants'; import { getEditBreadcrumbs } from '../breadcrumbs'; import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util'; import { unhashUrl, removeQueryParam } from '../../../../../../../plugins/kibana_utils/public'; import { MarkdownSimple, toMountPoint } from '../../../../../../../plugins/kibana_react/public'; -import { addFatalError, kbnBaseUrl } from '../../../../../../../plugins/kibana_legacy/public'; +import { addFatalError } from '../../../../../../../plugins/kibana_legacy/public'; import { SavedObjectSaveModal, showSaveModal, @@ -46,14 +46,7 @@ import { import { initVisEditorDirective } from './visualization_editor'; import { initVisualizationDirective } from './visualization'; -import { - VISUALIZE_EMBEDDABLE_TYPE, - subscribeWithScope, - absoluteToParsedUrl, - KibanaParsedUrl, - migrateLegacyQuery, - DashboardConstants, -} from '../../legacy_imports'; +import { subscribeWithScope, migrateLegacyQuery, DashboardConstants } from '../../legacy_imports'; import { getServices } from '../../kibana_services'; @@ -79,7 +72,6 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState data: { query: queryService }, toastNotifications, chrome, - getBasePath, core: { docLinks, fatalErrors }, savedQueryService, uiSettings, @@ -653,29 +645,14 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState }); if ($scope.isAddToDashMode()) { - const savedVisualizationParsedUrl = new KibanaParsedUrl({ - basePath: getBasePath(), - appId: kbnBaseUrl.slice('/app/'.length), - appPath: `${VisualizeConstants.EDIT_PATH}/${encodeURIComponent(savedVis.id)}`, - }); + const appPath = `${VisualizeConstants.EDIT_PATH}/${encodeURIComponent(savedVis.id)}`; // Manually insert a new url so the back button will open the saved visualization. - history.replace(savedVisualizationParsedUrl.appPath); - setActiveUrl(savedVisualizationParsedUrl.appPath); + history.replace(appPath); + setActiveUrl(appPath); - const lastDashboardAbsoluteUrl = chrome.navLinks.get('kibana:dashboard').url; - const dashboardParsedUrl = absoluteToParsedUrl( - lastDashboardAbsoluteUrl, - getBasePath() - ); - dashboardParsedUrl.addQueryParameter( - DashboardConstants.ADD_EMBEDDABLE_TYPE, - VISUALIZE_EMBEDDABLE_TYPE - ); - dashboardParsedUrl.addQueryParameter( - DashboardConstants.ADD_EMBEDDABLE_ID, - savedVis.id - ); - history.push(dashboardParsedUrl.appPath); + const lastDashboardUrl = chrome.navLinks.get('kibana:dashboard').url; + const dashboardUrl = addEmbeddableToDashboardUrl(lastDashboardUrl, savedVis.id); + history.push(dashboardUrl); } else if (savedVis.id === $route.current.params.id) { chrome.docTitle.change(savedVis.lastSavedTitle); chrome.setBreadcrumbs($injector.invoke(getEditBreadcrumbs)); diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts index fa5b91b00edaf..6e2f759c73f2f 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts @@ -19,3 +19,4 @@ export { useVisualizeAppState } from './visualize_app_state'; export { makeStateful } from './make_stateful'; +export { addEmbeddableToDashboardUrl } from './url_helper'; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.test.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.test.ts new file mode 100644 index 0000000000000..e6974af9af832 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.test.ts @@ -0,0 +1,48 @@ +/* + * 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 { addEmbeddableToDashboardUrl } from './url_helper'; + +jest.mock('../../../legacy_imports', () => ({ + DashboardConstants: { + ADD_EMBEDDABLE_ID: 'addEmbeddableId', + ADD_EMBEDDABLE_TYPE: 'addEmbeddableType', + CREATE_NEW_DASHBOARD_URL: '/dashboard', + }, + VISUALIZE_EMBEDDABLE_TYPE: 'visualization', +})); + +describe('', () => { + it('addEmbeddableToDashboardUrl when dashboard is not saved', () => { + const id = '123eb456cd'; + const url = + "/pep/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!())"; + expect(addEmbeddableToDashboardUrl(url, id)).toEqual( + `/dashboard?_a=%28description%3A%27%27%2Cfilters%3A%21%28%29%29&_g=%28refreshInterval%3A%28pause%3A%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3Anow-15m%2Cto%3Anow%29%29&addEmbeddableId=${id}&addEmbeddableType=visualization` + ); + }); + it('addEmbeddableToDashboardUrl when dashboard is saved', () => { + const id = '123eb456cd'; + const url = + "/pep/app/kibana#/dashboard/9b780cd0-3dd3-11e8-b2b9-5d5dc1715159?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!())"; + expect(addEmbeddableToDashboardUrl(url, id)).toEqual( + `/dashboard/9b780cd0-3dd3-11e8-b2b9-5d5dc1715159?_a=%28description%3A%27%27%2Cfilters%3A%21%28%29%29&_g=%28refreshInterval%3A%28pause%3A%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3Anow-15m%2Cto%3Anow%29%29&addEmbeddableId=${id}&addEmbeddableType=visualization` + ); + }); +}); diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.ts new file mode 100644 index 0000000000000..c7937c856184a --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.ts @@ -0,0 +1,39 @@ +/* + * 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 { parseUrl, stringify } from 'query-string'; +import { DashboardConstants, VISUALIZE_EMBEDDABLE_TYPE } from '../../../legacy_imports'; + +/** * + * Returns relative dashboard URL with added embeddableType and embeddableId query params + * eg. + * input: url: lol/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)), embeddableId: 12345 + * output: /dashboard?addEmbeddableType=visualization&addEmbeddableId=12345&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)) + * @param url dasbhoard absolute url + * @param embeddableId id of the saved visualization + */ +export function addEmbeddableToDashboardUrl(dashboardUrl: string, embeddableId: string) { + const { url, query } = parseUrl(dashboardUrl); + const [, dashboardId] = url.split(DashboardConstants.CREATE_NEW_DASHBOARD_URL); + + query[DashboardConstants.ADD_EMBEDDABLE_TYPE] = VISUALIZE_EMBEDDABLE_TYPE; + query[DashboardConstants.ADD_EMBEDDABLE_ID] = embeddableId; + + return `${DashboardConstants.CREATE_NEW_DASHBOARD_URL}${dashboardId}?${stringify(query)}`; +} diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js index b0b1ae31a02a5..d1bf4411cac2a 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js @@ -204,6 +204,9 @@ export function initVisualizeApp(app, deps) { '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, }, toastNotifications, + onBeforeRedirect() { + deps.setActiveUrl(VisualizeConstants.LANDING_PAGE_PATH); + }, }) ); }, diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js index 6c02afb672e4c..098633d046062 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js @@ -18,19 +18,17 @@ */ import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util'; -import { VisualizeListingTable } from './visualize_listing_table'; +import { withI18nContext } from './visualize_listing_table'; import { VisualizeConstants } from '../visualize_constants'; import { i18n } from '@kbn/i18n'; import { getServices } from '../../kibana_services'; -import { wrapInI18nContext } from '../../legacy_imports'; - import { syncQueryStateWithUrl } from '../../../../../../../plugins/data/public'; -export function initListingDirective(app) { +export function initListingDirective(app, I18nContext) { app.directive('visualizeListingTable', reactDirective => - reactDirective(wrapInI18nContext(VisualizeListingTable)) + reactDirective(withI18nContext(I18nContext)) ); } diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing_table.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing_table.js index b770625cd3d70..932ac8996e97e 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing_table.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing_table.js @@ -230,4 +230,10 @@ VisualizeListingTable.propTypes = { listingLimit: PropTypes.number.isRequired, }; -export { VisualizeListingTable }; +const withI18nContext = I18nContext => props => ( + + + +); + +export { withI18nContext }; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts index 246a031f05769..e376b4f2bbacf 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts @@ -27,9 +27,8 @@ import { import { EmbeddableStart } from 'src/plugins/embeddable/public'; import { PersistedState } from 'src/plugins/visualizations/public'; import { LegacyCoreStart } from 'kibana/public'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; import { VisSavedObject } from '../legacy_imports'; -import { SavedVisState } from '../../../../visualizations/public/np_ready/public/types'; +import { SavedVisState } from '../../../../../../plugins/visualizations/public'; import { SavedSearch } from '../../../../../../plugins/discover/public'; export type PureVisState = SavedVisState; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/visualize_app.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/visualize_app.ts index 1e7ac668697de..a4afac23f4842 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/visualize_app.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/visualize_app.ts @@ -27,5 +27,5 @@ import { initListingDirective } from './listing/visualize_listing'; export function initVisualizeAppDirective(app: IModule, deps: VisualizeKibanaServices) { initEditorDirective(app, deps); - initListingDirective(app); + initListingDirective(app, deps.core.i18n.Context); } diff --git a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts index 9d88152c59aa7..6d32579f5c541 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts @@ -43,7 +43,7 @@ import { KibanaLegacySetup, AngularRenderedAppUpdater, } from '../../../../../plugins/kibana_legacy/public'; -import { VisualizationsStart } from '../../../visualizations/public'; +import { VisualizationsStart } from '../../../../../plugins/visualizations/public'; import { VisualizeConstants } from './np_ready/visualize_constants'; import { setServices, VisualizeKibanaServices } from './kibana_services'; import { @@ -140,7 +140,6 @@ export class VisualizePlugin implements Plugin { chrome: coreStart.chrome, data: dataStart, embeddable, - getBasePath: core.http.basePath.get, indexPatterns: dataStart.indexPatterns, localStorage: new Storage(localStorage), navigation, diff --git a/src/legacy/core_plugins/management/public/legacy.ts b/src/legacy/core_plugins/management/public/legacy.ts index 4481bad79c47d..96d2c74398a0e 100644 --- a/src/legacy/core_plugins/management/public/legacy.ts +++ b/src/legacy/core_plugins/management/public/legacy.ts @@ -29,7 +29,7 @@ * simply delete this shim file. * * We are also calling `setup/start` here and exporting our public contract so that - * other legacy plugins are able to import from '../core_plugins/visualizations/legacy' + * other legacy plugins are able to import from '../core_plugins/management/legacy' * and receive the response value of the `setup/start` contract, mimicking the * data that will eventually be injected by the new platform. */ diff --git a/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js b/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js index 23ca99791e92e..3880f42d52561 100644 --- a/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js +++ b/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js @@ -37,11 +37,13 @@ import afterdatachangePng from './afterdatachange.png'; import afterdatachangeandresizePng from './afterdatachangeandresize.png'; import aftercolorchangePng from './aftercolorchange.png'; import changestartupPng from './changestartup.png'; -import { setup as visualizationsSetup } from '../../../visualizations/public/np_ready/public/legacy'; import { createRegionMapVisualization } from '../region_map_visualization'; import { createRegionMapTypeDefinition } from '../region_map_type'; -import { ExprVis } from '../../../visualizations/public/np_ready/public/expressions/vis'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ExprVis } from '../../../../../plugins/visualizations/public/expressions/vis'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { BaseVisType } from '../../../../../plugins/visualizations/public/vis_types/base_vis_type'; const THRESHOLD = 0.45; const PIXEL_DIFF = 96; @@ -50,6 +52,7 @@ describe('RegionMapsVisualizationTests', function() { let domNode; let RegionMapsVisualization; let vis; + let regionMapVisType; let dependencies; let imageComparator; @@ -84,8 +87,6 @@ describe('RegionMapsVisualizationTests', function() { ], }; - let visRegComplete = false; - beforeEach(ngMock.module('kibana')); let getManifestStub; @@ -105,11 +106,7 @@ describe('RegionMapsVisualizationTests', function() { uiSettings, }; - if (!visRegComplete) { - visRegComplete = true; - visualizationsSetup.createBaseVisualization(createRegionMapTypeDefinition(dependencies)); - } - + regionMapVisType = new BaseVisType(createRegionMapTypeDefinition(dependencies)); RegionMapsVisualization = createRegionMapVisualization(dependencies); ChoroplethLayer.prototype._makeJsonAjaxCall = async function() { @@ -154,7 +151,7 @@ describe('RegionMapsVisualizationTests', function() { imageComparator = new ImageComparator(); vis = new ExprVis({ - type: 'region_map', + type: regionMapVisType, }); vis.params.bucket = { diff --git a/src/legacy/core_plugins/region_map/public/legacy.ts b/src/legacy/core_plugins/region_map/public/legacy.ts index 495e558e29dd7..08615946affa2 100644 --- a/src/legacy/core_plugins/region_map/public/legacy.ts +++ b/src/legacy/core_plugins/region_map/public/legacy.ts @@ -20,7 +20,6 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; -import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; import { RegionMapPluginSetupDependencies, RegionMapsConfig } from './plugin'; import { LegacyDependenciesPlugin } from './shim'; import { plugin } from '.'; @@ -31,7 +30,7 @@ const regionmapsConfig = npSetup.core.injectedMetadata.getInjectedVar( const plugins: Readonly = { expressions: npSetup.plugins.expressions, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, // Temporary solution // It will be removed when all dependent services are migrated to the new platform. diff --git a/src/legacy/core_plugins/region_map/public/plugin.ts b/src/legacy/core_plugins/region_map/public/plugin.ts index 98fb5604c3d65..cae569f8fd26d 100644 --- a/src/legacy/core_plugins/region_map/public/plugin.ts +++ b/src/legacy/core_plugins/region_map/public/plugin.ts @@ -24,7 +24,7 @@ import { IUiSettingsClient, } from '../../../../core/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup } from '../../visualizations/public'; +import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim'; diff --git a/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js b/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js index 3b8a7dfbed313..2c142b19d9096 100644 --- a/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js +++ b/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js @@ -31,11 +31,13 @@ import EMS_TILES from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_ import EMS_STYLE_ROAD_MAP_BRIGHT from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_bright'; import EMS_STYLE_ROAD_MAP_DESATURATED from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_desaturated'; import EMS_STYLE_DARK_MAP from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_dark'; -import { setup as visualizationsSetup } from '../../../visualizations/public/np_ready/public/legacy'; import { createTileMapVisualization } from '../tile_map_visualization'; import { createTileMapTypeDefinition } from '../tile_map_type'; -import { ExprVis } from '../../../visualizations/public/np_ready/public/expressions/vis'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ExprVis } from '../../../../../plugins/visualizations/public/expressions/vis'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { BaseVisType } from '../../../../../plugins/visualizations/public/vis_types/base_vis_type'; function mockRawData() { const stack = [dummyESResponse]; @@ -59,13 +61,13 @@ mockRawData(); const THRESHOLD = 0.45; const PIXEL_DIFF = 64; -let visRegComplete = false; describe('CoordinateMapsVisualizationTest', function() { let domNode; let CoordinateMapsVisualization; let vis; let dependencies; + let visType; let imageComparator; @@ -82,10 +84,7 @@ describe('CoordinateMapsVisualizationTest', function() { $injector, }; - if (!visRegComplete) { - visRegComplete = true; - visualizationsSetup.createBaseVisualization(createTileMapTypeDefinition(dependencies)); - } + visType = new BaseVisType(createTileMapTypeDefinition(dependencies)); CoordinateMapsVisualization = createTileMapVisualization(dependencies); @@ -120,7 +119,7 @@ describe('CoordinateMapsVisualizationTest', function() { imageComparator = new ImageComparator(); vis = new ExprVis({ - type: 'tile_map', + type: visType, }); vis.params = { mapType: 'Scaled Circle Markers', diff --git a/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx b/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx index 204ad5efa9b40..27127b781cd4d 100644 --- a/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx +++ b/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { TmsLayer } from 'ui/vis/map/service_settings'; -import { Vis } from '../../../visualizations/public'; +import { Vis } from '../../../../../plugins/visualizations/public'; import { RegionMapVisParams } from '../../../region_map/public/types'; import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public'; import { WmsInternalOptions } from './wms_internal_options'; diff --git a/src/legacy/core_plugins/tile_map/public/legacy.ts b/src/legacy/core_plugins/tile_map/public/legacy.ts index 74be8482bfd30..7b1f916076f61 100644 --- a/src/legacy/core_plugins/tile_map/public/legacy.ts +++ b/src/legacy/core_plugins/tile_map/public/legacy.ts @@ -20,14 +20,13 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; -import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; import { TileMapPluginSetupDependencies } from './plugin'; import { LegacyDependenciesPlugin } from './shim'; import { plugin } from '.'; const plugins: Readonly = { expressions: npSetup.plugins.expressions, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, // Temporary solution // It will be removed when all dependent services are migrated to the new platform. diff --git a/src/legacy/core_plugins/tile_map/public/plugin.ts b/src/legacy/core_plugins/tile_map/public/plugin.ts index a12c2753cc525..f2addbe3ab872 100644 --- a/src/legacy/core_plugins/tile_map/public/plugin.ts +++ b/src/legacy/core_plugins/tile_map/public/plugin.ts @@ -24,7 +24,7 @@ import { IUiSettingsClient, } from '../../../../core/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup } from '../../visualizations/public'; +import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim'; diff --git a/src/legacy/core_plugins/timelion/public/app.js b/src/legacy/core_plugins/timelion/public/app.js index a50f8a2cd3e8d..c15318d29e761 100644 --- a/src/legacy/core_plugins/timelion/public/app.js +++ b/src/legacy/core_plugins/timelion/public/app.js @@ -41,7 +41,6 @@ import './directives/saved_object_save_as_checkbox'; import './services/saved_sheet_register'; import rootTemplate from 'plugins/timelion/index.html'; -import { start as visualizations } from '../../visualizations/public/np_ready/public/legacy'; import { loadKbnTopNavDirectives } from '../../../../plugins/kibana_legacy/public'; loadKbnTopNavDirectives(npStart.plugins.navigation.ui); @@ -125,7 +124,7 @@ app.controller('timelion', function( timefilter.enableAutoRefreshSelector(); timefilter.enableTimeRangeSelector(); - const savedVisualizations = visualizations.savedVisualizationsLoader; + const savedVisualizations = npStart.plugins.visualizations.savedVisualizationsLoader; const timezone = getTimezone(config); const defaultExpression = '.es(*)'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx index 2a45273207623..83fbf70c9099e 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx @@ -35,6 +35,8 @@ import { AGGS_ACTION_KEYS, AggsAction } from './agg_group_state'; import { RowsOrColumnsControl } from './controls/rows_or_columns'; import { RadiusRatioOptionControl } from './controls/radius_ratio_option'; import { getSchemaByName } from '../schemas'; +import { TimeRange } from '../../../../../plugins/data/public'; +import { buildAggDescription } from './agg_params_helper'; export interface DefaultEditorAggProps extends DefaultEditorAggCommonProps { agg: IAggConfig; @@ -46,6 +48,7 @@ export interface DefaultEditorAggProps extends DefaultEditorAggCommonProps { isLastBucket: boolean; isRemovable: boolean; setAggsState: React.Dispatch; + timeRange?: TimeRange; } function DefaultEditorAgg({ @@ -69,6 +72,7 @@ function DefaultEditorAgg({ removeAgg, setAggsState, schemas, + timeRange, }: DefaultEditorAggProps) { const [isEditorOpen, setIsEditorOpen] = useState((agg as any).brandNew); const [validState, setValidState] = useState(true); @@ -103,18 +107,15 @@ function DefaultEditorAgg({ } } - // A description of the aggregation, for displaying in the collapsed agg header - let aggDescription = ''; + const [aggDescription, setAggDescription] = useState(buildAggDescription(agg)); - if (agg.type && agg.type.makeLabel) { - try { - aggDescription = agg.type.makeLabel(agg); - } catch (e) { - // Date Histogram's `makeLabel` implementation invokes 'write' method for each param, including interval's 'write', - // which throws an error when interval is undefined. - aggDescription = ''; + // This useEffect is required to update the timeRange value and initiate rerender to keep labels up to date (Issue #57822). + useEffect(() => { + if (timeRange && aggName === 'date_histogram') { + agg.aggConfigs.setTimeRange(timeRange); } - } + setAggDescription(buildAggDescription(agg)); + }, [agg, aggName, timeRange]); useEffect(() => { if (isLastBucketAgg && ['date_histogram', 'histogram'].includes(aggName)) { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_common_props.ts b/src/legacy/core_plugins/vis_default_editor/public/components/agg_common_props.ts index 3aae10879138a..0c130a96230b4 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_common_props.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_common_props.ts @@ -17,7 +17,7 @@ * under the License. */ -import { VisParams } from 'src/legacy/core_plugins/visualizations/public'; +import { VisParams } from 'src/plugins/visualizations/public'; import { IAggType, IAggConfig, IAggGroupNames } from 'src/plugins/data/public'; import { Schema } from '../schemas'; import { EditorVisState } from './sidebar/state/reducers'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx index 551c221cdefb9..54793f793323d 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx @@ -42,6 +42,7 @@ import { } from './agg_group_helper'; import { aggGroupReducer, initAggsState, AGGS_ACTION_KEYS } from './agg_group_state'; import { Schema, getSchemasByGroup } from '../schemas'; +import { TimeRange } from '../../../../../plugins/data/public'; export interface DefaultEditorAggGroupProps extends DefaultEditorAggCommonProps { schemas: Schema[]; @@ -49,6 +50,7 @@ export interface DefaultEditorAggGroupProps extends DefaultEditorAggCommonProps reorderAggs: ReorderAggs; setValidity(modelName: string, value: boolean): void; setTouched(isTouched: boolean): void; + timeRange?: TimeRange; } function DefaultEditorAggGroup({ @@ -67,6 +69,7 @@ function DefaultEditorAggGroup({ reorderAggs, setTouched, setValidity, + timeRange, }: DefaultEditorAggGroupProps) { const groupNameLabel = (search.aggs.aggGroupNamesMap() as any)[groupName]; // e.g. buckets can have no aggs @@ -185,6 +188,7 @@ function DefaultEditorAggGroup({ removeAgg={removeAgg} setAggsState={setAggsState} schemas={schemas} + timeRange={timeRange} /> )} diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts index 10590e1a59f4a..073cb7d5ac66c 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts @@ -174,4 +174,17 @@ function isInvalidParamsTouched( return invalidParams.every(param => param.touched); } -export { getAggParamsToRender, getAggTypeOptions, isInvalidParamsTouched }; +function buildAggDescription(agg: IAggConfig) { + let description = ''; + if (agg.type && agg.type.makeLabel) { + try { + description = agg.type.makeLabel(agg); + } catch (e) { + // Date Histogram's `makeLabel` implementation invokes 'write' method for each param, including interval's 'write', + // which throws an error when interval is undefined. + } + } + return description; +} + +export { getAggParamsToRender, getAggTypeOptions, isInvalidParamsTouched, buildAggDescription }; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_control_props.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_control_props.tsx index 98540d3414f2d..329704ca106db 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_control_props.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_control_props.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { VisParams } from 'src/legacy/core_plugins/visualizations/public'; +import { VisParams } from 'src/plugins/visualizations/public'; import { IAggConfig } from 'src/plugins/data/public'; import { DefaultEditorAggCommonProps } from '../agg_common_props'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx index 9aacd6a10262f..971a62faf7d7c 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx @@ -61,7 +61,7 @@ function validateInterval( timeBase?: string ) { if (definedOption) { - return { isValid: true }; + return { isValid: true, interval: agg.buckets?.getInterval() }; } if (!value) { @@ -131,7 +131,7 @@ function TimeIntervalParamEditor({ const scaledHelpText = interval && interval.scaled ? ( - + @@ -128,6 +131,7 @@ function DefaultEditorDataTab({ diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx index 04c931f593e5a..071e10682e22c 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { keyCodes, EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { EventEmitter } from 'events'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from 'src/plugins/visualizations/public'; import { DefaultEditorNavBar, OptionTab } from './navbar'; import { DefaultEditorControls } from './controls'; import { setStateParamValue, useEditorReducer, useEditorFormState, discardChanges } from './state'; @@ -33,6 +33,7 @@ import { PersistedState } from '../../../../../../plugins/visualizations/public' import { SavedSearch } from '../../../../../../plugins/discover/public'; import { AggGroupNames } from '../../../../../../plugins/data/public'; import { getSchemasByGroup } from '../../schemas'; +import { TimeRange } from '../../../../../../plugins/data/public'; interface DefaultEditorSideBarProps { isCollapsed: boolean; @@ -43,6 +44,7 @@ interface DefaultEditorSideBarProps { isLinkedSearch: boolean; eventEmitter: EventEmitter; savedSearch?: SavedSearch; + timeRange: TimeRange; } function DefaultEditorSideBar({ @@ -54,6 +56,7 @@ function DefaultEditorSideBar({ isLinkedSearch, eventEmitter, savedSearch, + timeRange, }: DefaultEditorSideBarProps) { const [selectedTab, setSelectedTab] = useState(optionTabs[0].name); const [isDirty, setDirty] = useState(false); @@ -214,6 +217,7 @@ function DefaultEditorSideBar({ ); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx index 575ad5ae2a95c..fb63a598a4fae 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx @@ -35,7 +35,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from '../../../../../../plugins/visualizations/public'; import { SavedSearch } from '../../../../../../plugins/discover/public'; interface LinkedSearchProps { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/actions.ts b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/actions.ts index e3577218b7e25..16c3632bed697 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/actions.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/actions.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Vis, VisParams } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis, VisParams } from 'src/plugins/visualizations/public'; import { IAggConfig } from 'src/plugins/data/public'; import { EditorStateActionTypes } from './constants'; import { Schema } from '../../../schemas'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/index.ts b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/index.ts index 11cbc3f93e9d3..d39d6d07b32d2 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/index.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/index.ts @@ -20,7 +20,7 @@ import { useReducer, useCallback } from 'react'; import { EventEmitter } from 'events'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from 'src/plugins/visualizations/public'; import { createEditorStateReducer, initEditorState, EditorVisState } from './reducers'; import { EditorStateActionTypes } from './constants'; import { EditorAction } from './actions'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/reducers.ts b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/reducers.ts index 6e5bec7c69c90..b9f89cebd8bf3 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/reducers.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/reducers.ts @@ -19,7 +19,7 @@ import { cloneDeep } from 'lodash'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from 'src/plugins/visualizations/public'; import { AggGroupNames, DataPublicPluginStart } from '../../../../../../../plugins/data/public'; import { EditorStateActionTypes } from './constants'; import { getEnabledMetricAggsCount } from '../../agg_group_helper'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx b/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx index b504dfd6a55e9..899b9c1b5fd6e 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx @@ -92,6 +92,7 @@ function DefaultEditor({ uiState={uiState} isLinkedSearch={linked} savedSearch={savedSearch} + timeRange={timeRange} eventEmitter={eventEmitter} /> diff --git a/src/legacy/core_plugins/vis_default_editor/public/default_editor_controller.tsx b/src/legacy/core_plugins/vis_default_editor/public/default_editor_controller.tsx index 13fcabd799959..58e67b5064da5 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/default_editor_controller.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/default_editor_controller.tsx @@ -20,23 +20,21 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { i18n } from '@kbn/i18n'; -import { I18nProvider } from '@kbn/i18n/react'; import { EventEmitter } from 'events'; import { EditorRenderProps } from 'src/legacy/core_plugins/kibana/public/visualize/np_ready/types'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public/'; +import { Vis, VisualizeEmbeddableContract } from '../../../../plugins/visualizations/public'; import { Storage } from '../../../../plugins/kibana_utils/public'; import { KibanaContextProvider } from '../../../../plugins/kibana_react/public'; import { DefaultEditor } from './default_editor'; import { DefaultEditorDataTab, OptionTab } from './components/sidebar'; -import { VisualizeEmbeddable } from '../../visualizations/public/np_ready/public/embeddable'; const localStorage = new Storage(window.localStorage); export interface DefaultEditorControllerState { vis: Vis; eventEmitter: EventEmitter; - embeddableHandler: VisualizeEmbeddable; + embeddableHandler: VisualizeEmbeddableContract; optionTabs: OptionTab[]; } @@ -84,7 +82,7 @@ class DefaultEditorController { render({ data, core, ...props }: EditorRenderProps) { render( - + - , + , this.el ); } diff --git a/src/legacy/core_plugins/vis_default_editor/public/vis_options_props.tsx b/src/legacy/core_plugins/vis_default_editor/public/vis_options_props.tsx index 3239e871a2465..a9b20ec0f00da 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/vis_options_props.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/vis_options_props.tsx @@ -17,9 +17,8 @@ * under the License. */ -import { PersistedState } from 'src/plugins/visualizations/public'; +import { Vis, PersistedState } from 'src/plugins/visualizations/public'; import { IAggConfigs } from 'src/plugins/data/public'; -import { Vis } from '../../visualizations/public'; export interface VisOptionsProps { aggs: IAggConfigs; diff --git a/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts b/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts index d4a5290df865c..1cfc583f6e005 100644 --- a/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts @@ -19,14 +19,12 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; - -import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; import { MarkdownPluginSetupDependencies } from './plugin'; import { plugin } from '.'; const plugins: Readonly = { expressions: npSetup.plugins.expressions, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, }; const pluginInstance = plugin({} as PluginInitializerContext); diff --git a/src/legacy/core_plugins/vis_type_markdown/public/plugin.ts b/src/legacy/core_plugins/vis_type_markdown/public/plugin.ts index 71d6c1c69ef2d..0445d270c9330 100644 --- a/src/legacy/core_plugins/vis_type_markdown/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_markdown/public/plugin.ts @@ -19,7 +19,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup } from '../../visualizations/public'; +import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; import { markdownVisDefinition } from './markdown_vis'; import { createMarkdownVisFn } from './markdown_fn'; diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.test.tsx b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.test.tsx index 00e8df2f0f936..2bd423656b0f0 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.test.tsx +++ b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.test.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { MetricVisComponent, MetricVisComponentProps } from './metric_vis_component'; -import { ExprVis } from '../../../visualizations/public/np_ready/public/expressions/vis'; +import { ExprVis } from '../../../../../plugins/visualizations/public'; jest.mock('../services', () => ({ getFormatService: () => ({ diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.tsx b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.tsx index 3fca1df92eacb..de2cc66a99c79 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.tsx +++ b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.tsx @@ -27,8 +27,7 @@ import { KibanaDatatable } from '../../../../../plugins/expressions/public'; import { getHeatmapColors } from '../../../../../plugins/charts/public'; import { VisParams, MetricVisMetric } from '../types'; import { getFormatService } from '../services'; -import { SchemaConfig } from '../../../visualizations/public'; -import { ExprVis } from '../../../visualizations/public/np_ready/public/expressions/vis'; +import { SchemaConfig, ExprVis } from '../../../../../plugins/visualizations/public'; export interface MetricVisComponentProps { visParams: VisParams; diff --git a/src/legacy/core_plugins/vis_type_metric/public/legacy.ts b/src/legacy/core_plugins/vis_type_metric/public/legacy.ts index 5fc2e48609d4b..ba883601e5d65 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/legacy.ts @@ -19,14 +19,12 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; - -import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; import { MetricVisPluginSetupDependencies } from './plugin'; import { plugin } from '.'; const plugins: Readonly = { expressions: npSetup.plugins.expressions, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, charts: npSetup.plugins.charts, }; diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts index 2a5478f23e850..459da47556307 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts @@ -17,112 +17,19 @@ * under the License. */ -import $ from 'jquery'; - -// TODO This is an integration test and thus requires a running platform. When moving to the new platform, -// this test has to be migrated to a real unit test. -// @ts-ignore -import getStubIndexPattern from 'fixtures/stubbed_logstash_index_pattern'; - -import { Vis } from '../../visualizations/public'; -import { - setup as visualizationsSetup, - start as visualizationsStart, -} from '../../visualizations/public/np_ready/public/legacy'; import { createMetricVisTypeDefinition } from './metric_vis_type'; +import { MetricVisComponent } from './components/metric_vis_component'; jest.mock('ui/new_platform'); -jest.mock('./services', () => ({ - getFormatService: () => ({ - deserialize: () => { - return { - convert: (x: unknown) => `ip[${x}]`, - }; - }, - }), -})); - jest.mock('../../vis_default_editor/public', () => ({ Schemas: class {}, })); describe('metric_vis - createMetricVisTypeDefinition', () => { - let vis: Vis; - - beforeAll(() => { - visualizationsSetup.createReactVisualization(createMetricVisTypeDefinition()); - }); - - const setup = () => { - const stubIndexPattern = getStubIndexPattern(); - - stubIndexPattern.stubSetFieldFormat('ip', 'url', { - urlTemplate: 'http://ip.info?address={{value}}', - labelTemplate: 'ip[{{value}}]', - }); - - const searchSource = { - getField: (name: string) => { - if (name === 'index') { - return stubIndexPattern; - } - }, - }; - - // TODO: remove when Vis is converted to typescript. Only importing Vis as type - // @ts-ignore - vis = visualizationsStart.createVis('metric', { - type: 'metric', - data: { - searchSource, - aggs: [{ id: '1', type: 'top_hits', schema: 'metric', params: { field: 'ip' } }], - }, - }); - - vis.params.dimensions = { - metrics: [ - { - accessor: 0, - format: { - id: 'url', - params: { - urlTemplate: 'http://ip.info?address={{value}}', - labelTemplate: 'ip[{{value}}]', - }, - }, - }, - ], - }; - - const el = document.createElement('div'); - const metricVisType = visualizationsStart.get('metric'); - const Controller = metricVisType.visualization; - const controller = new Controller(el, vis); - const render = (esResponse: any) => { - controller.render(esResponse, vis.params); - }; - - return { el, render }; - }; - - it('renders html value from field formatter', () => { - const { el, render } = setup(); - - const ip = '235.195.237.208'; - render({ - columns: [{ id: 'col-0', name: 'ip' }], - rows: [{ 'col-0': ip }], - }); - - const links = $(el) - .find('a[href]') - .filter(function() { - // @ts-ignore - return this.href.includes('ip.info'); - }); + it('has metric vis component set', () => { + const def = createMetricVisTypeDefinition(); - expect(links.length).toBe(1); - expect(links.text()).toBe(`ip[${ip}]`); + expect(def.visConfig.component).toBe(MetricVisComponent); }); }); diff --git a/src/legacy/core_plugins/vis_type_metric/public/plugin.ts b/src/legacy/core_plugins/vis_type_metric/public/plugin.ts index 1c3e1568f4de2..cb65d5cafbdd2 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/plugin.ts @@ -19,7 +19,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup } from '../../visualizations/public'; +import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; import { createMetricVisFn } from './metric_vis_fn'; import { createMetricVisTypeDefinition } from './metric_vis_type'; diff --git a/src/legacy/core_plugins/vis_type_metric/public/types.ts b/src/legacy/core_plugins/vis_type_metric/public/types.ts index 298eebf23027d..cae18dd8a2ab1 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/types.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/types.ts @@ -18,7 +18,7 @@ */ import { Range } from '../../../../plugins/expressions/public'; -import { SchemaConfig } from '../../visualizations/public'; +import { SchemaConfig } from '../../../../plugins/visualizations/public'; import { ColorModes, Labels, Style } from '../../vis_type_vislib/public'; import { ColorSchemas } from '../../../../plugins/charts/public'; diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js index 211b79e915038..a23407a599ae2 100644 --- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js +++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js @@ -21,105 +21,17 @@ import $ from 'jquery'; import moment from 'moment'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; -import { - metricOnly, - threeTermBuckets, - oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative, -} from 'fixtures/fake_hierarchical_data'; import sinon from 'sinon'; import { npStart } from '../../legacy_imports'; -import { search } from '../../../../../../plugins/data/public'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import { round } from 'lodash'; -import { tableVisTypeDefinition } from '../../table_vis_type'; -import { - setup as visualizationsSetup, - start as visualizationsStart, -} from '../../../../visualizations/public/np_ready/public/legacy'; import { getAngularModule } from '../../get_inner_angular'; import { initTableVisLegacyModule } from '../../table_vis_legacy_module'; -import { tableVisResponseHandler } from '../../table_vis_response_handler'; - -const { tabifyAggResponse } = search; +import { tabifiedData } from './tabified_data'; describe('Table Vis - AggTable Directive', function() { let $rootScope; let $compile; - let indexPattern; let settings; - const tabifiedData = {}; - - const init = () => { - const searchSource = { - getField: name => { - if (name === 'index') { - return indexPattern; - } - }, - }; - const vis1 = visualizationsStart.createVis('table', { - type: 'table', - data: { searchSource, aggs: [] }, - }); - tabifiedData.metricOnly = tabifyAggResponse(vis1.data.aggs, metricOnly); - - const vis2 = visualizationsStart.createVis('table', { - type: 'table', - params: { - showMetricsAtAllLevels: true, - }, - data: { - aggs: [ - { type: 'avg', schema: 'metric', params: { field: 'bytes' } }, - { type: 'terms', schema: 'bucket', params: { field: 'extension' } }, - { type: 'terms', schema: 'bucket', params: { field: 'geo.src' } }, - { type: 'terms', schema: 'bucket', params: { field: 'machine.os' } }, - ], - searchSource, - }, - }); - vis2.data.aggs.aggs.forEach(function(agg, i) { - agg.id = 'agg_' + (i + 1); - }); - tabifiedData.threeTermBuckets = tabifyAggResponse(vis2.data.aggs, threeTermBuckets, { - metricsAtAllLevels: true, - }); - - const vis3 = visualizationsStart.createVis('table', { - type: 'table', - data: { - aggs: [ - { type: 'avg', schema: 'metric', params: { field: 'bytes' } }, - { type: 'min', schema: 'metric', params: { field: '@timestamp' } }, - { type: 'terms', schema: 'bucket', params: { field: 'extension' } }, - { - type: 'date_histogram', - schema: 'bucket', - params: { field: '@timestamp', interval: 'd' }, - }, - { - type: 'derivative', - schema: 'metric', - params: { metricAgg: 'custom', customMetric: { id: '5-orderAgg', type: 'count' } }, - }, - { - type: 'top_hits', - schema: 'metric', - params: { field: 'bytes', aggregate: { val: 'min' }, size: 1 }, - }, - ], - searchSource, - }, - }); - vis3.data.aggs.aggs.forEach(function(agg, i) { - agg.id = 'agg_' + (i + 1); - }); - - tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative = tabifyAggResponse( - vis3.data.aggs, - oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative - ); - }; const initLocalAngular = () => { const tableVisModule = getAngularModule('kibana/table_vis', npStart.core); @@ -128,20 +40,13 @@ describe('Table Vis - AggTable Directive', function() { beforeEach(initLocalAngular); - ngMock.inject(function() { - visualizationsSetup.createBaseVisualization(tableVisTypeDefinition); - }); - beforeEach(ngMock.module('kibana/table_vis')); beforeEach( - ngMock.inject(function($injector, Private, config) { - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); + ngMock.inject(function($injector, config) { settings = config; $rootScope = $injector.get('$rootScope'); $compile = $injector.get('$compile'); - - init(); }) ); @@ -158,7 +63,7 @@ describe('Table Vis - AggTable Directive', function() { metrics: [{ accessor: 0, format: { id: 'number' }, params: {} }], buckets: [], }; - $scope.table = tableVisResponseHandler(tabifiedData.metricOnly, $scope.dimensions).tables[0]; + $scope.table = tabifiedData.metricOnly.tables[0]; const $el = $compile('')( $scope @@ -194,10 +99,7 @@ describe('Table Vis - AggTable Directive', function() { { accessor: 5, params: {} }, ], }; - $scope.table = tableVisResponseHandler( - tabifiedData.threeTermBuckets, - $scope.dimensions - ).tables[0]; + $scope.table = tabifiedData.threeTermBuckets.tables[0]; const $el = $(''); $compile($el)($scope); $scope.$digest(); @@ -261,11 +163,8 @@ describe('Table Vis - AggTable Directive', function() { { accessor: 5, format: { id: 'number' } }, ], }; - const response = tableVisResponseHandler( - tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative, - $scope.dimensions - ); - $scope.table = response.tables[0]; + $scope.table = + tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative.tables[0]; $scope.showTotal = true; $scope.totalFunc = totalFunc; const $el = $(`')( $scope @@ -419,10 +315,7 @@ describe('Table Vis - AggTable Directive', function() { { accessor: 5, params: {} }, ], }; - $scope.table = tableVisResponseHandler( - tabifiedData.threeTermBuckets, - $scope.dimensions - ).tables[0]; + $scope.table = tabifiedData.threeTermBuckets.tables[0]; const $el = $compile('')( $scope @@ -481,11 +374,8 @@ describe('Table Vis - AggTable Directive', function() { { accessor: 5, format: { id: 'number' } }, ], }; - const response = tableVisResponseHandler( - tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative, - $scope.dimensions - ); - $scope.table = response.tables[0]; + $scope.table = + tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative.tables[0]; $scope.percentageCol = 'Average bytes'; const $el = $(` { - const searchSource = { - getField: name => { - if (name === 'index') { - return indexPattern; - } - }, - }; - const vis1 = visualizationsStart.createVis('table', { - type: 'table', - data: { searchSource, aggs: [] }, - }); - tabifiedData.metricOnly = tabifyAggResponse(vis1.data.aggs, metricOnly); - - const vis2 = visualizationsStart.createVis('pie', { - type: 'pie', - data: { - aggs: [ - { type: 'avg', schema: 'metric', params: { field: 'bytes' } }, - { type: 'terms', schema: 'split', params: { field: 'extension' } }, - { type: 'terms', schema: 'segment', params: { field: 'geo.src' } }, - { type: 'terms', schema: 'segment', params: { field: 'machine.os' } }, - ], - searchSource, - }, - }); - vis2.data.aggs.aggs.forEach(function(agg, i) { - agg.id = 'agg_' + (i + 1); - }); - tabifiedData.threeTermBuckets = tabifyAggResponse(vis2.data.aggs, threeTermBuckets); - }; const initLocalAngular = () => { const tableVisModule = getAngularModule('kibana/table_vis', npStart.core); @@ -78,23 +38,9 @@ describe('Table Vis - AggTableGroup Directive', function() { beforeEach(ngMock.module('kibana/table_vis')); beforeEach( - ngMock.inject(function($injector, Private) { - // this is provided in table_vis_controller.js - // tech debt that will be resolved through further deangularization and moving tests to jest - /* - legacyDependencies = { - // eslint-disable-next-line new-cap - createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization, - }; - - visualizationsSetup.types.registerVisualization(() => createTableVisTypeDefinition(legacyDependencies)); - */ - - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); + ngMock.inject(function($injector) { $rootScope = $injector.get('$rootScope'); $compile = $injector.get('$compile'); - - init(); }) ); @@ -111,7 +57,7 @@ describe('Table Vis - AggTableGroup Directive', function() { metrics: [{ accessor: 0, format: { id: 'number' }, params: {} }], buckets: [], }; - $scope.group = tableVisResponseHandler(tabifiedData.metricOnly, $scope.dimensions); + $scope.group = tabifiedData.metricOnly; $scope.sort = { columnIndex: null, direction: null, @@ -156,10 +102,7 @@ describe('Table Vis - AggTableGroup Directive', function() { { accessor: 5, params: {} }, ], }; - const group = ($scope.group = tableVisResponseHandler( - tabifiedData.threeTermBuckets, - $scope.dimensions - )); + const group = ($scope.group = tabifiedData.threeTermBucketsWithSplit); const $el = $( '' ); diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/tabified_data.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/tabified_data.js new file mode 100644 index 0000000000000..857b0ea8662cd --- /dev/null +++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/tabified_data.js @@ -0,0 +1,795 @@ +/* + * 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 const tabifiedData = { + metricOnly: { + tables: [ + { + columns: [ + { + id: 'col-0-1', + name: 'Count', + }, + ], + rows: [ + { + 'col-0-1': 1000, + }, + ], + }, + ], + }, + threeTermBuckets: { + tables: [ + { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'machine.os: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + ], + }, + threeTermBucketsWithSplit: { + tables: [ + { + title: 'png: extension: Descending', + name: 'extension: Descending', + key: 'png', + column: 0, + row: 0, + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-2-agg_4', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'IT', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'IT', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'linux', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'MX', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'MX', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'linux', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'CN', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'CN', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'FR', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'FR', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + ], + }, + tables: [ + { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-2-agg_4', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'IT', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'IT', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'linux', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + ], + }, + ], + }, + { + title: 'css: extension: Descending', + name: 'extension: Descending', + key: 'css', + column: 0, + row: 4, + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-2-agg_4', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'IT', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'IT', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'linux', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'MX', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'MX', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'linux', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'CN', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'CN', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'FR', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'FR', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + ], + }, + tables: [ + { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-2-agg_4', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'MX', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'MX', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'linux', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + ], + }, + ], + }, + { + title: 'html: extension: Descending', + name: 'extension: Descending', + key: 'html', + column: 0, + row: 8, + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-2-agg_4', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'IT', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'IT', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'linux', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'MX', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'MX', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'linux', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-1-agg_3': 'US', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'CN', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'CN', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'FR', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'FR', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + ], + }, + tables: [ + { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-2-agg_4', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'CN', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'CN', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'FR', + 'col-2-agg_4': 'win', + 'col-3-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-1-agg_3': 'FR', + 'col-2-agg_4': 'mac', + 'col-3-agg_1': 3029, + }, + ], + }, + ], + }, + ], + direction: 'row', + }, + oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative: { + tables: [ + { + columns: [ + { + id: 'col-0-agg_3', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_4', + name: '@timestamp per day', + }, + { + id: 'col-2-agg_1', + name: 'Average bytes', + }, + { + id: 'col-3-agg_2', + name: 'Min @timestamp', + }, + { + id: 'col-4-agg_5', + name: 'Derivative of Count', + }, + { + id: 'col-5-agg_6', + name: 'Last bytes', + }, + ], + rows: [ + { + 'col-0-agg_3': 'png', + 'col-1-agg_4': 1411862400000, + 'col-2-agg_1': 9283, + 'col-3-agg_2': 1411862400000, + 'col-5-agg_6': 23, + }, + { + 'col-0-agg_3': 'png', + 'col-1-agg_4': 1411948800000, + 'col-2-agg_1': 28349, + 'col-3-agg_2': 1411948800000, + 'col-4-agg_5': 203, + 'col-5-agg_6': 39, + }, + { + 'col-0-agg_3': 'png', + 'col-1-agg_4': 1412035200000, + 'col-2-agg_1': 84330, + 'col-3-agg_2': 1412035200000, + 'col-4-agg_5': 200, + 'col-5-agg_6': 329, + }, + { + 'col-0-agg_3': 'png', + 'col-1-agg_4': 1412121600000, + 'col-2-agg_1': 34992, + 'col-3-agg_2': 1412121600000, + 'col-4-agg_5': 103, + 'col-5-agg_6': 22, + }, + { + 'col-0-agg_3': 'png', + 'col-1-agg_4': 1412208000000, + 'col-2-agg_1': 145432, + 'col-3-agg_2': 1412208000000, + 'col-4-agg_5': 153, + 'col-5-agg_6': 93, + }, + { + 'col-0-agg_3': 'png', + 'col-1-agg_4': 1412294400000, + 'col-2-agg_1': 220943, + 'col-3-agg_2': 1412294400000, + 'col-4-agg_5': 239, + 'col-5-agg_6': 72, + }, + { + 'col-0-agg_3': 'css', + 'col-1-agg_4': 1411862400000, + 'col-2-agg_1': 9283, + 'col-3-agg_2': 1411862400000, + 'col-5-agg_6': 75, + }, + { + 'col-0-agg_3': 'css', + 'col-1-agg_4': 1411948800000, + 'col-2-agg_1': 28349, + 'col-3-agg_2': 1411948800000, + 'col-4-agg_5': 10, + 'col-5-agg_6': 11, + }, + { + 'col-0-agg_3': 'css', + 'col-1-agg_4': 1412035200000, + 'col-2-agg_1': 84330, + 'col-3-agg_2': 1412035200000, + 'col-4-agg_5': 24, + 'col-5-agg_6': 238, + }, + { + 'col-0-agg_3': 'css', + 'col-1-agg_4': 1412121600000, + 'col-2-agg_1': 34992, + 'col-3-agg_2': 1412121600000, + 'col-4-agg_5': 49, + 'col-5-agg_6': 343, + }, + { + 'col-0-agg_3': 'css', + 'col-1-agg_4': 1412208000000, + 'col-2-agg_1': 145432, + 'col-3-agg_2': 1412208000000, + 'col-4-agg_5': 100, + 'col-5-agg_6': 837, + }, + { + 'col-0-agg_3': 'css', + 'col-1-agg_4': 1412294400000, + 'col-2-agg_1': 220943, + 'col-3-agg_2': 1412294400000, + 'col-4-agg_5': 23, + 'col-5-agg_6': 302, + }, + { + 'col-0-agg_3': 'html', + 'col-1-agg_4': 1411862400000, + 'col-2-agg_1': 9283, + 'col-3-agg_2': 1411862400000, + 'col-5-agg_6': 30, + }, + { + 'col-0-agg_3': 'html', + 'col-1-agg_4': 1411948800000, + 'col-2-agg_1': 28349, + 'col-3-agg_2': 1411948800000, + 'col-4-agg_5': 1, + 'col-5-agg_6': 43, + }, + { + 'col-0-agg_3': 'html', + 'col-1-agg_4': 1412035200000, + 'col-2-agg_1': 84330, + 'col-3-agg_2': 1412035200000, + 'col-4-agg_5': 5, + 'col-5-agg_6': 88, + }, + { + 'col-0-agg_3': 'html', + 'col-1-agg_4': 1412121600000, + 'col-2-agg_1': 34992, + 'col-3-agg_2': 1412121600000, + 'col-4-agg_5': 10, + 'col-5-agg_6': 91, + }, + { + 'col-0-agg_3': 'html', + 'col-1-agg_4': 1412208000000, + 'col-2-agg_1': 145432, + 'col-3-agg_2': 1412208000000, + 'col-4-agg_5': 43, + 'col-5-agg_6': 534, + }, + { + 'col-0-agg_3': 'html', + 'col-1-agg_4': 1412294400000, + 'col-2-agg_1': 220943, + 'col-3-agg_2': 1412294400000, + 'col-4-agg_5': 1, + 'col-5-agg_6': 553, + }, + ], + }, + ], + }, +}; diff --git a/src/legacy/core_plugins/vis_type_table/public/legacy.ts b/src/legacy/core_plugins/vis_type_table/public/legacy.ts index 30403139d212d..3d5f8c1b3efe9 100644 --- a/src/legacy/core_plugins/vis_type_table/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_table/public/legacy.ts @@ -22,11 +22,10 @@ import { npSetup, npStart } from './legacy_imports'; import { plugin } from '.'; import { TablePluginSetupDependencies } from './plugin'; -import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; const plugins: Readonly = { expressions: npSetup.plugins.expressions, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, }; const pluginInstance = plugin({} as PluginInitializerContext); diff --git a/src/legacy/core_plugins/vis_type_table/public/plugin.ts b/src/legacy/core_plugins/vis_type_table/public/plugin.ts index 42bd36c83e28c..ea12a5320a14d 100644 --- a/src/legacy/core_plugins/vis_type_table/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_table/public/plugin.ts @@ -17,7 +17,7 @@ * under the License. */ import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup } from '../../visualizations/public'; +import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public'; diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts b/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts index ad56607e9296c..8d6f88bf8dd4a 100644 --- a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts +++ b/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts @@ -27,7 +27,7 @@ import StubIndexPattern from 'test_utils/stub_index_pattern'; import { getAngularModule } from './get_inner_angular'; import { initTableVisLegacyModule } from './table_vis_legacy_module'; import { tableVisTypeDefinition } from './table_vis_type'; -import { Vis } from '../../visualizations/public'; +import { Vis } from '../../../../plugins/visualizations/public'; // eslint-disable-next-line import { stubFields } from '../../../../plugins/data/public/stubs'; // eslint-disable-next-line diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts b/src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts index e70b09904253f..d26e860e51272 100644 --- a/src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts +++ b/src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts @@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import { AggGroupNames } from '../../../../plugins/data/public'; import { Schemas } from '../../vis_default_editor/public'; -import { Vis } from '../../visualizations/public'; +import { Vis } from '../../../../plugins/visualizations/public'; import { tableVisResponseHandler } from './table_vis_response_handler'; // @ts-ignore import tableVisTemplate from './table_vis.html'; diff --git a/src/legacy/core_plugins/vis_type_table/public/types.ts b/src/legacy/core_plugins/vis_type_table/public/types.ts index 39023d1305cb6..c6de14b9f050c 100644 --- a/src/legacy/core_plugins/vis_type_table/public/types.ts +++ b/src/legacy/core_plugins/vis_type_table/public/types.ts @@ -17,7 +17,7 @@ * under the License. */ -import { SchemaConfig } from '../../visualizations/public'; +import { SchemaConfig } from '../../../../plugins/visualizations/public'; export enum AggTypes { SUM = 'sum', diff --git a/src/legacy/core_plugins/vis_type_table/public/vis_controller.ts b/src/legacy/core_plugins/vis_type_table/public/vis_controller.ts index 2feaad9f4e6b6..5bb730d2f9b10 100644 --- a/src/legacy/core_plugins/vis_type_table/public/vis_controller.ts +++ b/src/legacy/core_plugins/vis_type_table/public/vis_controller.ts @@ -20,11 +20,10 @@ import angular, { IModule, auto, IRootScopeService, IScope, ICompileService } from 'angular'; import $ from 'jquery'; -import { VisParams } from '../../visualizations/public'; +import { VisParams, ExprVis } from '../../../../plugins/visualizations/public'; import { npStart } from './legacy_imports'; import { getAngularModule } from './get_inner_angular'; import { initTableVisLegacyModule } from './table_vis_legacy_module'; -import { ExprVis } from '../../visualizations/public/np_ready/public/expressions/vis'; const innerAngularName = 'kibana/table_vis'; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js b/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js index 6f54744a2f508..9e611861417cd 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js @@ -19,20 +19,23 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import { start as visualizationsStart } from '../../../../../core_plugins/visualizations/public/np_ready/public/legacy'; import { ImageComparator } from 'test_utils/image_comparator'; import { createTagCloudVisualization } from '../tag_cloud_visualization'; import basicdrawPng from './basicdraw.png'; import afterresizePng from './afterresize.png'; import afterparamChange from './afterparamchange.png'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ExprVis } from '../../../../../../plugins/visualizations/public/expressions/vis'; // Replace with mock when converting to jest tests // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { seedColors } from '../../../../../../plugins/charts/public/services/colors/seed_colors'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { BaseVisType } from '../../../../../../plugins/visualizations/public/vis_types/base_vis_type'; +import { createTagCloudVisTypeDefinition } from '../../tag_cloud_type'; const THRESHOLD = 0.65; const PIXEL_DIFF = 64; - describe('TagCloudVisualizationTest', function() { let domNode; let vis; @@ -67,10 +70,11 @@ describe('TagCloudVisualizationTest', function() { describe('TagCloudVisualization - basics', function() { beforeEach(async function() { + const visType = new BaseVisType(createTagCloudVisTypeDefinition({ colors: seedColors })); setupDOM('512px', '512px'); imageComparator = new ImageComparator(); - vis = visualizationsStart.createVis('tagcloud', { - type: 'tagcloud', + vis = new ExprVis({ + type: visType, params: { bucket: { accessor: 0, format: {} }, metric: { accessor: 0, format: {} }, diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts index 96073caf20515..f70789edc66ba 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts @@ -19,14 +19,12 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; - -import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; import { TagCloudPluginSetupDependencies } from './plugin'; import { plugin } from '.'; const plugins: Readonly = { expressions: npSetup.plugins.expressions, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, charts: npSetup.plugins.charts, }; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts index c9dd4943519be..1061271aa315b 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts @@ -19,7 +19,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup } from '../../visualizations/public'; +import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; import { ChartsPluginSetup } from '../../../../plugins/charts/public'; import { createTagCloudFn } from './tag_cloud_fn'; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/services.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/services.ts index 30fd185be877d..fef46282eb8dd 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/services.ts +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/services.ts @@ -23,3 +23,5 @@ import { DataPublicPluginStart } from '../../../../plugins/data/public'; export const [getFormatService, setFormatService] = createGetterSetter< DataPublicPluginStart['fieldFormats'] >('data.fieldFormats'); + +export { npStart } from 'ui/new_platform'; diff --git a/src/legacy/core_plugins/vis_type_timelion/index.ts b/src/legacy/core_plugins/vis_type_timelion/index.ts index 6c1e3f452959e..7bca5154c84fd 100644 --- a/src/legacy/core_plugins/vis_type_timelion/index.ts +++ b/src/legacy/core_plugins/vis_type_timelion/index.ts @@ -25,7 +25,7 @@ import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy const timelionVisPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => new Plugin({ id: 'timelion_vis', - require: ['kibana', 'elasticsearch', 'visualizations'], + require: ['kibana', 'elasticsearch'], publicDir: resolve(__dirname, 'public'), uiExports: { styleSheetPaths: resolve(__dirname, 'public/index.scss'), diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_vis.tsx b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_vis.tsx index f55d1602ea342..0fad0a164bf0b 100644 --- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_vis.tsx +++ b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_vis.tsx @@ -23,7 +23,7 @@ import { IUiSettingsClient } from 'kibana/public'; import { ChartComponent } from './chart'; import { VisParams } from '../timelion_vis_fn'; import { TimelionSuccessResponse } from '../helpers/timelion_request_handler'; -import { ExprVis } from '../../../visualizations/public/np_ready/public/expressions/vis'; +import { ExprVis } from '../../../../../plugins/visualizations/public'; export interface TimelionVisComponentProp { config: IUiSettingsClient; diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts b/src/legacy/core_plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts index 47bfed6340e93..61e31420f73ba 100644 --- a/src/legacy/core_plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts +++ b/src/legacy/core_plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { KIBANA_CONTEXT_NAME } from 'src/plugins/expressions/public'; -import { VisParams } from 'src/legacy/core_plugins/visualizations/public'; +import { VisParams } from '../../../../../plugins/visualizations/public'; import { TimeRange, Filter, esQuery, Query } from '../../../../../plugins/data/public'; import { TimelionVisDependencies } from '../plugin'; import { getTimezone } from './get_timezone'; diff --git a/src/legacy/core_plugins/vis_type_timelion/public/legacy.ts b/src/legacy/core_plugins/vis_type_timelion/public/legacy.ts index 9935f3d92f6bd..f8de9f94dcedf 100644 --- a/src/legacy/core_plugins/vis_type_timelion/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_timelion/public/legacy.ts @@ -20,15 +20,13 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from './legacy_imports'; - -import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; import { TimelionVisSetupDependencies } from './plugin'; import { plugin } from '.'; const setupPlugins: Readonly = { expressions: npSetup.plugins.expressions, data: npSetup.plugins.data, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, }; const pluginInstance = plugin({} as PluginInitializerContext); diff --git a/src/legacy/core_plugins/vis_type_timelion/public/plugin.ts b/src/legacy/core_plugins/vis_type_timelion/public/plugin.ts index 8d067369fef70..b5aa64db19aa4 100644 --- a/src/legacy/core_plugins/vis_type_timelion/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_timelion/public/plugin.ts @@ -29,7 +29,7 @@ import { Plugin as ExpressionsPlugin } from 'src/plugins/expressions/public'; import { DataPublicPluginSetup, TimefilterContract } from 'src/plugins/data/public'; import { PluginsStart } from './legacy_imports'; -import { VisualizationsSetup } from '../../visualizations/public/np_ready/public'; +import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; import { getTimelionVisualizationConfig } from './timelion_vis_fn'; import { getTimelionVisDefinition } from './timelion_vis_type'; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts b/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts index fb22bbd4146e2..42f116701be51 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts @@ -19,14 +19,12 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; - -import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; import { MetricsPluginSetupDependencies } from './plugin'; import { plugin } from '.'; const plugins: Readonly = { expressions: npSetup.plugins.expressions, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, }; const pluginInstance = plugin({} as PluginInitializerContext); diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts b/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts index 441b1f05ea78c..0310ecf6cfd87 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts @@ -18,7 +18,7 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup } from '../../visualizations/public'; +import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; import { createMetricsFn } from './metrics_fn'; import { metricsVisDefinition } from './metrics_type'; diff --git a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js index 0db3e6cefa724..c7fbc0815b07c 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js +++ b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js @@ -39,15 +39,15 @@ import vegaMapImage256 from './vega_map_image_256.png'; import { VegaParser } from '../data_model/vega_parser'; import { SearchCache } from '../data_model/search_cache'; -import { - setup as visualizationsSetup, - start as visualizationsStart, -} from '../../../visualizations/public/np_ready/public/legacy'; import { createVegaTypeDefinition } from '../vega_type'; // TODO This is an integration test and thus requires a running platform. When moving to the new platform, // this test has to be migrated to the newly created integration test environment. // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { npStart } from 'ui/new_platform'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { BaseVisType } from '../../../../../plugins/visualizations/public/vis_types/base_vis_type'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ExprVis } from '../../../../../plugins/visualizations/public/expressions/vis'; import { setInjectedVars } from '../services'; const THRESHOLD = 0.1; @@ -59,7 +59,7 @@ describe('VegaVisualizations', () => { let vis; let imageComparator; let vegaVisualizationDependencies; - let visRegComplete = false; + let vegaVisType; setInjectedVars({ emsTileLayerId: {}, @@ -89,13 +89,7 @@ describe('VegaVisualizations', () => { }, }; - if (!visRegComplete) { - visRegComplete = true; - visualizationsSetup.createBaseVisualization( - createVegaTypeDefinition(vegaVisualizationDependencies) - ); - } - + vegaVisType = new BaseVisType(createVegaTypeDefinition(vegaVisualizationDependencies)); VegaVisualization = createVegaVisualization(vegaVisualizationDependencies); }) ); @@ -105,7 +99,9 @@ describe('VegaVisualizations', () => { setupDOM('512px', '512px'); imageComparator = new ImageComparator(); - vis = visualizationsStart.createVis('vega', { type: 'vega' }); + vis = new ExprVis({ + type: vegaVisType, + }); }); afterEach(function() { diff --git a/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.js b/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.js index 7c2638d1f5165..735ce60f76d47 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.js +++ b/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.js @@ -116,7 +116,8 @@ export class VegaParser { */ _compileVegaLite() { this.vlspec = this.spec; - const logger = vega.logger(vega.Warn); + // eslint-disable-next-line import/namespace + const logger = vega.logger(vega.Warn); // note: eslint has a false positive here logger.warn = this._onWarning.bind(this); this.spec = vegaLite.compile(this.vlspec, logger).spec; diff --git a/src/legacy/core_plugins/vis_type_vega/public/legacy.ts b/src/legacy/core_plugins/vis_type_vega/public/legacy.ts index 38ce706ed13ef..b2c73894d978d 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/legacy.ts @@ -19,15 +19,13 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; - -import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; import { VegaPluginSetupDependencies, VegaPluginStartDependencies } from './plugin'; import { LegacyDependenciesPlugin } from './shim'; import { plugin } from '.'; const setupPlugins: Readonly = { ...npSetup.plugins, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, // Temporary solution // It will be removed when all dependent services are migrated to the new platform. diff --git a/src/legacy/core_plugins/vis_type_vega/public/plugin.ts b/src/legacy/core_plugins/vis_type_vega/public/plugin.ts index 3b01d9ceca5a6..38b92a40cd99a 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/plugin.ts @@ -20,7 +20,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../.. import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public'; -import { VisualizationsSetup } from '../../visualizations/public'; +import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; import { setNotifications, setData, diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js index 76a2e672e0bd0..c90f059ff7c94 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -132,7 +132,8 @@ export class VegaBaseView { createViewConfig() { const config = { - logLevel: vega.Warn, + // eslint-disable-next-line import/namespace + logLevel: vega.Warn, // note: eslint has a false positive here renderer: this._parser.renderer, }; diff --git a/src/legacy/core_plugins/vis_type_vislib/index.ts b/src/legacy/core_plugins/vis_type_vislib/index.ts index 1f75aea31ba0b..da9476285a9b2 100644 --- a/src/legacy/core_plugins/vis_type_vislib/index.ts +++ b/src/legacy/core_plugins/vis_type_vislib/index.ts @@ -25,7 +25,7 @@ import { LegacyPluginApi, LegacyPluginInitializer } from '../../types'; const visTypeVislibPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => new Plugin({ id: 'vis_type_vislib', - require: ['kibana', 'elasticsearch', 'visualizations', 'interpreter'], + require: ['kibana', 'elasticsearch', 'interpreter'], publicDir: resolve(__dirname, 'public'), styleSheetPaths: resolve(__dirname, 'public/index.scss'), uiExports: { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx index bc12e04e29468..ec7a325ba43d1 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx @@ -22,7 +22,7 @@ import React, { useMemo, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from '../../../../../../../plugins/visualizations/public'; import { SeriesParam, ValueAxis } from '../../../types'; import { ChartTypes } from '../../../utils/collections'; import { SelectOption } from '../../common'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx index a53d21b121f7d..01a69a6fac70b 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx @@ -22,7 +22,7 @@ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from '../../../../../../../plugins/visualizations/public'; import { SeriesParam } from '../../../types'; import { NumberInputOption, SelectOption, SwitchOption } from '../../common'; import { SetChart } from './chart_options'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts index 58c75629f1fa1..0d9fa8c25a4f7 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from '../../../../../../../plugins/visualizations/public'; import { Axis, ValueAxis, SeriesParam, Style } from '../../../types'; import { ChartTypes, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx index 44e7a4cfb0088..22a726b53363b 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx @@ -23,7 +23,7 @@ import { EuiPanel, EuiTitle, EuiSpacer, EuiAccordion } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from '../../../../../../../plugins/visualizations/public'; import { ValueAxis, SeriesParam } from '../../../types'; import { ChartOptions } from './chart_options'; import { SetParamByIndex, ChangeValueAxis } from './'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx index 30d80ed595fe7..912c3b904b110 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx @@ -31,7 +31,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from '../../../../../../../plugins/visualizations/public'; import { SeriesParam, ValueAxis } from '../../../types'; import { ValueAxisOptions } from './value_axis_options'; import { SetParamByIndex } from './'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx index 0e78bf2f31ef6..8f0327e78c7ab 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx @@ -21,7 +21,7 @@ import React, { useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSpacer, EuiAccordion, EuiHorizontalRule } from '@elastic/eui'; -import { Vis } from 'src/legacy/core_plugins/visualizations/public'; +import { Vis } from '../../../../../../../plugins/visualizations/public'; import { ValueAxis } from '../../../types'; import { Positions } from '../../../utils/collections'; import { SelectOption, SwitchOption, TextInputOption } from '../../common'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts index ef7277222e5fd..aa11e0ef41fba 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts @@ -25,11 +25,10 @@ import { VisTypeVislibPluginSetupDependencies, VisTypeVislibPluginStartDependencies, } from './plugin'; -import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; const setupPlugins: Readonly = { expressions: npSetup.plugins.expressions, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, charts: npSetup.plugins.charts, }; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts index 9f7b8cbeea11e..da16a38deba9f 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts @@ -19,6 +19,7 @@ import { search } from '../../../../plugins/data/public'; export const { tabifyAggResponse, tabifyGetColumns } = search; + // @ts-ignore export { buildHierarchicalData } from 'ui/agg_response/hierarchical/build_hierarchical_data'; // @ts-ignore diff --git a/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts b/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts index 8b4510050802e..2731fb6f5fbe6 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts @@ -25,7 +25,7 @@ import { } from 'kibana/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup } from '../../visualizations/public'; +import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; import { createVisTypeVislibVisFn } from './vis_type_vislib_vis_fn'; import { createPieVisFn } from './pie_fn'; import { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx b/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx index 010b61a0900b0..ec091e5d29cfd 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx @@ -26,8 +26,7 @@ import { Positions } from './utils/collections'; import { VisTypeVislibDependencies } from './plugin'; import { mountReactNode } from '../../../../core/public/utils'; import { VisLegend, CUSTOM_LEGEND_VIS_TYPES } from './vislib/components/legend'; -import { VisParams } from '../../visualizations/public'; -import { ExprVis } from '../../visualizations/public/np_ready/public/expressions/vis'; +import { VisParams, ExprVis } from '../../../../plugins/visualizations/public'; const legendClassName = { top: 'visLib--legend-top', diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js index 21f4e60e4bc6e..caafb2c636271 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js @@ -22,91 +22,8 @@ import _ from 'lodash'; import $ from 'jquery'; import expect from '@kbn/expect'; -import { threeTermBuckets } from 'fixtures/fake_hierarchical_data'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; - -import { start as visualizationsStart } from '../../../../../visualizations/public/np_ready/public/legacy'; import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture'; -import { tabifyAggResponse } from '../../../legacy_imports'; -import { vislibSlicesResponseHandler } from '../../response_handler'; - -const rowAgg = [ - { type: 'avg', schema: 'metric', params: { field: 'bytes' } }, - { type: 'terms', schema: 'split', params: { field: 'extension', rows: true } }, - { type: 'terms', schema: 'segment', params: { field: 'machine.os' } }, - { type: 'terms', schema: 'segment', params: { field: 'geo.src' } }, -]; - -const rowAggDimensions = { - splitRow: [ - { - accessor: 0, - }, - ], - buckets: [ - { - accessor: 2, - }, - { - accessor: 4, - }, - ], - metric: { - accessor: 5, - }, -}; - -const colAgg = [ - { type: 'avg', schema: 'metric', params: { field: 'bytes' } }, - { type: 'terms', schema: 'split', params: { field: 'extension', row: false } }, - { type: 'terms', schema: 'segment', params: { field: 'machine.os' } }, - { type: 'terms', schema: 'segment', params: { field: 'geo.src' } }, -]; - -const colAggDimensions = { - splitColumn: [ - { - accessor: 0, - }, - ], - buckets: [ - { - accessor: 2, - }, - { - accessor: 4, - }, - ], - metric: { - accessor: 5, - }, -}; - -const sliceAgg = [ - { type: 'avg', schema: 'metric', params: { field: 'bytes' } }, - { type: 'terms', schema: 'segment', params: { field: 'machine.os' } }, - { type: 'terms', schema: 'segment', params: { field: 'geo.src' } }, -]; - -const sliceAggDimensions = { - buckets: [ - { - accessor: 0, - }, - { - accessor: 2, - }, - ], - metric: { - accessor: 3, - }, -}; - -const aggArray = [ - [rowAgg, rowAggDimensions], - [colAgg, colAggDimensions], - [sliceAgg, sliceAggDimensions], -]; +import { pieChartMockData } from './pie_chart_mock_data'; const names = ['rows', 'columns', 'slices']; @@ -121,47 +38,14 @@ describe('No global chart settings', function() { }; let chart1; let mockUiState; - let indexPattern; - let responseHandler; - let data1; - let stubVis1; beforeEach(() => { chart1 = getVis(visLibParams1); mockUiState = getMockUiState(); - indexPattern = new FixturesStubbedLogstashIndexPatternProvider(); - responseHandler = vislibSlicesResponseHandler; - - let id1 = 1; - stubVis1 = visualizationsStart.createVis('pie', { - type: 'pie', - data: { - aggs: rowAgg, - searchSource: { - getField: name => { - if (name === 'index') { - return indexPattern; - } - }, - }, - }, - }); - - stubVis1.isHierarchical = () => true; - - // We need to set the aggs to a known value. - _.each(stubVis1.data.aggs.aggs, function(agg) { - agg.id = 'agg_' + id1++; - }); }); beforeEach(async () => { - const table1 = tabifyAggResponse(stubVis1.data.aggs, threeTermBuckets, { - metricsAtAllLevels: true, - }); - data1 = await responseHandler(table1, rowAggDimensions); - - chart1.render(data1, mockUiState); + chart1.render(pieChartMockData.rowData, mockUiState); }); afterEach(function() { @@ -209,55 +93,21 @@ describe('No global chart settings', function() { }); describe('Vislib PieChart Class Test Suite', function() { - aggArray.forEach(function(aggItem, i) { - const [dataAgg, dataDimensions] = aggItem; + ['rowData', 'columnData', 'sliceData'].forEach(function(aggItem, i) { describe('Vislib PieChart Class Test Suite for ' + names[i] + ' data', function() { + const mockPieData = pieChartMockData[aggItem]; + const visLibParams = { type: 'pie', addLegend: true, addTooltip: true, }; let vis; - let mockUiState; - let indexPattern; - let data; - let stubVis; - let responseHandler; - - beforeEach(() => { - vis = getVis(visLibParams); - mockUiState = getMockUiState(); - indexPattern = new FixturesStubbedLogstashIndexPatternProvider(); - responseHandler = vislibSlicesResponseHandler; - - let id = 1; - stubVis = visualizationsStart.createVis('pie', { - type: 'pie', - data: { - aggs: dataAgg, - searchSource: { - getField: name => { - if (name === 'index') { - return indexPattern; - } - }, - }, - }, - }); - - // We need to set the aggs to a known value. - _.each(stubVis.data.aggs.aggs, function(agg) { - agg.id = 'agg_' + id++; - }); - }); beforeEach(async () => { - const table = tabifyAggResponse(stubVis.data.aggs, threeTermBuckets, { - metricsAtAllLevels: true, - }); - data = await responseHandler(table, dataDimensions); - - vis.render(data, mockUiState); + vis = getVis(visLibParams); + const mockUiState = getMockUiState(); + vis.render(mockPieData, mockUiState); }); afterEach(function() { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart_mock_data.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart_mock_data.js new file mode 100644 index 0000000000000..188cd51759e51 --- /dev/null +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart_mock_data.js @@ -0,0 +1,3742 @@ +/* + * 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 const pieChartMockData = { + rowData: { + rows: [ + { + hits: 4, + raw: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + names: ['IT', 'win', 'mac', 'US', 'linux'], + slices: { + children: [ + { + name: 'IT', + size: 9299, + children: [ + { + name: 'win', + size: 0, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 4, + value: 'win', + }, + }, + { + name: 'mac', + size: 9299, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 1, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 2, + value: 'IT', + }, + }, + { + name: 'US', + size: 8293, + children: [ + { + name: 'linux', + size: 3992, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 4, + value: 'linux', + }, + }, + { + name: 'mac', + size: 3029, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 3, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 2, + value: 'US', + }, + }, + ], + }, + label: 'png: extension: Descending', + }, + { + hits: 4, + raw: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + names: ['MX', 'win', 'mac', 'US', 'linux'], + slices: { + children: [ + { + name: 'MX', + size: 9299, + children: [ + { + name: 'win', + size: 4992, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 4, + value: 'win', + }, + }, + { + name: 'mac', + size: 5892, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 1, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 2, + value: 'MX', + }, + }, + { + name: 'US', + size: 8293, + children: [ + { + name: 'linux', + size: 3992, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 4, + value: 'linux', + }, + }, + { + name: 'mac', + size: 3029, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 3, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 2, + value: 'US', + }, + }, + ], + }, + label: 'css: extension: Descending', + }, + { + hits: 4, + raw: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + names: ['CN', 'win', 'mac', 'FR'], + slices: { + children: [ + { + name: 'CN', + size: 9299, + children: [ + { + name: 'win', + size: 4992, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 4, + value: 'win', + }, + }, + { + name: 'mac', + size: 5892, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 1, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 2, + value: 'CN', + }, + }, + { + name: 'FR', + size: 8293, + children: [ + { + name: 'win', + size: 3992, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 4, + value: 'win', + }, + }, + { + name: 'mac', + size: 3029, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 3, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 2, + value: 'FR', + }, + }, + ], + }, + label: 'html: extension: Descending', + }, + ], + hits: 12, + }, + columnData: { + columns: [ + { + hits: 4, + raw: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + names: ['IT', 'win', 'mac', 'US', 'linux'], + slices: { + children: [ + { + name: 'IT', + size: 9299, + children: [ + { + name: 'win', + size: 0, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 4, + value: 'win', + }, + }, + { + name: 'mac', + size: 9299, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 1, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 2, + value: 'IT', + }, + }, + { + name: 'US', + size: 8293, + children: [ + { + name: 'linux', + size: 3992, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 4, + value: 'linux', + }, + }, + { + name: 'mac', + size: 3029, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 3, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 0, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 2, + value: 'US', + }, + }, + ], + }, + label: 'png: extension: Descending', + }, + { + hits: 4, + raw: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + names: ['MX', 'win', 'mac', 'US', 'linux'], + slices: { + children: [ + { + name: 'MX', + size: 9299, + children: [ + { + name: 'win', + size: 4992, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 4, + value: 'win', + }, + }, + { + name: 'mac', + size: 5892, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 1, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 2, + value: 'MX', + }, + }, + { + name: 'US', + size: 8293, + children: [ + { + name: 'linux', + size: 3992, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 4, + value: 'linux', + }, + }, + { + name: 'mac', + size: 3029, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 3, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'linux', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 2, + value: 'US', + }, + }, + ], + }, + label: 'css: extension: Descending', + }, + { + hits: 4, + raw: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + names: ['CN', 'win', 'mac', 'FR'], + slices: { + children: [ + { + name: 'CN', + size: 9299, + children: [ + { + name: 'win', + size: 4992, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 4, + value: 'win', + }, + }, + { + name: 'mac', + size: 5892, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 1, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 0, + column: 2, + value: 'CN', + }, + }, + { + name: 'FR', + size: 8293, + children: [ + { + name: 'win', + size: 3992, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 4, + value: 'win', + }, + }, + { + name: 'mac', + size: 3029, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 3, + column: 4, + value: 'mac', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'extension: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'machine.os: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + { + id: 'col-4-agg_4', + name: 'geo.src: Descending', + }, + { + id: 'col-5-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 4992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + 'col-5-agg_1': 5892, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'win', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3992, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-4-agg_4': 'mac', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + 'col-5-agg_1': 3029, + }, + ], + }, + row: 2, + column: 2, + value: 'FR', + }, + }, + ], + }, + label: 'html: extension: Descending', + }, + ], + hits: 12, + }, + sliceData: { + hits: 6, + raw: { + columns: [ + { + id: 'col-0-agg_2', + name: 'machine.os: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + ], + }, + names: ['png', 'IT', 'US', 'css', 'MX', 'html', 'CN', 'FR'], + slices: { + children: [ + { + name: 'png', + size: 412032, + children: [ + { + name: 'IT', + size: 9299, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'machine.os: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + ], + }, + row: 0, + column: 2, + value: 'IT', + }, + }, + { + name: 'US', + size: 8293, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'machine.os: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + ], + }, + row: 1, + column: 2, + value: 'US', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'machine.os: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + ], + }, + row: 0, + column: 0, + value: 'png', + }, + }, + { + name: 'css', + size: 412032, + children: [ + { + name: 'MX', + size: 9299, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'machine.os: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + ], + }, + row: 2, + column: 2, + value: 'MX', + }, + }, + { + name: 'US', + size: 8293, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'machine.os: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + ], + }, + row: 3, + column: 2, + value: 'US', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'machine.os: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + ], + }, + row: 2, + column: 0, + value: 'css', + }, + }, + { + name: 'html', + size: 412032, + children: [ + { + name: 'CN', + size: 9299, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'machine.os: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + ], + }, + row: 4, + column: 2, + value: 'CN', + }, + }, + { + name: 'FR', + size: 8293, + children: [], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'machine.os: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + ], + }, + row: 5, + column: 2, + value: 'FR', + }, + }, + ], + rawData: { + table: { + columns: [ + { + id: 'col-0-agg_2', + name: 'machine.os: Descending', + }, + { + id: 'col-1-agg_1', + name: 'Average bytes', + }, + { + id: 'col-2-agg_3', + name: 'geo.src: Descending', + }, + { + id: 'col-3-agg_1', + name: 'Average bytes', + }, + ], + rows: [ + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'IT', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'png', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'MX', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'css', + 'col-2-agg_3': 'US', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'CN', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 9299, + }, + { + 'col-0-agg_2': 'html', + 'col-2-agg_3': 'FR', + 'col-1-agg_1': 412032, + 'col-3-agg_1': 8293, + }, + ], + }, + row: 4, + column: 0, + value: 'html', + }, + }, + ], + }, + }, +}; diff --git a/src/legacy/core_plugins/vis_type_xy/public/legacy.ts b/src/legacy/core_plugins/vis_type_xy/public/legacy.ts index e1cee9c30804a..740ceeaac6a7d 100644 --- a/src/legacy/core_plugins/vis_type_xy/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_xy/public/legacy.ts @@ -22,20 +22,16 @@ import { PluginInitializerContext } from 'kibana/public'; import { plugin } from '.'; import { VisTypeXyPluginSetupDependencies, VisTypeXyPluginStartDependencies } from './plugin'; -import { - setup as visualizationsSetup, - start as visualizationsStart, -} from '../../visualizations/public/np_ready/public/legacy'; const setupPlugins: Readonly = { expressions: npSetup.plugins.expressions, - visualizations: visualizationsSetup, + visualizations: npSetup.plugins.visualizations, charts: npSetup.plugins.charts, }; const startPlugins: Readonly = { expressions: npStart.plugins.expressions, - visualizations: visualizationsStart, + visualizations: npStart.plugins.visualizations, }; const pluginInstance = plugin({} as PluginInitializerContext); diff --git a/src/legacy/core_plugins/vis_type_xy/public/plugin.ts b/src/legacy/core_plugins/vis_type_xy/public/plugin.ts index 0884cdf6a5e18..ab01b6b3153fb 100644 --- a/src/legacy/core_plugins/vis_type_xy/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_xy/public/plugin.ts @@ -26,7 +26,10 @@ import { } from 'kibana/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup, VisualizationsStart } from '../../visualizations/public'; +import { + VisualizationsSetup, + VisualizationsStart, +} from '../../../../plugins/visualizations/public'; import { ChartsPluginSetup } from '../../../../plugins/charts/public'; export interface VisTypeXyDependencies { diff --git a/src/legacy/core_plugins/visualizations/package.json b/src/legacy/core_plugins/visualizations/package.json deleted file mode 100644 index 5b436f0c2fef2..0000000000000 --- a/src/legacy/core_plugins/visualizations/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "visualizations", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/visualizations/public/index.scss b/src/legacy/core_plugins/visualizations/public/index.scss deleted file mode 100644 index 238f58fbfa295..0000000000000 --- a/src/legacy/core_plugins/visualizations/public/index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'src/legacy/ui/public/styles/styling_constants'; -@import './np_ready/public/index'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json b/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json deleted file mode 100644 index f8637a71b2d35..0000000000000 --- a/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "id": "visualizations", - "version": "kibana", - "server": false, - "ui": true, - "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "usageCollection"] -} diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts deleted file mode 100644 index 078cc4a3f4035..0000000000000 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts +++ /dev/null @@ -1,52 +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. - */ - -/** - * Visualizations Plugin - public - * - * This is the entry point for the entire client-side public contract of the plugin. - * If something is not explicitly exported here, you can safely assume it is private - * to the plugin and not considered stable. - * - * All stateful contracts will be injected by the platform at runtime, and are defined - * in the setup/start interfaces in `plugin.ts`. The remaining items exported here are - * either types, or static code. - */ - -import { PublicContract } from '@kbn/utility-types'; -import { PluginInitializerContext } from '../../../../../../core/public'; -import { VisualizationsPlugin, VisualizationsSetup, VisualizationsStart } from './plugin'; - -/** @public */ -export { VisualizationsSetup, VisualizationsStart }; - -/** @public types */ -export { VisTypeAlias, VisType } from './vis_types'; -export { VisSavedObject } from './types'; -export { Vis, VisParams, SerializedVis, SerializedVisData, VisData } from './vis'; -import { VisualizeEmbeddableFactory, VisualizeEmbeddable } from './embeddable'; -export type VisualizeEmbeddableFactoryContract = PublicContract; -export type VisualizeEmbeddableContract = PublicContract; -export { TypesService } from './vis_types/types_service'; -export { VISUALIZE_EMBEDDABLE_TYPE, VisualizeInput } from './embeddable'; -export { SchemaConfig } from './legacy/build_pipeline'; - -export function plugin(initializerContext: PluginInitializerContext) { - return new VisualizationsPlugin(initializerContext); -} diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts deleted file mode 100644 index 216e523b07141..0000000000000 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts +++ /dev/null @@ -1,28 +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. - */ - -// eslint-disable-next-line -import { npSetup, npStart } from 'ui/new_platform'; -import { PluginInitializerContext } from '../../../../../../core/public'; -import { plugin } from '.'; - -const pluginInstance = plugin({} as PluginInitializerContext); - -export const setup = pluginInstance.setup(npSetup.core, npSetup.plugins); -export const start = pluginInstance.start(npStart.core, npStart.plugins); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/_vis.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/_vis.js deleted file mode 100644 index deb345a77cdb6..0000000000000 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/_vis.js +++ /dev/null @@ -1,107 +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 _ from 'lodash'; -import ngMock from 'ng_mock'; -import expect from '@kbn/expect'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { start as visualizations } from '../../legacy'; - -describe('Vis Class', function() { - let indexPattern; - let visTypes; - - let vis; - const stateFixture = { - type: 'pie', - aggs: [ - { type: 'avg', schema: 'metric', params: { field: 'bytes' } }, - { type: 'terms', schema: 'segment', params: { field: 'machine.os' } }, - { type: 'terms', schema: 'segment', params: { field: 'geo.src' } }, - ], - params: { isDonut: true }, - listeners: { click: _.noop }, - }; - - beforeEach(ngMock.module('kibana')); - beforeEach( - ngMock.inject(function(Private) { - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - visTypes = visualizations; - }) - ); - - beforeEach(function() { - vis = visualizations.createVis(indexPattern, stateFixture); - }); - - const verifyVis = function(vis) { - expect(vis).to.have.property('aggs'); - expect(vis.aggs.aggs).to.have.length(3); - - expect(vis).to.have.property('type'); - expect(vis.type).to.eql(visTypes.get('pie')); - - expect(vis).to.have.property('params'); - expect(vis.params).to.have.property('isDonut', true); - expect(vis).to.have.property('indexPattern', indexPattern); - }; - - describe('initialization', function() { - it('should set the state', function() { - verifyVis(vis); - }); - }); - - describe('getState()', function() { - it('should get a state that represents the... er... state', function() { - const state = vis.getEnabledState(); - expect(state).to.have.property('type', 'pie'); - - expect(state).to.have.property('params'); - expect(state.params).to.have.property('isDonut', true); - - expect(state).to.have.property('aggs'); - expect(state.aggs).to.have.length(3); - }); - }); - - describe('setState()', function() { - it('should set the state to defaults', function() { - const vis = visualizations.createVis(indexPattern); - expect(vis).to.have.property('type'); - expect(vis.type).to.eql(visTypes.get('histogram')); - expect(vis).to.have.property('aggs'); - expect(vis.aggs.aggs).to.have.length(1); - expect(vis).to.have.property('params'); - expect(vis.params).to.have.property('addLegend', true); - expect(vis.params).to.have.property('addTooltip', true); - }); - }); - - describe('isHierarchical()', function() { - it('should return true for hierarchical vis (like pie)', function() { - expect(vis.isHierarchical()).to.be(true); - }); - it('should return false for non-hierarchical vis (like histogram)', function() { - const vis = visualizations.createVis(indexPattern); - expect(vis.isHierarchical()).to.be(false); - }); - }); -}); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/base_vis_type.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/base_vis_type.js deleted file mode 100644 index 9c1dfd9780255..0000000000000 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/base_vis_type.js +++ /dev/null @@ -1,60 +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 expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { BaseVisType } from '../../../vis_types/base_vis_type'; - -describe('Base Vis Type', function() { - beforeEach(ngMock.module('kibana')); - - describe('initialization', () => { - it('should throw if mandatory properties are missing', () => { - expect(() => { - new BaseVisType({}); - }).to.throwError('vis_type must define its name'); - - expect(() => { - new BaseVisType({ name: 'test' }); - }).to.throwError('vis_type must define its title'); - - expect(() => { - new BaseVisType({ name: 'test', title: 'test' }); - }).to.throwError('vis_type must define its description'); - - expect(() => { - new BaseVisType({ name: 'test', title: 'test', description: 'test' }); - }).to.throwError('vis_type must define its icon or image'); - - expect(() => { - new BaseVisType({ name: 'test', title: 'test', description: 'test', icon: 'test' }); - }).to.throwError('vis_type must define visualization controller'); - - expect(() => { - new BaseVisType({ - name: 'test', - title: 'test', - description: 'test', - icon: 'test', - visualization: {}, - }); - }).to.not.throwError(); - }); - }); -}); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts deleted file mode 100644 index 17f777e4e80e1..0000000000000 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts +++ /dev/null @@ -1,75 +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 { PluginInitializerContext } from '../../../../../../core/public'; -import { VisualizationsSetup, VisualizationsStart } from './'; -import { VisualizationsPlugin } from './plugin'; -import { coreMock } from '../../../../../../core/public/mocks'; -import { embeddablePluginMock } from '../../../../../../plugins/embeddable/public/mocks'; -import { expressionsPluginMock } from '../../../../../../plugins/expressions/public/mocks'; -import { dataPluginMock } from '../../../../../../plugins/data/public/mocks'; -import { usageCollectionPluginMock } from '../../../../../../plugins/usage_collection/public/mocks'; -import { uiActionsPluginMock } from '../../../../../../plugins/ui_actions/public/mocks'; - -const createSetupContract = (): VisualizationsSetup => ({ - createBaseVisualization: jest.fn(), - createReactVisualization: jest.fn(), - registerAlias: jest.fn(), - hideTypes: jest.fn(), -}); - -const createStartContract = (): VisualizationsStart => ({ - get: jest.fn(), - all: jest.fn(), - getAliases: jest.fn(), - savedVisualizationsLoader: {} as any, - showNewVisModal: jest.fn(), - createVis: jest.fn(), - convertFromSerializedVis: jest.fn(), - convertToSerializedVis: jest.fn(), -}); - -const createInstance = async () => { - const plugin = new VisualizationsPlugin({} as PluginInitializerContext); - - const setup = plugin.setup(coreMock.createSetup(), { - data: dataPluginMock.createSetupContract(), - expressions: expressionsPluginMock.createSetupContract(), - embeddable: embeddablePluginMock.createSetupContract(), - usageCollection: usageCollectionPluginMock.createSetupContract(), - }); - const doStart = () => - plugin.start(coreMock.createStart(), { - data: dataPluginMock.createStartContract(), - expressions: expressionsPluginMock.createStartContract(), - uiActions: uiActionsPluginMock.createStartContract(), - }); - - return { - plugin, - setup, - doStart, - }; -}; - -export const visualizationsPluginMock = { - createSetupContract, - createStartContract, - createInstance, -}; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts deleted file mode 100644 index 3ade6cee0d4d2..0000000000000 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts +++ /dev/null @@ -1,176 +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 { - PluginInitializerContext, - CoreSetup, - CoreStart, - Plugin, -} from '../../../../../../core/public'; -import { TypesService, TypesSetup, TypesStart } from './vis_types'; -import { - setUISettings, - setTypes, - setI18n, - setCapabilities, - setHttp, - setIndexPatterns, - setSavedObjects, - setUsageCollector, - setFilterManager, - setExpressions, - setUiActions, - setSavedVisualizationsLoader, - setTimeFilter, - setAggs, - setChrome, - setOverlays, -} from './services'; -import { VISUALIZE_EMBEDDABLE_TYPE, VisualizeEmbeddableFactory } from './embeddable'; -import { ExpressionsSetup, ExpressionsStart } from '../../../../../../plugins/expressions/public'; -import { EmbeddableSetup } from '../../../../../../plugins/embeddable/public'; -import { visualization as visualizationFunction } from './expressions/visualization_function'; -import { visualization as visualizationRenderer } from './expressions/visualization_renderer'; -import { - DataPublicPluginSetup, - DataPublicPluginStart, -} from '../../../../../../plugins/data/public'; -import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/public'; -import { createSavedVisLoader, SavedVisualizationsLoader } from './saved_visualizations'; -import { SerializedVis, Vis } from './vis'; -import { showNewVisModal } from './wizard'; -import { UiActionsStart } from '../../../../../../plugins/ui_actions/public'; -import { - convertFromSerializedVis, - convertToSerializedVis, -} from './saved_visualizations/_saved_vis'; - -/** - * Interface for this plugin's returned setup/start contracts. - * - * @public - */ - -export type VisualizationsSetup = TypesSetup; - -export interface VisualizationsStart extends TypesStart { - savedVisualizationsLoader: SavedVisualizationsLoader; - createVis: (visType: string, visState?: SerializedVis) => Vis; - convertToSerializedVis: typeof convertToSerializedVis; - convertFromSerializedVis: typeof convertFromSerializedVis; - showNewVisModal: typeof showNewVisModal; -} - -export interface VisualizationsSetupDeps { - expressions: ExpressionsSetup; - embeddable: EmbeddableSetup; - usageCollection: UsageCollectionSetup; - data: DataPublicPluginSetup; -} - -export interface VisualizationsStartDeps { - data: DataPublicPluginStart; - expressions: ExpressionsStart; - uiActions: UiActionsStart; -} - -/** - * Visualizations Plugin - public - * - * This plugin's stateful contracts are returned from the `setup` and `start` methods - * below. The interfaces for these contracts are provided above. - * - * @internal - */ -export class VisualizationsPlugin - implements - Plugin< - VisualizationsSetup, - VisualizationsStart, - VisualizationsSetupDeps, - VisualizationsStartDeps - > { - private readonly types: TypesService = new TypesService(); - - constructor(initializerContext: PluginInitializerContext) {} - - public setup( - core: CoreSetup, - { expressions, embeddable, usageCollection, data }: VisualizationsSetupDeps - ): VisualizationsSetup { - setUISettings(core.uiSettings); - setUsageCollector(usageCollection); - - expressions.registerFunction(visualizationFunction); - expressions.registerRenderer(visualizationRenderer); - - const embeddableFactory = new VisualizeEmbeddableFactory(); - embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory); - - return { - ...this.types.setup(), - }; - } - - public start( - core: CoreStart, - { data, expressions, uiActions }: VisualizationsStartDeps - ): VisualizationsStart { - const types = this.types.start(); - setI18n(core.i18n); - setTypes(types); - setCapabilities(core.application.capabilities); - setHttp(core.http); - setSavedObjects(core.savedObjects); - setIndexPatterns(data.indexPatterns); - setFilterManager(data.query.filterManager); - setExpressions(expressions); - setUiActions(uiActions); - setTimeFilter(data.query.timefilter.timefilter); - setAggs(data.search.aggs); - setOverlays(core.overlays); - setChrome(core.chrome); - const savedVisualizationsLoader = createSavedVisLoader({ - savedObjectsClient: core.savedObjects.client, - indexPatterns: data.indexPatterns, - chrome: core.chrome, - overlays: core.overlays, - visualizationTypes: types, - }); - setSavedVisualizationsLoader(savedVisualizationsLoader); - - return { - ...types, - showNewVisModal, - /** - * creates new instance of Vis - * @param {IIndexPattern} indexPattern - index pattern to use - * @param {VisState} visState - visualization configuration - */ - createVis: (visType: string, visState?: SerializedVis) => new Vis(visType, visState), - convertToSerializedVis, - convertFromSerializedVis, - savedVisualizationsLoader, - }; - } - - public stop() { - this.types.stop(); - } -} diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/types.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/types.ts deleted file mode 100644 index 8f93a179af3bc..0000000000000 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/types.ts +++ /dev/null @@ -1,50 +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 { SavedObject } from '../../../../../../plugins/saved_objects/public'; -import { ISearchSource, AggConfigOptions } from '../../../../../../plugins/data/public'; -import { SerializedVis, Vis, VisParams } from './vis'; - -export { Vis, SerializedVis, VisParams }; - -export interface VisualizationController { - render(visData: any, visParams: any): Promise; - destroy(): void; - isLoaded?(): Promise | void; -} - -export interface SavedVisState { - type: string; - params: VisParams; - aggs: AggConfigOptions[]; -} - -export interface ISavedVis { - id: string; - title: string; - description?: string; - visState: SavedVisState; - searchSource?: ISearchSource; - uiStateJSON?: string; - savedSearchRefName?: string; - savedSearchId?: string; -} - -// @ts-ignore-next-line -export interface VisSavedObject extends SavedObject, ISavedVis {} diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/base_vis_type.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/base_vis_type.js deleted file mode 100644 index 50ff74cfe9dd3..0000000000000 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/base_vis_type.js +++ /dev/null @@ -1,80 +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 _ from 'lodash'; - -export class BaseVisType { - constructor(opts = {}) { - if (!opts.name) { - throw 'vis_type must define its name'; - } - if (!opts.title) { - throw 'vis_type must define its title'; - } - if (!opts.description) { - throw 'vis_type must define its description'; - } - if (!opts.icon && !opts.image) { - throw 'vis_type must define its icon or image'; - } - if (!opts.visualization) { - throw 'vis_type must define visualization controller'; - } - - const _defaults = { - // name, title, description, icon, image - visualization: null, // must be a class with render/resize/destroy methods - visConfig: { - defaults: {}, // default configuration - }, - requestHandler: 'courier', // select one from registry or pass a function - responseHandler: 'none', - editor: null, // no default is provided - editorConfig: { - collections: {}, // collections used for configuration (list of positions, ...) - }, - options: { - // controls the visualize editor - showTimePicker: true, - showQueryBar: true, - showFilterBar: true, - showIndexSelection: true, - hierarchicalData: false, // we should get rid of this i guess ? - }, - stage: 'production', - feedbackMessage: '', - hidden: false, - }; - - _.defaultsDeep(this, opts, _defaults); - - this.requiresSearch = this.requestHandler !== 'none'; - } - - shouldMarkAsExperimentalInUI() { - return this.stage === 'experimental'; - } - - get schemas() { - if (this.editorConfig && this.editorConfig.schemas) { - return this.editorConfig.schemas; - } - return []; - } -} diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index 4b7618712cdd8..a66d3b24732f0 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -195,6 +195,7 @@ export default () => }), workers: Joi.number().min(1), profile: Joi.boolean().default(false), + validateSyntaxOfNodeModules: Joi.boolean().default(true), }).default(), status: Joi.object({ allowAnonymous: Joi.boolean().default(false), diff --git a/src/legacy/server/http/index.js b/src/legacy/server/http/index.js index d616afb533d0a..3649987d89b9a 100644 --- a/src/legacy/server/http/index.js +++ b/src/legacy/server/http/index.js @@ -18,7 +18,6 @@ */ import { format } from 'url'; -import { resolve } from 'path'; import _ from 'lodash'; import Boom from 'boom'; @@ -32,22 +31,6 @@ export default async function(kbnServer, server, config) { await registerHapiPlugins(server); - // provide a simple way to expose static directories - server.decorate('server', 'exposeStaticDir', function(routePath, dirPath) { - this.route({ - path: routePath, - method: 'GET', - handler: { - directory: { - path: dirPath, - listing: false, - lookupCompressed: true, - }, - }, - config: { auth: false }, - }); - }); - // helper for creating view managers for servers server.decorate('server', 'setupViews', function(path, engines) { this.views({ @@ -77,7 +60,4 @@ export default async function(kbnServer, server, config) { .permanent(true); }, }); - - // Expose static assets - server.exposeStaticDir('/ui/{path*}', resolve(__dirname, '../../ui/public/assets')); } diff --git a/src/legacy/server/plugins/lib/plugin.js b/src/legacy/server/plugins/lib/plugin.js index e73f1bf2c4d38..2b392d13d595a 100644 --- a/src/legacy/server/plugins/lib/plugin.js +++ b/src/legacy/server/plugins/lib/plugin.js @@ -73,7 +73,10 @@ export class Plugin { }); if (this.publicDir) { - server.exposeStaticDir(`/plugins/${id}/{path*}`, this.publicDir); + server.newPlatform.__internals.http.registerStaticDir( + `/plugins/${id}/{path*}`, + this.publicDir + ); } // Many of the plugins are simply adding static assets to the server and we don't need diff --git a/src/legacy/server/sass/build.js b/src/legacy/server/sass/build.js index 3d892ce321c2e..1ec656786ccc5 100644 --- a/src/legacy/server/sass/build.js +++ b/src/legacy/server/sass/build.js @@ -34,7 +34,7 @@ const access = promisify(fs.access); const copyFile = promisify(fs.copyFile); const mkdirAsync = promisify(fs.mkdir); -const UI_ASSETS_DIR = resolve(__dirname, '../../ui/public/assets'); +const UI_ASSETS_DIR = resolve(__dirname, '../../../core/server/core_app/assets'); const DARK_THEME_IMPORTER = url => { if (url.includes('eui_colors_light')) { return { file: url.replace('eui_colors_light', 'eui_colors_dark') }; diff --git a/src/legacy/ui/public/chrome/api/sub_url_hooks.js b/src/legacy/ui/public/chrome/api/sub_url_hooks.js index 27d147b1ffc72..f147aef7b4b7d 100644 --- a/src/legacy/ui/public/chrome/api/sub_url_hooks.js +++ b/src/legacy/ui/public/chrome/api/sub_url_hooks.js @@ -17,11 +17,10 @@ * under the License. */ -import url from 'url'; - import { unhashUrl } from '../../../../../plugins/kibana_utils/public'; import { toastNotifications } from '../../notify/toasts'; import { npSetup } from '../../new_platform'; +import { areHashesDifferentButDecodedHashesEquals } from './sub_url_hooks_utils'; export function registerSubUrlHooks(angularModule, internals) { angularModule.run(($rootScope, Private, $location) => { @@ -49,17 +48,10 @@ export function registerSubUrlHooks(angularModule, internals) { $rootScope.$on('$locationChangeStart', (e, newUrl) => { // This handler fixes issue #31238 where browser back navigation // fails due to angular 1.6 parsing url encoded params wrong. - const parsedAbsUrl = url.parse($location.absUrl()); - const absUrlHash = parsedAbsUrl.hash ? parsedAbsUrl.hash.slice(1) : ''; - const decodedAbsUrlHash = decodeURIComponent(absUrlHash); - - const parsedNewUrl = url.parse(newUrl); - const newHash = parsedNewUrl.hash ? parsedNewUrl.hash.slice(1) : ''; - const decodedHash = decodeURIComponent(newHash); - - if (absUrlHash !== newHash && decodedHash === decodedAbsUrlHash) { + if (areHashesDifferentButDecodedHashesEquals($location.absUrl(), newUrl)) { // replace the urlencoded hash with the version that angular sees. - $location.url(absUrlHash).replace(); + const newHash = newUrl.split('#')[1] || ''; + $location.url(newHash).replace(); } }); diff --git a/src/legacy/ui/public/chrome/api/sub_url_hooks_utils.test.ts b/src/legacy/ui/public/chrome/api/sub_url_hooks_utils.test.ts new file mode 100644 index 0000000000000..4dec526302344 --- /dev/null +++ b/src/legacy/ui/public/chrome/api/sub_url_hooks_utils.test.ts @@ -0,0 +1,58 @@ +/* + * 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 { areHashesDifferentButDecodedHashesEquals } from './sub_url_hooks_utils'; + +test('false for different hashes', () => { + const url1 = `https://localhost/kibana/#/dashboard/id`; + const url2 = `https://localhost/kibana/#/dashboard/DIFFERENT`; + expect(areHashesDifferentButDecodedHashesEquals(url1, url2)).toBeFalsy(); +}); + +test('false for same hashes', () => { + const hash = `/dashboard/id?_a=(filters:!(),query:(language:kuery,query:''))&_g=(filters:!(),time:(from:now-120m,to:now))`; + const url1 = `https://localhost/kibana/#/${hash}`; + expect(areHashesDifferentButDecodedHashesEquals(url1, url1)).toBeFalsy(); +}); + +test('true for same hashes, but one is encoded', () => { + const hash = `/dashboard/id?_a=(filters:!(),query:(language:kuery,query:''))&_g=(filters:!(),time:(from:now-120m,to:now))`; + const url1 = `https://localhost/kibana/#/${hash}`; + const url2 = `https://localhost/kibana/#/${encodeURIComponent(hash)}`; + expect(areHashesDifferentButDecodedHashesEquals(url1, url2)).toBeTruthy(); +}); + +/** + * This edge case occurs when trying to navigate within kibana app using core's `navigateToApp` api + * and there is reserved characters in hash (see: query:'' part) + * For example: + * ```ts + * navigateToApp('kibana', { + * path: '#/dashboard/f8bc19f0-6918-11ea-9258-a74c2ded064d?_a=(filters:!(),query:(language:kuery,query:''))&_g=(filters:!(),time:(from:now-120m,to:now))' + * }) + * ``` + * Core internally is using url.parse which parses ' -> %27 and performs the navigation + * Then angular decodes it back and causes redundant history record if not the fix which is covered by the test below + */ +test("true for same hashes, but one has reserved character (') encoded", () => { + const hash = `/dashboard/id?_a=(filters:!(),query:(language:kuery,query:''))&_g=(filters:!(),time:(from:now-120m,to:now))`; + const url1 = `https://localhost/kibana/#/${hash}`; + const url2 = `https://localhost/kibana/#/${hash.replace(/\'/g, '%27')}`; + expect(areHashesDifferentButDecodedHashesEquals(url1, url2)).toBeTruthy(); +}); diff --git a/src/legacy/ui/public/chrome/api/sub_url_hooks_utils.ts b/src/legacy/ui/public/chrome/api/sub_url_hooks_utils.ts new file mode 100644 index 0000000000000..8517877acd387 --- /dev/null +++ b/src/legacy/ui/public/chrome/api/sub_url_hooks_utils.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. + */ + +export function areHashesDifferentButDecodedHashesEquals(urlA: string, urlB: string): boolean { + const getHash = (url: string) => url.split('#')[1] ?? ''; + const hashA = getHash(urlA); + const decodedHashA = decodeURIComponent(hashA); + + const hashB = getHash(urlB); + const decodedHashB = decodeURIComponent(hashB); + + return hashA !== hashB && decodedHashA === decodedHashB; +} diff --git a/src/legacy/ui/public/new_platform/__mocks__/helpers.ts b/src/legacy/ui/public/new_platform/__mocks__/helpers.ts index 35d6e070ae8f7..f9f4494929014 100644 --- a/src/legacy/ui/public/new_platform/__mocks__/helpers.ts +++ b/src/legacy/ui/public/new_platform/__mocks__/helpers.ts @@ -31,6 +31,7 @@ import { kibanaLegacyPluginMock } from '../../../../../plugins/kibana_legacy/pub import { chartPluginMock } from '../../../../../plugins/charts/public/mocks'; import { advancedSettingsMock } from '../../../../../plugins/advanced_settings/public/mocks'; import { savedObjectsManagementPluginMock } from '../../../../../plugins/saved_objects_management/public/mocks'; +import { visualizationsPluginMock } from '../../../../../plugins/visualizations/public/mocks'; /* eslint-enable @kbn/eslint/no-restricted-paths */ export const pluginsMock = { @@ -44,6 +45,7 @@ export const pluginsMock = { uiActions: uiActionsPluginMock.createSetupContract(), usageCollection: usageCollectionPluginMock.createSetupContract(), advancedSettings: advancedSettingsMock.createSetupContract(), + visualizations: visualizationsPluginMock.createSetupContract(), kibanaLegacy: kibanaLegacyPluginMock.createSetupContract(), savedObjectsManagement: savedObjectsManagementPluginMock.createSetupContract(), }), @@ -57,6 +59,7 @@ export const pluginsMock = { uiActions: uiActionsPluginMock.createStartContract(), management: managementPluginMock.createStartContract(), advancedSettings: advancedSettingsMock.createStartContract(), + visualizations: visualizationsPluginMock.createStartContract(), kibanaLegacy: kibanaLegacyPluginMock.createStartContract(), savedObjectsManagement: savedObjectsManagementPluginMock.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 67877c5382633..25647e4a08897 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 @@ -20,20 +20,7 @@ import sinon from 'sinon'; import { getFieldFormatsRegistry } from '../../../../test_utils/public/stub_field_formats'; import { METRIC_TYPE } from '@kbn/analytics'; -import { - setFieldFormats, - setIndexPatterns, - setInjectedMetadata, - setHttp, - setNotifications, - setOverlays, - setQueryService, - setSearchService, - setUiSettings, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../plugins/data/public/services'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { setAggs } from '../../../../../src/legacy/core_plugins/visualizations/public/np_ready/public/services'; +import { setSetupServices, setStartServices } from './set_services'; import { AggTypesRegistry, getAggTypes, @@ -57,6 +44,10 @@ const mockComponent = () => { return null; }; +let refreshInterval = undefined; +let isTimeRangeSelectorEnabled = true; +let isAutoRefreshSelectorEnabled = true; + export const mockUiSettings = { get: item => { return mockUiSettings[item]; @@ -64,6 +55,7 @@ export const mockUiSettings = { getUpdate$: () => ({ subscribe: sinon.fake(), }), + isDefault: sinon.fake(), 'query:allowLeadingWildcards': true, 'query:queryString:options': {}, 'courier:ignoreFilterIfFieldNotInIndex': true, @@ -71,20 +63,110 @@ export const mockUiSettings = { 'format:defaultTypeMap': {}, }; -const mockCore = { +const mockCoreSetup = { chrome: {}, + http: { + basePath: { + get: sinon.fake.returns(''), + }, + }, + injectedMetadata: {}, uiSettings: mockUiSettings, +}; + +const mockCoreStart = { + application: { + capabilities: {}, + }, + chrome: { + overlays: { + openModal: sinon.fake(), + }, + }, http: { basePath: { get: sinon.fake.returns(''), }, }, + i18n: {}, + overlays: {}, + savedObjects: { + client: {}, + }, + uiSettings: mockUiSettings, +}; + +const querySetup = { + state$: mockObservable(), + filterManager: { + getFetches$: sinon.fake(), + getFilters: sinon.fake(), + getAppFilters: sinon.fake(), + getGlobalFilters: sinon.fake(), + removeFilter: sinon.fake(), + addFilters: sinon.fake(), + setFilters: sinon.fake(), + removeAll: sinon.fake(), + getUpdates$: mockObservable, + }, + timefilter: { + timefilter: { + getFetch$: mockObservable, + getAutoRefreshFetch$: mockObservable, + getEnabledUpdated$: mockObservable, + getTimeUpdate$: mockObservable, + getRefreshIntervalUpdate$: mockObservable, + isTimeRangeSelectorEnabled: () => { + return isTimeRangeSelectorEnabled; + }, + isAutoRefreshSelectorEnabled: () => { + return isAutoRefreshSelectorEnabled; + }, + disableAutoRefreshSelector: () => { + isAutoRefreshSelectorEnabled = false; + }, + enableAutoRefreshSelector: () => { + isAutoRefreshSelectorEnabled = true; + }, + getRefreshInterval: () => { + return refreshInterval; + }, + setRefreshInterval: interval => { + refreshInterval = interval; + }, + enableTimeRangeSelector: () => { + isTimeRangeSelectorEnabled = true; + }, + disableTimeRangeSelector: () => { + isTimeRangeSelectorEnabled = false; + }, + getTime: sinon.fake(), + setTime: sinon.fake(), + getActiveBounds: sinon.fake(), + getBounds: sinon.fake(), + calculateBounds: sinon.fake(), + createFilter: sinon.fake(), + }, + history: sinon.fake(), + }, + savedQueries: { + saveQuery: sinon.fake(), + getAllSavedQueries: sinon.fake(), + findSavedQueries: sinon.fake(), + getSavedQuery: sinon.fake(), + deleteSavedQuery: sinon.fake(), + getSavedQueryCount: sinon.fake(), + }, }; const mockAggTypesRegistry = () => { const registry = new AggTypesRegistry(); const registrySetup = registry.setup(); - const aggTypes = getAggTypes({ uiSettings: mockCore.uiSettings }); + const aggTypes = getAggTypes({ + uiSettings: mockCoreSetup.uiSettings, + notifications: mockCoreStart.notifications, + query: querySetup, + }); aggTypes.buckets.forEach(type => registrySetup.registerBucket(type)); aggTypes.metrics.forEach(type => registrySetup.registerMetric(type)); @@ -93,12 +175,8 @@ const mockAggTypesRegistry = () => { const aggTypesRegistry = mockAggTypesRegistry(); -let refreshInterval = undefined; -let isTimeRangeSelectorEnabled = true; -let isAutoRefreshSelectorEnabled = true; - export const npSetup = { - core: mockCore, + core: mockCoreSetup, plugins: { advancedSettings: { component: { @@ -135,72 +213,7 @@ export const npSetup = { addProvider: sinon.fake(), getProvider: sinon.fake(), }, - query: { - state$: mockObservable(), - filterManager: { - getFetches$: sinon.fake(), - getFilters: sinon.fake(), - getAppFilters: sinon.fake(), - getGlobalFilters: sinon.fake(), - removeFilter: sinon.fake(), - addFilters: sinon.fake(), - setFilters: sinon.fake(), - removeAll: sinon.fake(), - getUpdates$: mockObservable, - }, - timefilter: { - timefilter: { - getTime: sinon.fake(), - getRefreshInterval: sinon.fake(), - getTimeUpdate$: mockObservable, - getRefreshIntervalUpdate$: mockObservable, - getFetch$: mockObservable, - getAutoRefreshFetch$: mockObservable, - getEnabledUpdated$: mockObservable, - getTimeUpdate$: mockObservable, - getRefreshIntervalUpdate$: mockObservable, - isTimeRangeSelectorEnabled: () => { - return isTimeRangeSelectorEnabled; - }, - isAutoRefreshSelectorEnabled: () => { - return isAutoRefreshSelectorEnabled; - }, - disableAutoRefreshSelector: () => { - isAutoRefreshSelectorEnabled = false; - }, - enableAutoRefreshSelector: () => { - isAutoRefreshSelectorEnabled = true; - }, - getRefreshInterval: () => { - return refreshInterval; - }, - setRefreshInterval: interval => { - refreshInterval = interval; - }, - enableTimeRangeSelector: () => { - isTimeRangeSelectorEnabled = true; - }, - disableTimeRangeSelector: () => { - isTimeRangeSelectorEnabled = false; - }, - getTime: sinon.fake(), - setTime: sinon.fake(), - getActiveBounds: sinon.fake(), - getBounds: sinon.fake(), - calculateBounds: sinon.fake(), - createFilter: sinon.fake(), - }, - history: sinon.fake(), - }, - savedQueries: { - saveQuery: sinon.fake(), - getAllSavedQueries: sinon.fake(), - findSavedQueries: sinon.fake(), - getSavedQuery: sinon.fake(), - deleteSavedQuery: sinon.fake(), - getSavedQueryCount: sinon.fake(), - }, - }, + query: querySetup, search: { aggs: { calculateAutoTimeExpression: sinon.fake(), @@ -213,7 +226,7 @@ export const npSetup = { }, }, }, - fieldFormats: getFieldFormatsRegistry(mockCore), + fieldFormats: getFieldFormatsRegistry(mockCoreSetup), }, share: { register: () => {}, @@ -280,17 +293,17 @@ export const npSetup = { visTypeVega: { config: sinon.fake(), }, + visualizations: { + createBaseVisualization: sinon.fake(), + createReactVisualization: sinon.fake(), + registerAlias: sinon.fake(), + hideTypes: sinon.fake(), + }, }, }; export const npStart = { - core: { - chrome: { - overlays: { - openModal: sinon.fake(), - }, - }, - }, + core: mockCoreStart, plugins: { management: { legacy: { @@ -410,7 +423,6 @@ export const npStart = { search: { aggs: { calculateAutoTimeExpression: sinon.fake(), - createAggConfigs: sinon.fake(), createAggConfigs: (indexPattern, configStates = []) => { return new AggConfigs(indexPattern, configStates, { typesRegistry: aggTypesRegistry.start(), @@ -435,7 +447,7 @@ export const npStart = { }, }, }, - fieldFormats: getFieldFormatsRegistry(mockCore), + fieldFormats: getFieldFormatsRegistry(mockCoreStart), }, share: { toggleShareContextMenu: () => {}, @@ -457,6 +469,16 @@ export const npStart = { getTriggerActions: sinon.fake(), getTriggerCompatibleActions: sinon.fake(), }, + visualizations: { + get: sinon.fake(), + all: sinon.fake(), + getAliases: sinon.fake(), + savedVisualizationsLoader: {}, + showNewVisModal: sinon.fake(), + createVis: sinon.fake(), + convertFromSerializedVis: sinon.fake(), + convertToSerializedVis: sinon.fake(), + }, navigation: { ui: { TopNavMenu: mockComponent, @@ -483,23 +505,15 @@ export function __setup__(coreSetup) { // bootstrap an LP plugin outside of tests) npSetup.core.application.register = () => {}; - // Services that need to be set in the legacy platform since the legacy data plugin - // which previously provided them has been removed. - setInjectedMetadata(npSetup.core.injectedMetadata); + // Services that need to be set in the legacy platform since the legacy data + // & vis plugins which previously provided them have been removed. + setSetupServices(npSetup); } export function __start__(coreStart) { npStart.core = coreStart; - // Services that need to be set in the legacy platform since the legacy data plugin - // which previously provided them has been removed. - setHttp(npStart.core.http); - setNotifications(npStart.core.notifications); - setOverlays(npStart.core.overlays); - setUiSettings(npStart.core.uiSettings); - setFieldFormats(npStart.plugins.data.fieldFormats); - setIndexPatterns(npStart.plugins.data.indexPatterns); - setQueryService(npStart.plugins.data.query); - setSearchService(npStart.plugins.data.search); - setAggs(npStart.plugins.data.search.aggs); + // Services that need to be set in the legacy platform since the legacy data + // & vis plugins which previously provided them have been removed. + setStartServices(npStart); } diff --git a/src/legacy/ui/public/new_platform/new_platform.test.ts b/src/legacy/ui/public/new_platform/new_platform.test.ts index dd41093f3a1f0..1629aac588a61 100644 --- a/src/legacy/ui/public/new_platform/new_platform.test.ts +++ b/src/legacy/ui/public/new_platform/new_platform.test.ts @@ -20,19 +20,8 @@ jest.mock('history'); import { setRootControllerMock, historyMock } from './new_platform.test.mocks'; -import { - legacyAppRegister, - __reset__, - __setup__, - __start__, - PluginsSetup, - PluginsStart, -} from './new_platform'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import * as dataServices from '../../../../plugins/data/public/services'; -import { LegacyCoreSetup, LegacyCoreStart } from '../../../../core/public'; +import { legacyAppRegister, __reset__, __setup__, __start__ } from './new_platform'; import { coreMock } from '../../../../core/public/mocks'; -import { npSetup, npStart } from './__mocks__'; describe('ui/new_platform', () => { describe('legacyAppRegister', () => { @@ -119,25 +108,4 @@ describe('ui/new_platform', () => { expect(unmountMock).toHaveBeenCalled(); }); }); - - describe('service getters', () => { - const services: Record = dataServices; - const getters = Object.keys(services).filter(k => k.substring(0, 3) === 'get'); - - getters.forEach(g => { - it(`sets a value for ${g}`, () => { - __reset__(); - __setup__( - (coreMock.createSetup() as unknown) as LegacyCoreSetup, - (npSetup.plugins as unknown) as PluginsSetup - ); - __start__( - (coreMock.createStart() as unknown) as LegacyCoreStart, - (npStart.plugins as unknown) as PluginsStart - ); - - expect(services[g]()).toBeDefined(); - }); - }); - }); }); diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index 4fa368af313b4..b4b5099081759 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -23,6 +23,7 @@ import { UiActionsStart, UiActionsSetup } from 'src/plugins/ui_actions/public'; import { EmbeddableStart, EmbeddableSetup } from 'src/plugins/embeddable/public'; import { createBrowserHistory } from 'history'; import { DashboardStart } from '../../../../plugins/dashboard/public'; +import { setSetupServices, setStartServices } from './set_services'; import { LegacyCoreSetup, LegacyCoreStart, @@ -31,18 +32,6 @@ import { ScopedHistory, } from '../../../../core/public'; import { Plugin as DataPlugin } from '../../../../plugins/data/public'; -import { - setFieldFormats, - setIndexPatterns, - setInjectedMetadata, - setHttp, - setNotifications, - setOverlays, - setQueryService, - setSearchService, - setUiSettings, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../plugins/data/public/services'; import { Plugin as ExpressionsPlugin } from '../../../../plugins/expressions/public'; import { Setup as InspectorSetup, @@ -71,6 +60,10 @@ import { SavedObjectsManagementPluginSetup, SavedObjectsManagementPluginStart, } from '../../../../plugins/saved_objects_management/public'; +import { + VisualizationsSetup, + VisualizationsStart, +} from '../../../../plugins/visualizations/public'; export interface PluginsSetup { bfetch: BfetchPublicSetup; @@ -90,6 +83,7 @@ export interface PluginsSetup { management: ManagementSetup; visTypeVega: VisTypeVegaSetup; discover: DiscoverSetup; + visualizations: VisualizationsSetup; telemetry?: TelemetryPluginSetup; savedObjectsManagement: SavedObjectsManagementPluginSetup; } @@ -109,6 +103,7 @@ export interface PluginsStart { management: ManagementStart; advancedSettings: AdvancedSettingsStart; discover: DiscoverStart; + visualizations: VisualizationsStart; telemetry?: TelemetryPluginStart; dashboard: DashboardStart; savedObjectsManagement: SavedObjectsManagementPluginStart; @@ -143,25 +138,18 @@ export function __setup__(coreSetup: LegacyCoreSetup, plugins: PluginsSetup) { // Setup compatibility layer for AppService in legacy platform npSetup.core.application.register = legacyAppRegister; - // Services that need to be set in the legacy platform since the legacy data plugin - // which previously provided them has been removed. - setInjectedMetadata(npSetup.core.injectedMetadata); + // Services that need to be set in the legacy platform since the legacy data + // & vis plugins which previously provided them have been removed. + setSetupServices(npSetup); } export function __start__(coreStart: LegacyCoreStart, plugins: PluginsStart) { npStart.core = coreStart; npStart.plugins = plugins; - // Services that need to be set in the legacy platform since the legacy data plugin - // which previously provided them has been removed. - setHttp(npStart.core.http); - setNotifications(npStart.core.notifications); - setOverlays(npStart.core.overlays); - setUiSettings(npStart.core.uiSettings); - setFieldFormats(npStart.plugins.data.fieldFormats); - setIndexPatterns(npStart.plugins.data.indexPatterns); - setQueryService(npStart.plugins.data.query); - setSearchService(npStart.plugins.data.search); + // Services that need to be set in the legacy platform since the legacy data + // & vis plugins which previously provided them have been removed. + setStartServices(npStart); } /** Flag used to ensure `legacyAppRegister` is only called once. */ diff --git a/src/legacy/ui/public/new_platform/set_services.test.ts b/src/legacy/ui/public/new_platform/set_services.test.ts new file mode 100644 index 0000000000000..25a4524925169 --- /dev/null +++ b/src/legacy/ui/public/new_platform/set_services.test.ts @@ -0,0 +1,53 @@ +/* + * 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 { __reset__, __setup__, __start__, PluginsSetup, PluginsStart } from './new_platform'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import * as dataServices from '../../../../plugins/data/public/services'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import * as visualizationsServices from '../../../../plugins/visualizations/public/services'; +import { LegacyCoreSetup, LegacyCoreStart } from '../../../../core/public'; +import { coreMock } from '../../../../core/public/mocks'; +import { npSetup, npStart } from './__mocks__'; + +describe('ui/new_platform', () => { + describe('set service getters', () => { + const testServiceGetters = (name: string, services: Record) => { + const getters = Object.keys(services).filter(k => k.substring(0, 3) === 'get'); + getters.forEach(g => { + it(`ui/new_platform sets a value for ${name} getter ${g}`, () => { + __reset__(); + __setup__( + (coreMock.createSetup() as unknown) as LegacyCoreSetup, + (npSetup.plugins as unknown) as PluginsSetup + ); + __start__( + (coreMock.createStart() as unknown) as LegacyCoreStart, + (npStart.plugins as unknown) as PluginsStart + ); + + expect(services[g]()).toBeDefined(); + }); + }); + }; + + testServiceGetters('data', dataServices); + testServiceGetters('visualizations', visualizationsServices); + }); +}); diff --git a/src/legacy/ui/public/new_platform/set_services.ts b/src/legacy/ui/public/new_platform/set_services.ts new file mode 100644 index 0000000000000..8cf015d5dff5c --- /dev/null +++ b/src/legacy/ui/public/new_platform/set_services.ts @@ -0,0 +1,83 @@ +/* + * 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 { pick } from 'lodash'; + +import { PluginsSetup, PluginsStart } from './new_platform'; +import { LegacyCoreSetup, LegacyCoreStart } from '../../../../core/public'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import * as dataServices from '../../../../plugins/data/public/services'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import * as visualizationsServices from '../../../../plugins/visualizations/public/services'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { createSavedVisLoader } from '../../../../plugins/visualizations/public/saved_visualizations/saved_visualizations'; + +interface NpSetup { + core: LegacyCoreSetup; + plugins: PluginsSetup; +} + +interface NpStart { + core: LegacyCoreStart; + plugins: PluginsStart; +} + +export function setSetupServices(npSetup: NpSetup) { + // Services that need to be set in the legacy platform since the legacy data plugin + // which previously provided them has been removed. + dataServices.setInjectedMetadata(npSetup.core.injectedMetadata); + visualizationsServices.setUISettings(npSetup.core.uiSettings); + visualizationsServices.setUsageCollector(npSetup.plugins.usageCollection); +} + +export function setStartServices(npStart: NpStart) { + // Services that need to be set in the legacy platform since the legacy data plugin + // which previously provided them has been removed. + dataServices.setHttp(npStart.core.http); + dataServices.setNotifications(npStart.core.notifications); + dataServices.setOverlays(npStart.core.overlays); + dataServices.setUiSettings(npStart.core.uiSettings); + dataServices.setFieldFormats(npStart.plugins.data.fieldFormats); + dataServices.setIndexPatterns(npStart.plugins.data.indexPatterns); + dataServices.setQueryService(npStart.plugins.data.query); + dataServices.setSearchService(npStart.plugins.data.search); + visualizationsServices.setI18n(npStart.core.i18n); + visualizationsServices.setTypes( + pick(npStart.plugins.visualizations, ['get', 'all', 'getAliases']) + ); + visualizationsServices.setCapabilities(npStart.core.application.capabilities); + visualizationsServices.setHttp(npStart.core.http); + visualizationsServices.setSavedObjects(npStart.core.savedObjects); + visualizationsServices.setIndexPatterns(npStart.plugins.data.indexPatterns); + visualizationsServices.setFilterManager(npStart.plugins.data.query.filterManager); + visualizationsServices.setExpressions(npStart.plugins.expressions); + visualizationsServices.setUiActions(npStart.plugins.uiActions); + visualizationsServices.setTimeFilter(npStart.plugins.data.query.timefilter.timefilter); + visualizationsServices.setAggs(npStart.plugins.data.search.aggs); + visualizationsServices.setOverlays(npStart.core.overlays); + visualizationsServices.setChrome(npStart.core.chrome); + const savedVisualizationsLoader = createSavedVisLoader({ + savedObjectsClient: npStart.core.savedObjects.client, + indexPatterns: npStart.plugins.data.indexPatterns, + chrome: npStart.core.chrome, + overlays: npStart.core.overlays, + visualizationTypes: visualizationsServices.getTypes(), + }); + visualizationsServices.setSavedVisualizationsLoader(savedVisualizationsLoader); +} diff --git a/src/legacy/ui/public/styles/_legacy/components/_index.scss b/src/legacy/ui/public/styles/_legacy/components/_index.scss index 4a50a0430d559..cfae0700bb71e 100644 --- a/src/legacy/ui/public/styles/_legacy/components/_index.scss +++ b/src/legacy/ui/public/styles/_legacy/components/_index.scss @@ -7,7 +7,6 @@ @import './navbar'; @import './config'; @import './pagination'; -@import './sidebar'; @import './spinner'; @import './table'; @import './truncate'; diff --git a/src/legacy/ui/public/styles/_legacy/components/_sidebar.scss b/src/legacy/ui/public/styles/_legacy/components/_sidebar.scss deleted file mode 100644 index d44129b6ec849..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_sidebar.scss +++ /dev/null @@ -1,127 +0,0 @@ -// ONLY USED IN DISCOVER - -.sidebar-container { - padding-left: 0 !important; - padding-right: 0 !important; - background-color: $euiColorLightestShade; - border-right-color: transparent; - border-bottom-color: transparent; - - .sidebar-well { - background-color: lightOrDarkTheme(tint($euiColorPrimary, 90%), $euiColorLightShade); - } - - .sidebar-list { - .sidebar-controls { - border-radius: $euiBorderRadius; - margin-right: -13px; - margin-top: $euiSizeXS / 2; - - .navbar-btn-link { - padding-left: $euiSizeS; - padding-right: $euiSizeS; - } - - .sidebar-controls-error { - cursor: default; - } - } - - ul { - list-style: none; - margin-bottom: 0; - } - - .sidebar-item { - border-top-color: transparent; - font-size: $euiFontSizeXS; - border-top: solid 1px transparent; - border-bottom: solid 1px transparent; - line-height: normal; - - label { - @include __legacyLabelStyles__bad; - margin-bottom: $euiSizeXS; - display: block; - } - - &.active { - background-color: shade($euiColorLightestShade, 10%); - color: $euiColorDarkestShade; - border-color: $euiColorLightShade; - } - } - - .sidebar-item-title, - .sidebar-item-text { - margin: 0; - padding: $euiSizeXS 0; - text-align: center; - width: 100%; - border: none; - border-radius: 0; - } - - .sidebar-item-title { - @include euiTextTruncate; - text-align: left; - - &.full-title { - white-space: normal; - } - } - - .sidebar-item-text { - background: $euiColorEmptyShade; - } - } - - .sidebar-list-header { - .sidebar-list-header-heading { - color: $euiColorDarkestShade; - border: 1px solid transparent; - } - - .sidebar-list-header-label { - padding-left: $euiSizeS; - font-size: $euiFontSizeXS; - line-height: $euiLineHeight; - font-weight: $euiFontWeightBold; - color: $euiColorDarkShade; - border-bottom: 1px solid $euiColorLightShade; - } - } - - .index-pattern { - font-weight: $euiFontWeightBold; - padding: $euiSizeXS $euiSizeS; - display: flex; - justify-content: space-between; - background-color: shadeOrTint($euiColorPrimary, 60%, 60%); - color: $euiColorEmptyShade; - line-height: $euiSizeL; - - .index-pattern-label { - font-size: $euiFontSizeS; - font-weight: $euiFontWeightBold; - margin: 0; - } - - > * { - flex: 0 1 auto; - align-self: center; - } - } -} - -.indexPattern__container { - display: flex; - align-items: center; - height: $euiSize * 3; - margin-top: -$euiSizeS; -} - -.indexPattern__triggerButton { - @include euiTitle('xs'); - line-height: $euiSizeXXL; -} diff --git a/src/legacy/ui/public/vis/map/service_settings.js b/src/legacy/ui/public/vis/map/service_settings.js index 9f3d21831e3da..a014aeb182c67 100644 --- a/src/legacy/ui/public/vis/map/service_settings.js +++ b/src/legacy/ui/public/vis/map/service_settings.js @@ -53,6 +53,10 @@ uiModules tileApiUrl: mapConfig.emsTileApiUrl, htmlSanitizer: $sanitize, landingPageUrl: mapConfig.emsLandingPageUrl, + // Wrap to avoid errors passing window fetch + fetchFunction: function(...args) { + return fetch(...args); + }, }); } diff --git a/src/legacy/ui/public/visualize/_index.scss b/src/legacy/ui/public/visualize/_index.scss index c528c1e37b412..d9761f741353b 100644 --- a/src/legacy/ui/public/visualize/_index.scss +++ b/src/legacy/ui/public/visualize/_index.scss @@ -1 +1 @@ -@import '../../../core_plugins/visualizations/public/np_ready/public/components/index'; +@import '../../../../plugins/visualizations/public/components/index'; diff --git a/src/legacy/ui/public/visualize/loader/pipeline_helpers/index.ts b/src/legacy/ui/public/visualize/loader/pipeline_helpers/index.ts index f19940726ef2d..fe7f239fbea3b 100644 --- a/src/legacy/ui/public/visualize/loader/pipeline_helpers/index.ts +++ b/src/legacy/ui/public/visualize/loader/pipeline_helpers/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { buildPipeline } from '../../../../../core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline'; +export { buildPipeline } from '../../../../../../plugins/visualizations/public/legacy/build_pipeline'; diff --git a/src/legacy/ui/ui_bundles/ui_bundles_controller.js b/src/legacy/ui/ui_bundles/ui_bundles_controller.js index 1a78569e874f2..7afa283af83e0 100644 --- a/src/legacy/ui/ui_bundles/ui_bundles_controller.js +++ b/src/legacy/ui/ui_bundles/ui_bundles_controller.js @@ -73,6 +73,7 @@ export class UiBundlesController { this._workingDir = config.get('optimize.bundleDir'); this._env = config.get('env.name'); + this._validateSyntaxOfNodeModules = config.get('optimize.validateSyntaxOfNodeModules'); this._context = { env: config.get('env.name'), sourceMaps: config.get('optimize.sourceMaps'), @@ -135,6 +136,10 @@ export class UiBundlesController { return this._env === 'development'; } + shouldValidateSyntaxOfNodeModules() { + return !!this._validateSyntaxOfNodeModules; + } + getWebpackPluginProviders() { return this._webpackPluginProviders || []; } diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index 0a1b95c23450b..99560b0bf653f 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -23,8 +23,6 @@ import { resolve } from 'path'; import { i18n } from '@kbn/i18n'; import * as UiSharedDeps from '@kbn/ui-shared-deps'; import { AppBootstrap } from './bootstrap'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { fromRoot } from '../../../core/server/utils'; import { getApmConfig } from '../apm'; import { DllCompiler } from '../../../optimize/dynamic_dll_plugin'; @@ -43,11 +41,6 @@ export function uiRenderMixin(kbnServer, server, config) { // render all views from ./views server.setupViews(resolve(__dirname, 'views')); - server.exposeStaticDir( - '/node_modules/@kbn/ui-framework/dist/{path*}', - fromRoot('node_modules/@kbn/ui-framework/dist') - ); - const translationsCache = { translations: null, hash: null }; server.route({ path: '/translations/{locale}.json', diff --git a/src/optimize/dynamic_dll_plugin/dll_config_model.js b/src/optimize/dynamic_dll_plugin/dll_config_model.js index 9ca6071b8f515..eec369b194fef 100644 --- a/src/optimize/dynamic_dll_plugin/dll_config_model.js +++ b/src/optimize/dynamic_dll_plugin/dll_config_model.js @@ -28,6 +28,7 @@ import * as UiSharedDeps from '@kbn/ui-shared-deps'; function generateDLL(config) { const { dllAlias, + dllValidateSyntax, dllNoParseRules, dllContext, dllEntry, @@ -44,6 +45,22 @@ function generateDLL(config) { const BABEL_PRESET_PATH = require.resolve('@kbn/babel-preset/webpack_preset'); const BABEL_EXCLUDE_RE = [/[\/\\](webpackShims|node_modules|bower_components)[\/\\]/]; + /** + * Wrap plugin loading in a function so that we can require + * `@kbn/optimizer` only when absolutely necessary since we + * don't ship this package in the distributable but this code + * is still shipped, though it's not used. + */ + const getValidateSyntaxPlugins = () => { + if (!dllValidateSyntax) { + return []; + } + + // only require @kbn/optimizer + const { DisallowedSyntaxPlugin } = require('@kbn/optimizer'); + return [new DisallowedSyntaxPlugin()]; + }; + return { entry: dllEntry, context: dllContext, @@ -140,6 +157,7 @@ function generateDLL(config) { new MiniCssExtractPlugin({ filename: dllStyleFilename, }), + ...getValidateSyntaxPlugins(), ], // Single runtime for the dll bundles which assures that common transient dependencies won't be evaluated twice. // The module cache will be shared, even when module code may be duplicated across chunks. @@ -163,6 +181,7 @@ function generateDLL(config) { function extendRawConfig(rawConfig) { // Build all extended configs from raw config const dllAlias = rawConfig.uiBundles.getAliases(); + const dllValidateSyntax = rawConfig.uiBundles.shouldValidateSyntaxOfNodeModules(); const dllNoParseRules = rawConfig.uiBundles.getWebpackNoParseRules(); const dllDevMode = rawConfig.uiBundles.isDevMode(); const dllContext = rawConfig.context; @@ -195,6 +214,7 @@ function extendRawConfig(rawConfig) { // Export dll config map return { dllAlias, + dllValidateSyntax, dllNoParseRules, dllDevMode, dllContext, diff --git a/src/plugins/dashboard/public/url_generator.test.ts b/src/plugins/dashboard/public/url_generator.test.ts index 5dfc47b694f60..d48aacc1d8c1e 100644 --- a/src/plugins/dashboard/public/url_generator.test.ts +++ b/src/plugins/dashboard/public/url_generator.test.ts @@ -21,6 +21,7 @@ import { createDirectAccessDashboardLinkGenerator } from './url_generator'; import { hashedItemStore } from '../../kibana_utils/public'; // eslint-disable-next-line import { mockStorage } from '../../kibana_utils/public/storage/hashed_item_store/mock'; +import { esFilters } from '../../data/public'; const APP_BASE_PATH: string = 'xyz/app/kibana'; @@ -50,12 +51,13 @@ describe('dashboard url generator', () => { ); }); - test('creates a link with filters, time range and query to a saved object', async () => { + test('creates a link with filters, time range, refresh interval and query to a saved object', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false }) ); const url = await generator.createUrl!({ timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, + refreshInterval: { pause: false, value: 300 }, dashboardId: '123', filters: [ { @@ -66,11 +68,22 @@ describe('dashboard url generator', () => { }, query: { query: 'hi' }, }, + { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'hi' }, + $state: { + store: esFilters.FilterStateStore.GLOBAL_STATE, + }, + }, ], query: { query: 'bye', language: 'kuery' }, }); expect(url).toMatchInlineSnapshot( - `"xyz/app/kibana#/dashboard/123?_a=(filters:!((meta:(alias:!n,disabled:!f,negate:!f),query:(query:hi))),query:(language:kuery,query:bye))&_g=(time:(from:now-15m,mode:relative,to:now))"` + `"xyz/app/kibana#/dashboard/123?_a=(filters:!((meta:(alias:!n,disabled:!f,negate:!f),query:(query:hi))),query:(language:kuery,query:bye))&_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!f,negate:!f),query:(query:hi))),refreshInterval:(pause:!f,value:300),time:(from:now-15m,mode:relative,to:now))"` ); }); diff --git a/src/plugins/dashboard/public/url_generator.ts b/src/plugins/dashboard/public/url_generator.ts index 5f1255bc9d45f..0fdf395e75bca 100644 --- a/src/plugins/dashboard/public/url_generator.ts +++ b/src/plugins/dashboard/public/url_generator.ts @@ -17,7 +17,14 @@ * under the License. */ -import { TimeRange, Filter, Query } from '../../data/public'; +import { + TimeRange, + Filter, + Query, + esFilters, + QueryState, + RefreshInterval, +} from '../../data/public'; import { setStateToKbnUrl } from '../../kibana_utils/public'; import { UrlGeneratorsDefinition, UrlGeneratorState } from '../../share/public'; @@ -36,10 +43,15 @@ export type DashboardAppLinkGeneratorState = UrlGeneratorState<{ * Optionally set the time range in the time picker. */ timeRange?: TimeRange; + + /** + * Optionally set the refresh interval. + */ + refreshInterval?: RefreshInterval; + /** * Optionally apply filers. NOTE: if given and used in conjunction with `dashboardId`, and the - * saved dashboard has filters saved with it, this will _replace_ those filters. This will set - * app filters, not global filters. + * saved dashboard has filters saved with it, this will _replace_ those filters. */ filters?: Filter[]; /** @@ -64,21 +76,32 @@ export const createDirectAccessDashboardLinkGenerator = ( const appBasePath = startServices.appBasePath; const hash = state.dashboardId ? `dashboard/${state.dashboardId}` : `dashboard`; + const cleanEmptyKeys = (stateObj: Record) => { + Object.keys(stateObj).forEach(key => { + if (stateObj[key] === undefined) { + delete stateObj[key]; + } + }); + return stateObj; + }; + const appStateUrl = setStateToKbnUrl( STATE_STORAGE_KEY, - { + cleanEmptyKeys({ query: state.query, - filters: state.filters, - }, + filters: state.filters?.filter(f => !esFilters.isFilterPinned(f)), + }), { useHash }, `${appBasePath}#/${hash}` ); - return setStateToKbnUrl( + return setStateToKbnUrl( GLOBAL_STATE_STORAGE_KEY, - { + cleanEmptyKeys({ time: state.timeRange, - }, + filters: state.filters?.filter(f => esFilters.isFilterPinned(f)), + refreshInterval: state.refreshInterval, + }), { useHash }, appStateUrl ); diff --git a/src/plugins/data/common/query/filter_manager/compare_filters.test.ts b/src/plugins/data/common/query/filter_manager/compare_filters.test.ts index b0bb2f754d6cf..0c3947ade8221 100644 --- a/src/plugins/data/common/query/filter_manager/compare_filters.test.ts +++ b/src/plugins/data/common/query/filter_manager/compare_filters.test.ts @@ -197,6 +197,22 @@ describe('filter manager utilities', () => { expect(compareFilters([f1], [f2], COMPARE_ALL_OPTIONS)).toBeTruthy(); }); + test('should compare alias with alias true', () => { + const f1 = { + $state: { store: FilterStateStore.GLOBAL_STATE }, + ...buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index', ''), + }; + const f2 = { + $state: { store: FilterStateStore.GLOBAL_STATE }, + ...buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index', ''), + }; + + f2.meta.alias = 'wassup'; + f2.meta.alias = 'dog'; + + expect(compareFilters([f1], [f2], { alias: true })).toBeFalsy(); + }); + test('should compare alias with COMPARE_ALL_OPTIONS', () => { const f1 = { $state: { store: FilterStateStore.GLOBAL_STATE }, diff --git a/src/plugins/data/common/query/filter_manager/compare_filters.ts b/src/plugins/data/common/query/filter_manager/compare_filters.ts index e047d5e0665d5..3be52a9a60977 100644 --- a/src/plugins/data/common/query/filter_manager/compare_filters.ts +++ b/src/plugins/data/common/query/filter_manager/compare_filters.ts @@ -46,7 +46,7 @@ const mapFilter = ( if (comparators.negate) cleaned.negate = filter.meta && Boolean(filter.meta.negate); if (comparators.disabled) cleaned.disabled = filter.meta && Boolean(filter.meta.disabled); - if (comparators.disabled) cleaned.alias = filter.meta?.alias; + if (comparators.alias) cleaned.alias = filter.meta?.alias; return cleaned; }; diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index fc5dde94fa851..26587470adfd9 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -121,7 +121,10 @@ export class DataPublicPlugin implements Plugin Record<"buckets" | "metrics", string>; + aggGroupNamesMap: () => Record<"metrics" | "buckets", string>; aggTypeFilters: import("./search/aggs/filter/agg_type_filters").AggTypeFilters; CidrMask: typeof CidrMask; convertDateRangeToString: typeof convertDateRangeToString; diff --git a/src/plugins/data/public/query/state_sync/connect_to_query_state.test.ts b/src/plugins/data/public/query/state_sync/connect_to_query_state.test.ts index 5da929c441cde..06e4c1c8be6d5 100644 --- a/src/plugins/data/public/query/state_sync/connect_to_query_state.test.ts +++ b/src/plugins/data/public/query/state_sync/connect_to_query_state.test.ts @@ -463,3 +463,97 @@ describe('connect_to_app_state', () => { }); }); }); + +describe('filters with different state', () => { + let queryServiceStart: QueryStart; + let filterManager: FilterManager; + let state: BaseStateContainer; + let stateSub: Subscription; + let stateChangeTriggered = jest.fn(); + let filterManagerChangeSub: Subscription; + let filterManagerChangeTriggered = jest.fn(); + + let filter: Filter; + + beforeEach(() => { + const queryService = new QueryService(); + queryService.setup({ + uiSettings: setupMock.uiSettings, + storage: new Storage(new StubBrowserStorage()), + }); + queryServiceStart = queryService.start(startMock.savedObjects); + filterManager = queryServiceStart.filterManager; + + state = createStateContainer({}); + stateChangeTriggered = jest.fn(); + stateSub = state.state$.subscribe(stateChangeTriggered); + + filterManagerChangeTriggered = jest.fn(); + filterManagerChangeSub = filterManager.getUpdates$().subscribe(filterManagerChangeTriggered); + + filter = getFilter(FilterStateStore.GLOBAL_STATE, true, true, 'key1', 'value1'); + }); + + // applies filter state changes, changes only internal $state.store value + function runChanges() { + filter = { ...filter, $state: { store: FilterStateStore.GLOBAL_STATE } }; + + state.set({ + filters: [filter], + }); + + filter = { ...filter, $state: { store: FilterStateStore.APP_STATE } }; + + state.set({ + filters: [filter], + }); + + filter = { ...filter }; + delete filter.$state; + + state.set({ + filters: [filter], + }); + } + + test('when syncing all filters, changes to filter.state$ should be taken into account', () => { + const stop = connectToQueryState(queryServiceStart, state, { + filters: true, + }); + + runChanges(); + + expect(filterManagerChangeTriggered).toBeCalledTimes(3); + + stop(); + }); + + test('when syncing app state filters, changes to filter.state$ should be ignored', () => { + const stop = connectToQueryState(queryServiceStart, state, { + filters: FilterStateStore.APP_STATE, + }); + + runChanges(); + + expect(filterManagerChangeTriggered).toBeCalledTimes(1); + + stop(); + }); + + test('when syncing global state filters, changes to filter.state$ should be ignored', () => { + const stop = connectToQueryState(queryServiceStart, state, { + filters: FilterStateStore.GLOBAL_STATE, + }); + + runChanges(); + + expect(filterManagerChangeTriggered).toBeCalledTimes(1); + + stop(); + }); + + afterEach(() => { + stateSub.unsubscribe(); + filterManagerChangeSub.unsubscribe(); + }); +}); diff --git a/src/plugins/data/public/query/state_sync/connect_to_query_state.ts b/src/plugins/data/public/query/state_sync/connect_to_query_state.ts index 331d8969f2483..3256c1cbd65a1 100644 --- a/src/plugins/data/public/query/state_sync/connect_to_query_state.ts +++ b/src/plugins/data/public/query/state_sync/connect_to_query_state.ts @@ -91,7 +91,10 @@ export const connectToQueryState = ( } else if (syncConfig.filters === FilterStateStore.GLOBAL_STATE) { if ( !initialState.filters || - !compareFilters(initialState.filters, filterManager.getGlobalFilters(), COMPARE_ALL_OPTIONS) + !compareFilters(initialState.filters, filterManager.getGlobalFilters(), { + ...COMPARE_ALL_OPTIONS, + state: false, + }) ) { initialState.filters = filterManager.getGlobalFilters(); initialDirty = true; @@ -99,7 +102,10 @@ export const connectToQueryState = ( } else if (syncConfig.filters === FilterStateStore.APP_STATE) { if ( !initialState.filters || - !compareFilters(initialState.filters, filterManager.getAppFilters(), COMPARE_ALL_OPTIONS) + !compareFilters(initialState.filters, filterManager.getAppFilters(), { + ...COMPARE_ALL_OPTIONS, + state: false, + }) ) { initialState.filters = filterManager.getAppFilters(); initialDirty = true; @@ -173,11 +179,21 @@ export const connectToQueryState = ( filterManager.setFilters(_.cloneDeep(filters)); } } else if (syncConfig.filters === FilterStateStore.APP_STATE) { - if (!compareFilters(filters, filterManager.getAppFilters(), COMPARE_ALL_OPTIONS)) { + if ( + !compareFilters(filters, filterManager.getAppFilters(), { + ...COMPARE_ALL_OPTIONS, + state: false, + }) + ) { filterManager.setAppFilters(_.cloneDeep(filters)); } } else if (syncConfig.filters === FilterStateStore.GLOBAL_STATE) { - if (!compareFilters(filters, filterManager.getGlobalFilters(), COMPARE_ALL_OPTIONS)) { + if ( + !compareFilters(filters, filterManager.getGlobalFilters(), { + ...COMPARE_ALL_OPTIONS, + state: false, + }) + ) { filterManager.setGlobalFilters(_.cloneDeep(filters)); } } diff --git a/src/plugins/data/public/search/aggs/agg_types.ts b/src/plugins/data/public/search/aggs/agg_types.ts index 73c6a5046fd23..556f6b0c93c41 100644 --- a/src/plugins/data/public/search/aggs/agg_types.ts +++ b/src/plugins/data/public/search/aggs/agg_types.ts @@ -17,7 +17,8 @@ * under the License. */ -import { IUiSettingsClient } from 'src/core/public'; +import { IUiSettingsClient, NotificationsSetup } from 'src/core/public'; +import { QuerySetup } from '../../query/query_service'; import { countMetricAgg } from './metrics/count'; import { avgMetricAgg } from './metrics/avg'; @@ -36,10 +37,10 @@ import { derivativeMetricAgg } from './metrics/derivative'; import { cumulativeSumMetricAgg } from './metrics/cumulative_sum'; import { movingAvgMetricAgg } from './metrics/moving_avg'; import { serialDiffMetricAgg } from './metrics/serial_diff'; -import { dateHistogramBucketAgg } from './buckets/date_histogram'; -import { histogramBucketAgg } from './buckets/histogram'; +import { getDateHistogramBucketAgg } from './buckets/date_histogram'; +import { getHistogramBucketAgg } from './buckets/histogram'; import { rangeBucketAgg } from './buckets/range'; -import { dateRangeBucketAgg } from './buckets/date_range'; +import { getDateRangeBucketAgg } from './buckets/date_range'; import { ipRangeBucketAgg } from './buckets/ip_range'; import { termsBucketAgg } from './buckets/terms'; import { filterBucketAgg } from './buckets/filter'; @@ -52,44 +53,47 @@ import { bucketAvgMetricAgg } from './metrics/bucket_avg'; import { bucketMinMetricAgg } from './metrics/bucket_min'; import { bucketMaxMetricAgg } from './metrics/bucket_max'; -export function getAggTypes(deps: { uiSettings: IUiSettingsClient }) { - const { uiSettings } = deps; - return { - metrics: [ - countMetricAgg, - avgMetricAgg, - sumMetricAgg, - medianMetricAgg, - minMetricAgg, - maxMetricAgg, - stdDeviationMetricAgg, - cardinalityMetricAgg, - percentilesMetricAgg, - percentileRanksMetricAgg, - topHitMetricAgg, - derivativeMetricAgg, - cumulativeSumMetricAgg, - movingAvgMetricAgg, - serialDiffMetricAgg, - bucketAvgMetricAgg, - bucketSumMetricAgg, - bucketMinMetricAgg, - bucketMaxMetricAgg, - geoBoundsMetricAgg, - geoCentroidMetricAgg, - ], - buckets: [ - dateHistogramBucketAgg, - histogramBucketAgg, - rangeBucketAgg, - dateRangeBucketAgg, - ipRangeBucketAgg, - termsBucketAgg, - filterBucketAgg, - getFiltersBucketAgg({ uiSettings }), - significantTermsBucketAgg, - geoHashBucketAgg, - geoTileBucketAgg, - ], - }; +export interface AggTypesDependencies { + notifications: NotificationsSetup; + uiSettings: IUiSettingsClient; + query: QuerySetup; } + +export const getAggTypes = ({ notifications, uiSettings, query }: AggTypesDependencies) => ({ + metrics: [ + countMetricAgg, + avgMetricAgg, + sumMetricAgg, + medianMetricAgg, + minMetricAgg, + maxMetricAgg, + stdDeviationMetricAgg, + cardinalityMetricAgg, + percentilesMetricAgg, + percentileRanksMetricAgg, + topHitMetricAgg, + derivativeMetricAgg, + cumulativeSumMetricAgg, + movingAvgMetricAgg, + serialDiffMetricAgg, + bucketAvgMetricAgg, + bucketSumMetricAgg, + bucketMinMetricAgg, + bucketMaxMetricAgg, + geoBoundsMetricAgg, + geoCentroidMetricAgg, + ], + buckets: [ + getDateHistogramBucketAgg({ uiSettings, query }), + getHistogramBucketAgg({ uiSettings, notifications }), + rangeBucketAgg, + getDateRangeBucketAgg({ uiSettings }), + ipRangeBucketAgg, + termsBucketAgg, + filterBucketAgg, + getFiltersBucketAgg({ uiSettings }), + significantTermsBucketAgg, + geoHashBucketAgg, + geoTileBucketAgg, + ], +}); diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts index 12817a9ba1159..def354c4557cb 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts @@ -22,23 +22,35 @@ import { createFilterDateHistogram } from './date_histogram'; import { intervalOptions } from '../_interval_options'; import { AggConfigs } from '../../agg_configs'; import { mockDataServices, mockAggTypesRegistry } from '../../test_helpers'; -import { dateHistogramBucketAgg, IBucketDateHistogramAggConfig } from '../date_histogram'; +import { + getDateHistogramBucketAgg, + DateHistogramBucketAggDependencies, + IBucketDateHistogramAggConfig, +} from '../date_histogram'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { RangeFilter } from '../../../../../common'; +import { coreMock } from '../../../../../../../core/public/mocks'; +import { queryServiceMock } from '../../../../query/mocks'; describe('AggConfig Filters', () => { describe('date_histogram', () => { - beforeEach(() => { - mockDataServices(); - }); - - const typesRegistry = mockAggTypesRegistry([dateHistogramBucketAgg]); - + let aggTypesDependencies: DateHistogramBucketAggDependencies; let agg: IBucketDateHistogramAggConfig; let filter: RangeFilter; let bucketStart: any; let field: any; + beforeEach(() => { + const { uiSettings } = coreMock.createSetup(); + + aggTypesDependencies = { + uiSettings, + query: queryServiceMock.createSetupContract(), + }; + + mockDataServices(); + }); + const init = (interval: string = 'auto', duration: any = moment.duration(15, 'minutes')) => { field = { name: 'date', @@ -61,7 +73,7 @@ describe('AggConfig Filters', () => { params: { field: field.name, interval, customInterval: '5d' }, }, ], - { typesRegistry } + { typesRegistry: mockAggTypesRegistry([getDateHistogramBucketAgg(aggTypesDependencies)]) } ); const bucketKey = 1422579600000; diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts index d18a30fb6c6f8..6a03176959a83 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts @@ -18,7 +18,7 @@ */ import moment from 'moment'; -import { dateRangeBucketAgg } from '../date_range'; +import { getDateRangeBucketAgg, DateRangeBucketAggDependencies } from '../date_range'; import { createFilterDateRange } from './date_range'; import { FieldFormatsGetConfigFn } from '../../../../../common'; import { DateFormat } from '../../../../field_formats'; @@ -26,10 +26,20 @@ import { AggConfigs } from '../../agg_configs'; import { mockAggTypesRegistry } from '../../test_helpers'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../_bucket_agg_type'; +import { coreMock } from '../../../../../../../core/public/mocks'; describe('AggConfig Filters', () => { describe('Date range', () => { - const typesRegistry = mockAggTypesRegistry([dateRangeBucketAgg]); + let aggTypesDependencies: DateRangeBucketAggDependencies; + + beforeEach(() => { + const { uiSettings } = coreMock.createSetup(); + + aggTypesDependencies = { + uiSettings, + }; + }); + const getConfig = (() => {}) as FieldFormatsGetConfigFn; const getAggConfigs = () => { const field = { @@ -57,7 +67,7 @@ describe('AggConfig Filters', () => { }, }, ], - { typesRegistry } + { typesRegistry: mockAggTypesRegistry([getDateRangeBucketAgg(aggTypesDependencies)]) } ); }; diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts index 33ab1ce8186a1..32ada8d57c768 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts @@ -17,25 +17,24 @@ * under the License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { coreMock } from '../../../../../../../../src/core/public/mocks'; -import { getFiltersBucketAgg } from '../filters'; +import { getFiltersBucketAgg, FiltersBucketAggDependencies } from '../filters'; import { createFilterFilters } from './filters'; import { AggConfigs } from '../../agg_configs'; -import { mockDataServices, mockAggTypesRegistry } from '../../test_helpers'; +import { mockAggTypesRegistry } from '../../test_helpers'; import { IBucketAggConfig } from '../_bucket_agg_type'; +import { coreMock } from '../../../../../../../core/public/mocks'; describe('AggConfig Filters', () => { describe('filters', () => { + let aggTypesDependencies: FiltersBucketAggDependencies; + beforeEach(() => { - mockDataServices(); - }); + const { uiSettings } = coreMock.createSetup(); - const typesRegistry = mockAggTypesRegistry([ - getFiltersBucketAgg({ - uiSettings: coreMock.createSetup().uiSettings, - }), - ]); + aggTypesDependencies = { + uiSettings, + }; + }); const getAggConfigs = () => { const field = { @@ -65,7 +64,7 @@ describe('AggConfig Filters', () => { }, }, ], - { typesRegistry } + { typesRegistry: mockAggTypesRegistry([getFiltersBucketAgg(aggTypesDependencies)]) } ); }; it('should return a filters filter', () => { diff --git a/src/plugins/data/public/search/aggs/buckets/date_histogram.ts b/src/plugins/data/public/search/aggs/buckets/date_histogram.ts index d600b16f56764..7701f1bbcb4d0 100644 --- a/src/plugins/data/public/search/aggs/buckets/date_histogram.ts +++ b/src/plugins/data/public/search/aggs/buckets/date_histogram.ts @@ -17,9 +17,10 @@ * under the License. */ -import _ from 'lodash'; +import { get, noop, find, every } from 'lodash'; import moment from 'moment-timezone'; import { i18n } from '@kbn/i18n'; +import { IUiSettingsClient } from 'src/core/public'; import { TimeBuckets } from './lib/time_buckets'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; @@ -32,7 +33,8 @@ import { isMetricAggType } from '../metrics/metric_agg_type'; import { FIELD_FORMAT_IDS, KBN_FIELD_TYPES } from '../../../../common'; import { TimefilterContract } from '../../../query'; -import { getFieldFormats, getQueryService, getUiSettings } from '../../../../public/services'; +import { getFieldFormats } from '../../../../public/services'; +import { QuerySetup } from '../../../query/query_service'; const detectedTimezone = moment.tz.guess(); const tzOffset = moment().format('Z'); @@ -56,6 +58,11 @@ interface ITimeBuckets { getInterval: Function; } +export interface DateHistogramBucketAggDependencies { + uiSettings: IUiSettingsClient; + query: QuerySetup; +} + export interface IBucketDateHistogramAggConfig extends IBucketAggConfig { buckets: ITimeBuckets; } @@ -64,212 +71,214 @@ export function isDateHistogramBucketAggConfig(agg: any): agg is IBucketDateHist return Boolean(agg.buckets); } -export const dateHistogramBucketAgg = new BucketAggType({ - name: BUCKET_TYPES.DATE_HISTOGRAM, - title: i18n.translate('data.search.aggs.buckets.dateHistogramTitle', { - defaultMessage: 'Date Histogram', - }), - ordered: { - date: true, - }, - makeLabel(agg) { - let output: Record = {}; +export const getDateHistogramBucketAgg = ({ + uiSettings, + query, +}: DateHistogramBucketAggDependencies) => + new BucketAggType({ + name: BUCKET_TYPES.DATE_HISTOGRAM, + title: i18n.translate('data.search.aggs.buckets.dateHistogramTitle', { + defaultMessage: 'Date Histogram', + }), + ordered: { + date: true, + }, + makeLabel(agg) { + let output: Record = {}; - if (this.params) { - output = writeParams(this.params, agg); - } + if (this.params) { + output = writeParams(this.params, agg); + } - const field = agg.getFieldDisplayName(); - return i18n.translate('data.search.aggs.buckets.dateHistogramLabel', { - defaultMessage: '{fieldName} per {intervalDescription}', - values: { - fieldName: field, - intervalDescription: output.metricScaleText || output.bucketInterval.description, - }, - }); - }, - createFilter: createFilterDateHistogram, - decorateAggConfig() { - const uiSettings = getUiSettings(); - let buckets: any; + const field = agg.getFieldDisplayName(); + return i18n.translate('data.search.aggs.buckets.dateHistogramLabel', { + defaultMessage: '{fieldName} per {intervalDescription}', + values: { + fieldName: field, + intervalDescription: output.metricScaleText || output.bucketInterval.description, + }, + }); + }, + createFilter: createFilterDateHistogram, + decorateAggConfig() { + let buckets: any; - return { - buckets: { - configurable: true, - get() { - if (buckets) return buckets; + return { + buckets: { + configurable: true, + get() { + if (buckets) return buckets; - const { timefilter } = getQueryService().timefilter; - buckets = new TimeBuckets({ uiSettings }); - updateTimeBuckets(this, timefilter, buckets); + const { timefilter } = query.timefilter; + buckets = new TimeBuckets({ uiSettings }); + updateTimeBuckets(this, timefilter, buckets); - return buckets; - }, - } as any, - }; - }, - getFormat(agg) { - const DateFieldFormat = getFieldFormats().getType(FIELD_FORMAT_IDS.DATE); + return buckets; + }, + } as any, + }; + }, + getFormat(agg) { + const DateFieldFormat = getFieldFormats().getType(FIELD_FORMAT_IDS.DATE); - if (!DateFieldFormat) { - throw new Error('Unable to retrieve Date Field Format'); - } + if (!DateFieldFormat) { + throw new Error('Unable to retrieve Date Field Format'); + } - return new DateFieldFormat( + return new DateFieldFormat( + { + pattern: agg.buckets.getScaledDateFormat(), + }, + (key: string) => uiSettings.get(key) + ); + }, + params: [ { - pattern: agg.buckets.getScaledDateFormat(), + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.DATE, + default(agg: IBucketDateHistogramAggConfig) { + return agg.getIndexPattern().timeFieldName; + }, + onChange(agg: IBucketDateHistogramAggConfig) { + if (get(agg, 'params.interval') === 'auto' && !agg.fieldIsTimeField()) { + delete agg.params.interval; + } + }, }, - (key: string) => getUiSettings().get(key) - ); - }, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.DATE, - default(agg: IBucketDateHistogramAggConfig) { - return agg.getIndexPattern().timeFieldName; + { + name: 'timeRange', + default: null, + write: noop, }, - onChange(agg: IBucketDateHistogramAggConfig) { - if (_.get(agg, 'params.interval') === 'auto' && !agg.fieldIsTimeField()) { - delete agg.params.interval; - } + { + name: 'useNormalizedEsInterval', + default: true, + write: noop, }, - }, - { - name: 'timeRange', - default: null, - write: _.noop, - }, - { - name: 'useNormalizedEsInterval', - default: true, - write: _.noop, - }, - { - name: 'scaleMetricValues', - default: false, - write: _.noop, - advanced: true, - }, - { - name: 'interval', - deserialize(state: any, agg) { - // For upgrading from 7.0.x to 7.1.x - intervals are now stored as key of options or custom value - if (state === 'custom') { - return _.get(agg, 'params.customInterval'); - } + { + name: 'scaleMetricValues', + default: false, + write: noop, + advanced: true, + }, + { + name: 'interval', + deserialize(state: any, agg) { + // For upgrading from 7.0.x to 7.1.x - intervals are now stored as key of options or custom value + if (state === 'custom') { + return get(agg, 'params.customInterval'); + } - const interval = _.find(intervalOptions, { val: state }); + const interval = find(intervalOptions, { val: state }); - // For upgrading from 4.0.x to 4.1.x - intervals are now stored as 'y' instead of 'year', - // but this maps the old values to the new values - if (!interval && state === 'year') { - return 'y'; - } - return state; - }, - default: 'auto', - options: intervalOptions, - write(agg, output, aggs) { - const { timefilter } = getQueryService().timefilter; - updateTimeBuckets(agg, timefilter); + // For upgrading from 4.0.x to 4.1.x - intervals are now stored as 'y' instead of 'year', + // but this maps the old values to the new values + if (!interval && state === 'year') { + return 'y'; + } + return state; + }, + default: 'auto', + options: intervalOptions, + write(agg, output, aggs) { + const { timefilter } = query.timefilter; + updateTimeBuckets(agg, timefilter); - const { useNormalizedEsInterval, scaleMetricValues } = agg.params; - const interval = agg.buckets.getInterval(useNormalizedEsInterval); - output.bucketInterval = interval; - if (interval.expression === '0ms') { - // We are hitting this code a couple of times while configuring in editor - // with an interval of 0ms because the overall time range has not yet been - // set. Since 0ms is not a valid ES interval, we cannot pass it through dateHistogramInterval - // below, since it would throw an exception. So in the cases we still have an interval of 0ms - // here we simply skip the rest of the method and never write an interval into the DSL, since - // this DSL will anyway not be used before we're passing this code with an actual interval. - return; - } - output.params = { - ...output.params, - ...dateHistogramInterval(interval.expression), - }; + const { useNormalizedEsInterval, scaleMetricValues } = agg.params; + const interval = agg.buckets.getInterval(useNormalizedEsInterval); + output.bucketInterval = interval; + if (interval.expression === '0ms') { + // We are hitting this code a couple of times while configuring in editor + // with an interval of 0ms because the overall time range has not yet been + // set. Since 0ms is not a valid ES interval, we cannot pass it through dateHistogramInterval + // below, since it would throw an exception. So in the cases we still have an interval of 0ms + // here we simply skip the rest of the method and never write an interval into the DSL, since + // this DSL will anyway not be used before we're passing this code with an actual interval. + return; + } + output.params = { + ...output.params, + ...dateHistogramInterval(interval.expression), + }; - const scaleMetrics = scaleMetricValues && interval.scaled && interval.scale < 1; - if (scaleMetrics && aggs) { - const metrics = aggs.aggs.filter(a => isMetricAggType(a.type)); - const all = _.every(metrics, (a: IBucketAggConfig) => { - const { type } = a; + const scaleMetrics = scaleMetricValues && interval.scaled && interval.scale < 1; + if (scaleMetrics && aggs) { + const metrics = aggs.aggs.filter(a => isMetricAggType(a.type)); + const all = every(metrics, (a: IBucketAggConfig) => { + const { type } = a; - if (isMetricAggType(type)) { - return type.isScalable(); + if (isMetricAggType(type)) { + return type.isScalable(); + } + }); + if (all) { + output.metricScale = interval.scale; + output.metricScaleText = interval.preScaled.description; } - }); - if (all) { - output.metricScale = interval.scale; - output.metricScaleText = interval.preScaled.description; } - } + }, }, - }, - { - name: 'time_zone', - default: undefined, - // We don't ever want this parameter to be serialized out (when saving or to URLs) - // since we do all the logic handling it "on the fly" in the `write` method, to prevent - // time_zones being persisted into saved_objects - serialize: _.noop, - write(agg, output) { - // If a time_zone has been set explicitly always prefer this. - let tz = agg.params.time_zone; - if (!tz && agg.params.field) { - // If a field has been configured check the index pattern's typeMeta if a date_histogram on that - // field requires a specific time_zone - tz = _.get(agg.getIndexPattern(), [ - 'typeMeta', - 'aggs', - 'date_histogram', - agg.params.field.name, - 'time_zone', - ]); - } - if (!tz) { - const config = getUiSettings(); - // If the index pattern typeMeta data, didn't had a time zone assigned for the selected field use the configured tz - const isDefaultTimezone = config.isDefault('dateFormat:tz'); - tz = isDefaultTimezone ? detectedTimezone || tzOffset : config.get('dateFormat:tz'); - } - output.params.time_zone = tz; + { + name: 'time_zone', + default: undefined, + // We don't ever want this parameter to be serialized out (when saving or to URLs) + // since we do all the logic handling it "on the fly" in the `write` method, to prevent + // time_zones being persisted into saved_objects + serialize: noop, + write(agg, output) { + // If a time_zone has been set explicitly always prefer this. + let tz = agg.params.time_zone; + if (!tz && agg.params.field) { + // If a field has been configured check the index pattern's typeMeta if a date_histogram on that + // field requires a specific time_zone + tz = get(agg.getIndexPattern(), [ + 'typeMeta', + 'aggs', + 'date_histogram', + agg.params.field.name, + 'time_zone', + ]); + } + if (!tz) { + // If the index pattern typeMeta data, didn't had a time zone assigned for the selected field use the configured tz + const isDefaultTimezone = uiSettings.isDefault('dateFormat:tz'); + tz = isDefaultTimezone ? detectedTimezone || tzOffset : uiSettings.get('dateFormat:tz'); + } + output.params.time_zone = tz; + }, }, - }, - { - name: 'drop_partials', - default: false, - write: _.noop, - shouldShow: agg => { - const field = agg.params.field; - return field && field.name && field.name === agg.getIndexPattern().timeFieldName; + { + name: 'drop_partials', + default: false, + write: noop, + shouldShow: agg => { + const field = agg.params.field; + return field && field.name && field.name === agg.getIndexPattern().timeFieldName; + }, }, - }, - { - name: 'format', - }, - { - name: 'min_doc_count', - default: 1, - }, - { - name: 'extended_bounds', - default: {}, - write(agg, output) { - const val = agg.params.extended_bounds; + { + name: 'format', + }, + { + name: 'min_doc_count', + default: 1, + }, + { + name: 'extended_bounds', + default: {}, + write(agg, output) { + const val = agg.params.extended_bounds; - if (val.min != null || val.max != null) { - output.params.extended_bounds = { - min: moment(val.min).valueOf(), - max: moment(val.max).valueOf(), - }; + if (val.min != null || val.max != null) { + output.params.extended_bounds = { + min: moment(val.min).valueOf(), + max: moment(val.max).valueOf(), + }; - return; - } + return; + } + }, }, - }, - ], -}); + ], + }); diff --git a/src/plugins/data/public/search/aggs/buckets/date_range.test.ts b/src/plugins/data/public/search/aggs/buckets/date_range.test.ts index 03a453836e113..4ea550492fa09 100644 --- a/src/plugins/data/public/search/aggs/buckets/date_range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/date_range.test.ts @@ -17,20 +17,22 @@ * under the License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { coreMock } from '../../../../../../../src/core/public/mocks'; -import { setUiSettings } from '../../../../public/services'; -import { dateRangeBucketAgg } from './date_range'; +import { getDateRangeBucketAgg, DateRangeBucketAggDependencies } from './date_range'; import { AggConfigs } from '../agg_configs'; -import { mockDataServices, mockAggTypesRegistry } from '../test_helpers'; +import { mockAggTypesRegistry } from '../test_helpers'; import { BUCKET_TYPES } from './bucket_agg_types'; describe('date_range params', () => { + let aggTypesDependencies: DateRangeBucketAggDependencies; + beforeEach(() => { - mockDataServices(); - }); + const { uiSettings } = coreMock.createSetup(); - const typesRegistry = mockAggTypesRegistry([dateRangeBucketAgg]); + aggTypesDependencies = { + uiSettings, + }; + }); const getAggConfigs = (params: Record = {}, hasIncludeTypeMeta: boolean = true) => { const field = { @@ -67,12 +69,12 @@ describe('date_range params', () => { params, }, ], - { typesRegistry } + { typesRegistry: mockAggTypesRegistry([getDateRangeBucketAgg(aggTypesDependencies)]) } ); }; describe('getKey', () => { - it('should return object', () => { + test('should return object', () => { const aggConfigs = getAggConfigs(); const dateRange = aggConfigs.aggs[0]; const bucket = { from: 'from-date', to: 'to-date', key: 'from-dateto-date' }; @@ -82,7 +84,7 @@ describe('date_range params', () => { }); describe('time_zone', () => { - it('should use the specified time_zone', () => { + test('should use the specified time_zone', () => { const aggConfigs = getAggConfigs({ time_zone: 'Europe/Minsk', field: 'bytes', @@ -93,7 +95,7 @@ describe('date_range params', () => { expect(params.time_zone).toBe('Europe/Minsk'); }); - it('should use the fixed time_zone from the index pattern typeMeta', () => { + test('should use the fixed time_zone from the index pattern typeMeta', () => { const aggConfigs = getAggConfigs({ field: 'bytes', }); @@ -103,12 +105,14 @@ describe('date_range params', () => { expect(params.time_zone).toBe('defaultTimeZone'); }); - it('should use the Kibana time_zone if no parameter specified', () => { - const core = coreMock.createStart(); - setUiSettings({ - ...core.uiSettings, - get: () => 'kibanaTimeZone' as any, - }); + test('should use the Kibana time_zone if no parameter specified', () => { + aggTypesDependencies = { + ...aggTypesDependencies, + uiSettings: { + ...aggTypesDependencies.uiSettings, + get: () => 'kibanaTimeZone' as any, + }, + }; const aggConfigs = getAggConfigs( { @@ -119,8 +123,6 @@ describe('date_range params', () => { const dateRange = aggConfigs.aggs[0]; const params = dateRange.toDsl()[BUCKET_TYPES.DATE_RANGE]; - setUiSettings(core.uiSettings); // clean up - expect(params.time_zone).toBe('kibanaTimeZone'); }); }); diff --git a/src/plugins/data/public/search/aggs/buckets/date_range.ts b/src/plugins/data/public/search/aggs/buckets/date_range.ts index 59e78af2d7b95..8133a47ec7248 100644 --- a/src/plugins/data/public/search/aggs/buckets/date_range.ts +++ b/src/plugins/data/public/search/aggs/buckets/date_range.ts @@ -20,86 +20,92 @@ import { get } from 'lodash'; import moment from 'moment-timezone'; import { i18n } from '@kbn/i18n'; +import { IUiSettingsClient } from 'src/core/public'; + import { BUCKET_TYPES } from './bucket_agg_types'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; import { createFilterDateRange } from './create_filter/date_range'; import { convertDateRangeToString, DateRangeKey } from './lib/date_range'; import { KBN_FIELD_TYPES, FieldFormat, TEXT_CONTEXT_TYPE } from '../../../../common'; -import { getFieldFormats, getUiSettings } from '../../../../public/services'; +import { getFieldFormats } from '../../../../public/services'; const dateRangeTitle = i18n.translate('data.search.aggs.buckets.dateRangeTitle', { defaultMessage: 'Date Range', }); -export const dateRangeBucketAgg = new BucketAggType({ - name: BUCKET_TYPES.DATE_RANGE, - title: dateRangeTitle, - createFilter: createFilterDateRange, - getKey({ from, to }): DateRangeKey { - return { from, to }; - }, - getFormat(agg) { - const fieldFormatsService = getFieldFormats(); +export interface DateRangeBucketAggDependencies { + uiSettings: IUiSettingsClient; +} - const formatter = agg.fieldOwnFormatter( - TEXT_CONTEXT_TYPE, - fieldFormatsService.getDefaultInstance(KBN_FIELD_TYPES.DATE) - ); - const DateRangeFormat = FieldFormat.from(function(range: DateRangeKey) { - return convertDateRangeToString(range, formatter); - }); - return new DateRangeFormat(); - }, - makeLabel(aggConfig) { - return aggConfig.getFieldDisplayName() + ' date ranges'; - }, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.DATE, - default(agg: IBucketAggConfig) { - return agg.getIndexPattern().timeFieldName; - }, +export const getDateRangeBucketAgg = ({ uiSettings }: DateRangeBucketAggDependencies) => + new BucketAggType({ + name: BUCKET_TYPES.DATE_RANGE, + title: dateRangeTitle, + createFilter: createFilterDateRange, + getKey({ from, to }): DateRangeKey { + return { from, to }; }, - { - name: 'ranges', - default: [ - { - from: 'now-1w/w', - to: 'now', - }, - ], + getFormat(agg) { + const fieldFormatsService = getFieldFormats(); + + const formatter = agg.fieldOwnFormatter( + TEXT_CONTEXT_TYPE, + fieldFormatsService.getDefaultInstance(KBN_FIELD_TYPES.DATE) + ); + const DateRangeFormat = FieldFormat.from(function(range: DateRangeKey) { + return convertDateRangeToString(range, formatter); + }); + return new DateRangeFormat(); + }, + makeLabel(aggConfig) { + return aggConfig.getFieldDisplayName() + ' date ranges'; }, - { - name: 'time_zone', - default: undefined, - // Implimentation method is the same as that of date_histogram - serialize: () => undefined, - write: (agg, output) => { - const field = agg.getParam('field'); - let tz = agg.getParam('time_zone'); + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.DATE, + default(agg: IBucketAggConfig) { + return agg.getIndexPattern().timeFieldName; + }, + }, + { + name: 'ranges', + default: [ + { + from: 'now-1w/w', + to: 'now', + }, + ], + }, + { + name: 'time_zone', + default: undefined, + // Implimentation method is the same as that of date_histogram + serialize: () => undefined, + write: (agg, output) => { + const field = agg.getParam('field'); + let tz = agg.getParam('time_zone'); - if (!tz && field) { - tz = get(agg.getIndexPattern(), [ - 'typeMeta', - 'aggs', - 'date_range', - field.name, - 'time_zone', - ]); - } - if (!tz) { - const config = getUiSettings(); - const detectedTimezone = moment.tz.guess(); - const tzOffset = moment().format('Z'); - const isDefaultTimezone = config.isDefault('dateFormat:tz'); + if (!tz && field) { + tz = get(agg.getIndexPattern(), [ + 'typeMeta', + 'aggs', + 'date_range', + field.name, + 'time_zone', + ]); + } + if (!tz) { + const detectedTimezone = moment.tz.guess(); + const tzOffset = moment().format('Z'); + const isDefaultTimezone = uiSettings.isDefault('dateFormat:tz'); - tz = isDefaultTimezone ? detectedTimezone || tzOffset : config.get('dateFormat:tz'); - } - output.params.time_zone = tz; + tz = isDefaultTimezone ? detectedTimezone || tzOffset : uiSettings.get('dateFormat:tz'); + } + output.params.time_zone = tz; + }, }, - }, - ], -}); + ], + }); diff --git a/src/plugins/data/public/search/aggs/buckets/filters.ts b/src/plugins/data/public/search/aggs/buckets/filters.ts index 0ad28b8be2132..8b9aca87f8735 100644 --- a/src/plugins/data/public/search/aggs/buckets/filters.ts +++ b/src/plugins/data/public/search/aggs/buckets/filters.ts @@ -17,9 +17,8 @@ * under the License. */ -import _ from 'lodash'; import { i18n } from '@kbn/i18n'; - +import { size, transform, cloneDeep } from 'lodash'; import { IUiSettingsClient } from 'src/core/public'; import { createFilterFilters } from './create_filter/filters'; @@ -27,7 +26,6 @@ import { toAngularJSON } from '../utils'; import { BucketAggType } from './_bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { Storage } from '../../../../../../plugins/kibana_utils/public'; - import { getEsQueryConfig, buildEsQuery, Query } from '../../../../common'; import { getQueryLog } from '../../../query'; @@ -43,9 +41,12 @@ interface FilterValue { id: string; } -export function getFiltersBucketAgg(deps: { uiSettings: IUiSettingsClient }) { - const { uiSettings } = deps; - return new BucketAggType({ +export interface FiltersBucketAggDependencies { + uiSettings: IUiSettingsClient; +} + +export const getFiltersBucketAgg = ({ uiSettings }: FiltersBucketAggDependencies) => + new BucketAggType({ name: BUCKET_TYPES.FILTERS, title: filtersTitle, createFilter: createFilterFilters, @@ -58,7 +59,7 @@ export function getFiltersBucketAgg(deps: { uiSettings: IUiSettingsClient }) { ], write(aggConfig, output) { const inFilters: FilterValue[] = aggConfig.params.filters; - if (!_.size(inFilters)) return; + if (!size(inFilters)) return; inFilters.forEach(filter => { const persistedLog = getQueryLog( @@ -70,10 +71,10 @@ export function getFiltersBucketAgg(deps: { uiSettings: IUiSettingsClient }) { persistedLog.add(filter.input.query); }); - const outFilters = _.transform( + const outFilters = transform( inFilters, function(filters, filter) { - const input = _.cloneDeep(filter.input); + const input = cloneDeep(filter.input); if (!input) { console.log('malformed filter agg params, missing "input" query'); // eslint-disable-line no-console @@ -100,7 +101,7 @@ export function getFiltersBucketAgg(deps: { uiSettings: IUiSettingsClient }) { {} ); - if (!_.size(outFilters)) return; + if (!size(outFilters)) return; const params = output.params || (output.params = {}); params.filters = outFilters; @@ -108,4 +109,3 @@ export function getFiltersBucketAgg(deps: { uiSettings: IUiSettingsClient }) { }, ], }); -} diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts b/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts index 09dd03c759155..408cdf22bcbc2 100644 --- a/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts @@ -24,8 +24,6 @@ import { BUCKET_TYPES } from './bucket_agg_types'; import { IBucketAggConfig } from './_bucket_agg_type'; describe('Geohash Agg', () => { - // const typesRegistry = mockAggTypesRegistry([geoHashBucketAgg]); - const typesRegistry = mockAggTypesRegistry(); const getAggConfigs = (params?: Record) => { const indexPattern = { id: '1234', @@ -63,7 +61,7 @@ describe('Geohash Agg', () => { }, }, ], - { typesRegistry } + { typesRegistry: mockAggTypesRegistry() } ); }; diff --git a/src/plugins/data/public/search/aggs/buckets/histogram.test.ts b/src/plugins/data/public/search/aggs/buckets/histogram.test.ts index 07cf022dca83c..c61b4ff37935a 100644 --- a/src/plugins/data/public/search/aggs/buckets/histogram.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/histogram.test.ts @@ -17,21 +17,29 @@ * under the License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { coreMock } from '../../../../../../../src/core/public/mocks'; -import { setUiSettings } from '../../../../public/services'; import { AggConfigs } from '../agg_configs'; -import { mockDataServices, mockAggTypesRegistry } from '../test_helpers'; +import { mockAggTypesRegistry } from '../test_helpers'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { IBucketHistogramAggConfig, histogramBucketAgg, AutoBounds } from './histogram'; +import { + IBucketHistogramAggConfig, + getHistogramBucketAgg, + AutoBounds, + HistogramBucketAggDependencies, +} from './histogram'; import { BucketAggType } from './_bucket_agg_type'; describe('Histogram Agg', () => { + let aggTypesDependencies: HistogramBucketAggDependencies; + beforeEach(() => { - mockDataServices(); - }); + const { uiSettings, notifications } = coreMock.createSetup(); - const typesRegistry = mockAggTypesRegistry([histogramBucketAgg]); + aggTypesDependencies = { + uiSettings, + notifications, + }; + }); const getAggConfigs = (params: Record) => { const indexPattern = { @@ -58,7 +66,7 @@ describe('Histogram Agg', () => { params, }, ], - { typesRegistry } + { typesRegistry: mockAggTypesRegistry([getHistogramBucketAgg(aggTypesDependencies)]) } ); }; @@ -76,7 +84,7 @@ describe('Histogram Agg', () => { let histogramType: BucketAggType; beforeEach(() => { - histogramType = histogramBucketAgg; + histogramType = getHistogramBucketAgg(aggTypesDependencies); }); it('is ordered', () => { @@ -150,6 +158,14 @@ describe('Histogram Agg', () => { params?: Record, autoBounds?: AutoBounds ) => { + aggTypesDependencies = { + ...aggTypesDependencies, + uiSettings: { + ...aggTypesDependencies.uiSettings, + get: () => maxBars as any, + }, + }; + const aggConfigs = getAggConfigs({ ...params, field: { @@ -162,15 +178,7 @@ describe('Histogram Agg', () => { aggConfig.setAutoBounds(autoBounds); } - const core = coreMock.createStart(); - setUiSettings({ - ...core.uiSettings, - get: () => maxBars as any, - }); - - const interval = aggConfig.write(aggConfigs).params; - setUiSettings(core.uiSettings); // clean up - return interval; + return aggConfig.write(aggConfigs).params; }; it('will respect the histogram:maxBars setting', () => { diff --git a/src/plugins/data/public/search/aggs/buckets/histogram.ts b/src/plugins/data/public/search/aggs/buckets/histogram.ts index 7ccd5ae4bf98c..bbffc0912bf0d 100644 --- a/src/plugins/data/public/search/aggs/buckets/histogram.ts +++ b/src/plugins/data/public/search/aggs/buckets/histogram.ts @@ -17,182 +17,190 @@ * under the License. */ -import _ from 'lodash'; +import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; +import { IUiSettingsClient, NotificationsSetup } from 'src/core/public'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; import { createFilterHistogram } from './create_filter/histogram'; import { BUCKET_TYPES } from './bucket_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; -import { getNotifications, getUiSettings } from '../../../../public/services'; export interface AutoBounds { min: number; max: number; } +export interface HistogramBucketAggDependencies { + uiSettings: IUiSettingsClient; + notifications: NotificationsSetup; +} + export interface IBucketHistogramAggConfig extends IBucketAggConfig { setAutoBounds: (bounds: AutoBounds) => void; getAutoBounds: () => AutoBounds; } -export const histogramBucketAgg = new BucketAggType({ - name: BUCKET_TYPES.HISTOGRAM, - title: i18n.translate('data.search.aggs.buckets.histogramTitle', { - defaultMessage: 'Histogram', - }), - ordered: {}, - makeLabel(aggConfig) { - return aggConfig.getFieldDisplayName(); - }, - createFilter: createFilterHistogram, - decorateAggConfig() { - let autoBounds: AutoBounds; - - return { - setAutoBounds: { - configurable: true, - value(newValue: AutoBounds) { - autoBounds = newValue; +export const getHistogramBucketAgg = ({ + uiSettings, + notifications, +}: HistogramBucketAggDependencies) => + new BucketAggType({ + name: BUCKET_TYPES.HISTOGRAM, + title: i18n.translate('data.search.aggs.buckets.histogramTitle', { + defaultMessage: 'Histogram', + }), + ordered: {}, + makeLabel(aggConfig) { + return aggConfig.getFieldDisplayName(); + }, + createFilter: createFilterHistogram, + decorateAggConfig() { + let autoBounds: AutoBounds; + + return { + setAutoBounds: { + configurable: true, + value(newValue: AutoBounds) { + autoBounds = newValue; + }, }, - }, - getAutoBounds: { - configurable: true, - value() { - return autoBounds; + getAutoBounds: { + configurable: true, + value() { + return autoBounds; + }, }, - }, - }; - }, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.NUMBER, + }; }, - { - /* - * This parameter can be set if you want the auto scaled interval to always - * be a multiple of a specific base. - */ - name: 'intervalBase', - default: null, - write: () => {}, - }, - { - name: 'interval', - modifyAggConfigOnSearchRequestStart( - aggConfig: IBucketHistogramAggConfig, - searchSource: any, - options: any - ) { - const field = aggConfig.getField(); - const aggBody = field.scripted - ? { script: { source: field.script, lang: field.lang } } - : { field: field.name }; - - const childSearchSource = searchSource - .createChild() - .setField('size', 0) - .setField('aggs', { - maxAgg: { - max: aggBody, - }, - minAgg: { - min: aggBody, - }, - }); - - return childSearchSource - .fetch(options) - .then((resp: any) => { - aggConfig.setAutoBounds({ - min: _.get(resp, 'aggregations.minAgg.value'), - max: _.get(resp, 'aggregations.maxAgg.value'), - }); - }) - .catch((e: Error) => { - if (e.name === 'AbortError') return; - getNotifications().toasts.addWarning( - i18n.translate('data.search.aggs.histogram.missingMaxMinValuesWarning', { - defaultMessage: - 'Unable to retrieve max and min values to auto-scale histogram buckets. This may lead to poor visualization performance.', - }) - ); - }); + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.NUMBER, }, - write(aggConfig, output) { - let interval = parseFloat(aggConfig.params.interval); - if (interval <= 0) { - interval = 1; - } - const autoBounds = aggConfig.getAutoBounds(); - - // ensure interval does not create too many buckets and crash browser - if (autoBounds) { - const range = autoBounds.max - autoBounds.min; - const bars = range / interval; - - const config = getUiSettings(); - if (bars > config.get('histogram:maxBars')) { - const minInterval = range / config.get('histogram:maxBars'); - - // Round interval by order of magnitude to provide clean intervals - // Always round interval up so there will always be less buckets than histogram:maxBars - const orderOfMagnitude = Math.pow(10, Math.floor(Math.log10(minInterval))); - let roundInterval = orderOfMagnitude; - - while (roundInterval < minInterval) { - roundInterval += orderOfMagnitude; + { + /* + * This parameter can be set if you want the auto scaled interval to always + * be a multiple of a specific base. + */ + name: 'intervalBase', + default: null, + write: () => {}, + }, + { + name: 'interval', + modifyAggConfigOnSearchRequestStart( + aggConfig: IBucketHistogramAggConfig, + searchSource: any, + options: any + ) { + const field = aggConfig.getField(); + const aggBody = field.scripted + ? { script: { source: field.script, lang: field.lang } } + : { field: field.name }; + + const childSearchSource = searchSource + .createChild() + .setField('size', 0) + .setField('aggs', { + maxAgg: { + max: aggBody, + }, + minAgg: { + min: aggBody, + }, + }); + + return childSearchSource + .fetch(options) + .then((resp: any) => { + aggConfig.setAutoBounds({ + min: get(resp, 'aggregations.minAgg.value'), + max: get(resp, 'aggregations.maxAgg.value'), + }); + }) + .catch((e: Error) => { + if (e.name === 'AbortError') return; + notifications.toasts.addWarning( + i18n.translate('data.search.aggs.histogram.missingMaxMinValuesWarning', { + defaultMessage: + 'Unable to retrieve max and min values to auto-scale histogram buckets. This may lead to poor visualization performance.', + }) + ); + }); + }, + write(aggConfig, output) { + let interval = parseFloat(aggConfig.params.interval); + if (interval <= 0) { + interval = 1; + } + const autoBounds = aggConfig.getAutoBounds(); + + // ensure interval does not create too many buckets and crash browser + if (autoBounds) { + const range = autoBounds.max - autoBounds.min; + const bars = range / interval; + + if (bars > uiSettings.get('histogram:maxBars')) { + const minInterval = range / uiSettings.get('histogram:maxBars'); + + // Round interval by order of magnitude to provide clean intervals + // Always round interval up so there will always be less buckets than histogram:maxBars + const orderOfMagnitude = Math.pow(10, Math.floor(Math.log10(minInterval))); + let roundInterval = orderOfMagnitude; + + while (roundInterval < minInterval) { + roundInterval += orderOfMagnitude; + } + interval = roundInterval; } - interval = roundInterval; } - } - const base = aggConfig.params.intervalBase; - - if (base) { - if (interval < base) { - // In case the specified interval is below the base, just increase it to it's base - interval = base; - } else if (interval % base !== 0) { - // In case the interval is not a multiple of the base round it to the next base - interval = Math.round(interval / base) * base; + const base = aggConfig.params.intervalBase; + + if (base) { + if (interval < base) { + // In case the specified interval is below the base, just increase it to it's base + interval = base; + } else if (interval % base !== 0) { + // In case the interval is not a multiple of the base round it to the next base + interval = Math.round(interval / base) * base; + } } - } - output.params.interval = interval; + output.params.interval = interval; + }, }, - }, - { - name: 'min_doc_count', - default: false, - write(aggConfig, output) { - if (aggConfig.params.min_doc_count) { - output.params.min_doc_count = 0; - } else { - output.params.min_doc_count = 1; - } + { + name: 'min_doc_count', + default: false, + write(aggConfig, output) { + if (aggConfig.params.min_doc_count) { + output.params.min_doc_count = 0; + } else { + output.params.min_doc_count = 1; + } + }, }, - }, - { - name: 'has_extended_bounds', - default: false, - write: () => {}, - }, - { - name: 'extended_bounds', - default: { - min: '', - max: '', + { + name: 'has_extended_bounds', + default: false, + write: () => {}, }, - write(aggConfig, output) { - const { min, max } = aggConfig.params.extended_bounds; + { + name: 'extended_bounds', + default: { + min: '', + max: '', + }, + write(aggConfig, output) { + const { min, max } = aggConfig.params.extended_bounds; - if (aggConfig.params.has_extended_bounds && (min || min === 0) && (max || max === 0)) { - output.params.extended_bounds = { min, max }; - } + if (aggConfig.params.has_extended_bounds && (min || min === 0) && (max || max === 0)) { + output.params.extended_bounds = { min, max }; + } + }, + shouldShow: (aggConfig: IBucketAggConfig) => aggConfig.params.has_extended_bounds, }, - shouldShow: (aggConfig: IBucketAggConfig) => aggConfig.params.has_extended_bounds, - }, - ], -}); + ], + }); diff --git a/src/plugins/data/public/search/aggs/buckets/range.test.ts b/src/plugins/data/public/search/aggs/buckets/range.test.ts index d9e1af149524c..bf3711543ae88 100644 --- a/src/plugins/data/public/search/aggs/buckets/range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/range.test.ts @@ -48,8 +48,6 @@ describe('Range Agg', () => { mockDataServices(); }); - const typesRegistry = mockAggTypesRegistry([rangeBucketAgg]); - const getConfig = (() => {}) as FieldFormatsGetConfigFn; const getAggConfigs = () => { const field = { @@ -86,7 +84,7 @@ describe('Range Agg', () => { }, }, ], - { typesRegistry } + { typesRegistry: mockAggTypesRegistry([rangeBucketAgg]) } ); }; diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts b/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts index cee3ed506c29c..1c221126c35e0 100644 --- a/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts @@ -26,7 +26,6 @@ import { IBucketAggConfig } from './_bucket_agg_type'; describe('Significant Terms Agg', () => { describe('order agg editor UI', () => { describe('convert include/exclude from old format', () => { - const typesRegistry = mockAggTypesRegistry([significantTermsBucketAgg]); const getAggConfigs = (params: Record = {}) => { const indexPattern = { id: '1234', @@ -52,12 +51,12 @@ describe('Significant Terms Agg', () => { params, }, ], - { typesRegistry } + { typesRegistry: mockAggTypesRegistry([significantTermsBucketAgg]) } ); }; const testSerializeAndWrite = (aggs: IAggConfigs) => { - const agg = aggs.aggs[0]; + const [agg] = aggs.aggs; const { [BUCKET_TYPES.SIGNIFICANT_TERMS]: params } = agg.toDsl(); expect(params.field).toBe('field'); diff --git a/src/plugins/data/public/search/aggs/buckets/terms.test.ts b/src/plugins/data/public/search/aggs/buckets/terms.test.ts index 9a4f28afd3edf..280d78f6620bd 100644 --- a/src/plugins/data/public/search/aggs/buckets/terms.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/terms.test.ts @@ -23,7 +23,6 @@ import { BUCKET_TYPES } from './bucket_agg_types'; describe('Terms Agg', () => { describe('order agg editor UI', () => { - const typesRegistry = mockAggTypesRegistry(); const getAggConfigs = (params: Record = {}) => { const indexPattern = { id: '1234', @@ -48,7 +47,7 @@ describe('Terms Agg', () => { type: BUCKET_TYPES.TERMS, }, ], - { typesRegistry } + { typesRegistry: mockAggTypesRegistry() } ); }; diff --git a/src/plugins/data/public/search/aggs/index.test.ts b/src/plugins/data/public/search/aggs/index.test.ts index b5dedc9d45e84..8c0e47763c295 100644 --- a/src/plugins/data/public/search/aggs/index.test.ts +++ b/src/plugins/data/public/search/aggs/index.test.ts @@ -22,24 +22,29 @@ import { getAggTypes } from './index'; import { isBucketAggType } from './buckets/_bucket_agg_type'; import { isMetricAggType } from './metrics/metric_agg_type'; +import { QueryStart } from '../../query'; -const aggTypes = getAggTypes({ uiSettings: coreMock.createStart().uiSettings }); +describe('AggTypesComponent', () => { + const core = coreMock.createSetup(); + const aggTypes = getAggTypes({ + uiSettings: core.uiSettings, + notifications: core.notifications, + query: {} as QueryStart, + }); -const bucketAggs = aggTypes.buckets; -const metricAggs = aggTypes.metrics; + const { buckets, metrics } = aggTypes; -describe('AggTypesComponent', () => { describe('bucket aggs', () => { - it('all extend BucketAggType', () => { - bucketAggs.forEach(bucketAgg => { + test('all extend BucketAggType', () => { + buckets.forEach(bucketAgg => { expect(isBucketAggType(bucketAgg)).toBeTruthy(); }); }); }); describe('metric aggs', () => { - it('all extend MetricAggType', () => { - metricAggs.forEach(metricAgg => { + test('all extend MetricAggType', () => { + metrics.forEach(metricAgg => { expect(isMetricAggType(metricAgg)).toBeTruthy(); }); }); diff --git a/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts b/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts index 1ebd0ea29c9ff..57d27b7da6313 100644 --- a/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts +++ b/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts @@ -23,6 +23,7 @@ import { AggTypesRegistry, AggTypesRegistryStart } from '../agg_types_registry'; import { getAggTypes } from '../agg_types'; import { BucketAggType } from '../buckets/_bucket_agg_type'; import { MetricAggType } from '../metrics/metric_agg_type'; +import { queryServiceMock } from '../../../query/mocks'; /** * Testing utility which creates a new instance of AggTypesRegistry, @@ -51,7 +52,13 @@ export function mockAggTypesRegistry | MetricAggTyp } }); } else { - const aggTypes = getAggTypes({ uiSettings: coreMock.createSetup().uiSettings }); + const core = coreMock.createSetup(); + const aggTypes = getAggTypes({ + uiSettings: core.uiSettings, + notifications: core.notifications, + query: queryServiceMock.createSetupContract(), + }); + aggTypes.buckets.forEach(type => registrySetup.registerBucket(type)); aggTypes.metrics.forEach(type => registrySetup.registerMetric(type)); } diff --git a/src/plugins/data/public/search/search_service.test.ts b/src/plugins/data/public/search/search_service.test.ts index 4bf027b07b833..19308dd387d3a 100644 --- a/src/plugins/data/public/search/search_service.test.ts +++ b/src/plugins/data/public/search/search_service.test.ts @@ -34,7 +34,7 @@ describe('Search service', () => { describe('setup()', () => { it('exposes proper contract', async () => { const setup = searchService.setup(mockCoreSetup, { - version: '8', + packageInfo: { version: '8' }, } as any); expect(setup).toHaveProperty('registerSearchStrategyProvider'); }); diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 311a8a2fc6f60..dc1c99f76d59a 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -25,6 +25,7 @@ import { TStrategyTypes } from './strategy_types'; import { getEsClient, LegacyApiCaller } from './es_client'; import { ES_SEARCH_STRATEGY, DEFAULT_SEARCH_STRATEGY } from '../../common/search'; import { esSearchStrategyProvider } from './es_search/es_search_strategy'; +import { QuerySetup } from '../query/query_service'; import { SearchInterceptor } from './search_interceptor'; import { getAggTypes, @@ -40,6 +41,11 @@ import { siblingPipelineAggHelper, } from './aggs'; +interface SearchServiceSetupDependencies { + packageInfo: PackageInfo; + query: QuerySetup; +} + /** * The search plugin exposes two registration methods for other plugins: * - registerSearchStrategyProvider for plugins to add their own custom @@ -73,13 +79,21 @@ export class SearchService implements Plugin { return strategyProvider; }; - public setup(core: CoreSetup, packageInfo: PackageInfo): ISearchSetup { + public setup( + core: CoreSetup, + { packageInfo, query }: SearchServiceSetupDependencies + ): ISearchSetup { this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo); this.registerSearchStrategyProvider(SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider); this.registerSearchStrategyProvider(ES_SEARCH_STRATEGY, esSearchStrategyProvider); const aggTypesSetup = this.aggTypesRegistry.setup(); - const aggTypes = getAggTypes({ uiSettings: core.uiSettings }); + const aggTypes = getAggTypes({ + query, + uiSettings: core.uiSettings, + notifications: core.notifications, + }); + aggTypes.buckets.forEach(b => aggTypesSetup.registerBucket(b)); aggTypes.metrics.forEach(m => aggTypesSetup.registerMetric(m)); diff --git a/src/plugins/data/public/services.ts b/src/plugins/data/public/services.ts index 2af87d84b780e..199ba17b3b81b 100644 --- a/src/plugins/data/public/services.ts +++ b/src/plugins/data/public/services.ts @@ -17,8 +17,7 @@ * under the License. */ -import { NotificationsStart } from 'src/core/public'; -import { CoreSetup, CoreStart } from 'kibana/public'; +import { NotificationsStart, CoreSetup, CoreStart } from 'src/core/public'; import { FieldFormatsStart } from './field_formats'; import { createGetterSetter } from '../../kibana_utils/public'; import { IndexPatternsContract } from './index_patterns'; diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 5038b4226fad8..47bef4255347c 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -173,6 +173,7 @@ export { ISearchContext, TSearchStrategyProvider, getDefaultSearchParams, + getTotalLoaded, } from './search'; // Search namespace diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.ts b/src/plugins/data/server/search/es_search/es_search_strategy.ts index b4ee02eefaf84..47cad7aa6b4d7 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.ts @@ -21,7 +21,7 @@ import { APICaller } from 'kibana/server'; import { SearchResponse } from 'elasticsearch'; import { ES_SEARCH_STRATEGY } from '../../../common/search'; import { ISearchStrategy, TSearchStrategyProvider } from '../i_search_strategy'; -import { getDefaultSearchParams, ISearchContext } from '..'; +import { getDefaultSearchParams, getTotalLoaded, ISearchContext } from '..'; export const esSearchStrategyProvider: TSearchStrategyProvider = ( context: ISearchContext, @@ -46,9 +46,7 @@ export const esSearchStrategyProvider: TSearchStrategyProvider { + it('returns the total/loaded, not including skipped', () => { + const result = getTotalLoaded({ + successful: 10, + failed: 5, + skipped: 5, + total: 100, + }); + + expect(result).toEqual({ + total: 100, + loaded: 15, + }); }); -} +}); diff --git a/src/legacy/core_plugins/visualizations/public/index.ts b/src/plugins/data/server/search/es_search/get_total_loaded.ts similarity index 68% rename from src/legacy/core_plugins/visualizations/public/index.ts rename to src/plugins/data/server/search/es_search/get_total_loaded.ts index f5590c745b3f9..b5af600e84eff 100644 --- a/src/legacy/core_plugins/visualizations/public/index.ts +++ b/src/plugins/data/server/search/es_search/get_total_loaded.ts @@ -17,10 +17,14 @@ * under the License. */ +import { ShardsResponse } from 'elasticsearch'; + /** - * Static np-ready code, re-exported here so consumers can import from - * `src/legacy/core_plugins/visualizations/public` - * - * @public + * Get the `total`/`loaded` for this response (see `IKibanaSearchResponse`). Note that `skipped` is + * not included as it is already included in `successful`. + * @internal */ -export * from './np_ready/public'; +export function getTotalLoaded({ total, failed, successful }: ShardsResponse) { + const loaded = failed + successful; + return { total, loaded }; +} diff --git a/src/plugins/data/server/search/es_search/index.ts b/src/plugins/data/server/search/es_search/index.ts index 5a8b3bc94c679..20006b70730d8 100644 --- a/src/plugins/data/server/search/es_search/index.ts +++ b/src/plugins/data/server/search/es_search/index.ts @@ -20,3 +20,4 @@ export { ES_SEARCH_STRATEGY, IEsSearchRequest, IEsSearchResponse } from '../../../common/search'; export { esSearchStrategyProvider } from './es_search_strategy'; export { getDefaultSearchParams } from './get_default_search_params'; +export { getTotalLoaded } from './get_total_loaded'; diff --git a/src/plugins/data/server/search/index.ts b/src/plugins/data/server/search/index.ts index 15738a3befb27..e08eba1cad831 100644 --- a/src/plugins/data/server/search/index.ts +++ b/src/plugins/data/server/search/index.ts @@ -33,4 +33,4 @@ export { TStrategyTypes } from './strategy_types'; export { TSearchStrategyProvider } from './i_search_strategy'; -export { getDefaultSearchParams } from './es_search'; +export { getDefaultSearchParams, getTotalLoaded } from './es_search'; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 1abc74fe07ccc..c41023eab6d20 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -125,6 +125,7 @@ import { SearchResponse } from 'elasticsearch'; import { SearchShardsParams } from 'elasticsearch'; import { SearchTemplateParams } from 'elasticsearch'; import { ShallowPromise } from '@kbn/utility-types'; +import { ShardsResponse } from 'elasticsearch'; import { SnapshotCreateParams } from 'elasticsearch'; import { SnapshotCreateRepositoryParams } from 'elasticsearch'; import { SnapshotDeleteParams } from 'elasticsearch'; @@ -330,6 +331,12 @@ export function getDefaultSearchParams(config: SharedGlobalConfig): { restTotalHitsAsInt: boolean; }; +// @internal +export function getTotalLoaded({ total, failed, successful }: ShardsResponse): { + total: number; + loaded: number; +}; + // Warning: (ae-missing-release-tag) "IFieldFormatsRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -730,12 +737,12 @@ export type TSearchStrategyProvider = (context: ISearc // src/plugins/data/server/index.ts:102:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:130:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:130:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:181:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:182:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:183:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:184:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:185:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:188:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:182:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:183:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:184:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:185:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:186:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:189:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/server/plugin.ts:64:14 - (ae-forgotten-export) The symbol "ISearchSetup" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap b/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap index 2424416f6f92c..9f48e6e57e0ff 100644 --- a/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap +++ b/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap @@ -2,7 +2,7 @@ exports[`FieldName renders a geo field, useShortDots is set to true 1`] = `
{ - const field = { - type: 'number', - name: 'test.test.test', - rowCount: 100, - scripted: false, - }; - const component = render(); + const component = render(); expect(component).toMatchSnapshot(); }); test('FieldName renders a geo field, useShortDots is set to true', () => { - const field = { - type: 'geo_point', - name: 'test.test.test', - rowCount: 0, - scripted: false, - }; - const component = render(); + const component = render( + + ); expect(component).toMatchSnapshot(); }); diff --git a/src/plugins/discover/public/components/field_name/field_name.tsx b/src/plugins/discover/public/components/field_name/field_name.tsx index 63518aae28de6..f7f1433328adf 100644 --- a/src/plugins/discover/public/components/field_name/field_name.tsx +++ b/src/plugins/discover/public/components/field_name/field_name.tsx @@ -17,51 +17,36 @@ * under the License. */ import React from 'react'; -import classNames from 'classnames'; import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { FieldIcon, FieldIconProps } from '../../../../kibana_react/public'; import { shortenDottedString } from '../../helpers'; import { getFieldTypeName } from './field_type_name'; -// property field is provided at discover's field chooser // properties fieldType and fieldName are provided in kbn_doc_view // this should be changed when both components are deangularized interface Props { - field?: { - type: string; - name: string; - rowCount?: number; - scripted?: boolean; - }; - fieldName?: string; - fieldType?: string; + fieldName: string; + fieldType: string; useShortDots?: boolean; fieldIconProps?: Omit; + scripted?: boolean; } -export function FieldName({ field, fieldName, fieldType, useShortDots, fieldIconProps }: Props) { - const type = field ? String(field.type) : String(fieldType); - const typeName = getFieldTypeName(type); - - const name = field ? String(field.name) : String(fieldName); - const displayName = useShortDots ? shortenDottedString(name) : name; - - const noResults = field ? !field.rowCount && !field.scripted : false; - - const className = classNames('dscFieldName', { - 'dscFieldName--noResults': noResults, - }); +export function FieldName({ + fieldName, + fieldType, + useShortDots, + fieldIconProps, + scripted = false, +}: Props) { + const typeName = getFieldTypeName(fieldType); + const displayName = useShortDots ? shortenDottedString(fieldName) : fieldName; return ( - + - + diff --git a/src/plugins/home/server/tutorials/cisco_logs/index.ts b/src/plugins/home/server/tutorials/cisco_logs/index.ts index 303dbd9a9d856..a694802663171 100644 --- a/src/plugins/home/server/tutorials/cisco_logs/index.ts +++ b/src/plugins/home/server/tutorials/cisco_logs/index.ts @@ -50,7 +50,7 @@ supports the "asa" fileset for Cisco ASA firewall logs received over syslog or r learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-cisco.html', }, }), - // euiIconType: 'logoCisco', + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/cisco.svg', artifacts: { dashboards: [], application: { diff --git a/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts b/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts index ac2db66dff6b6..53803a9358a14 100644 --- a/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts +++ b/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts @@ -50,7 +50,7 @@ It supports both standalone deployment and Envoy proxy deployment in Kubernetes. learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-envoyproxy.html', }, }), - // euiIconType: 'logoCisco', + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/envoyproxy.svg', artifacts: { dashboards: [], application: { diff --git a/src/plugins/home/server/tutorials/iis_logs/index.ts b/src/plugins/home/server/tutorials/iis_logs/index.ts index ff9996c1b0187..82ce098018e0b 100644 --- a/src/plugins/home/server/tutorials/iis_logs/index.ts +++ b/src/plugins/home/server/tutorials/iis_logs/index.ts @@ -49,7 +49,7 @@ export function iisLogsSpecProvider(context: TutorialContext): TutorialSchema { learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-iis.html', }, }), - // euiIconType: 'logoIIS', + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/iis.svg', artifacts: { dashboards: [ { diff --git a/src/plugins/home/server/tutorials/iptables_logs/index.ts b/src/plugins/home/server/tutorials/iptables_logs/index.ts index e3f2124347b6b..b29ab20cb6653 100644 --- a/src/plugins/home/server/tutorials/iptables_logs/index.ts +++ b/src/plugins/home/server/tutorials/iptables_logs/index.ts @@ -52,7 +52,7 @@ number and the action performed on the traffic (allow/deny).. \ learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-iptables.html', }, }), - // euiIconType: 'logoUbiquiti', + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/ubiquiti.svg', artifacts: { dashboards: [], application: { diff --git a/src/plugins/home/server/tutorials/mssql_metrics/index.ts b/src/plugins/home/server/tutorials/mssql_metrics/index.ts index cea46c3220d5a..a1c994d670a3d 100644 --- a/src/plugins/home/server/tutorials/mssql_metrics/index.ts +++ b/src/plugins/home/server/tutorials/mssql_metrics/index.ts @@ -48,7 +48,7 @@ export function mssqlMetricsSpecProvider(context: TutorialContext): TutorialSche learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-mssql.html', }, }), - // euiIconType: 'logoMSSQL', + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/mssql.svg', isBeta: false, artifacts: { dashboards: [ diff --git a/src/plugins/home/server/tutorials/munin_metrics/index.ts b/src/plugins/home/server/tutorials/munin_metrics/index.ts index e438d3015c77c..90e4ac6026dad 100644 --- a/src/plugins/home/server/tutorials/munin_metrics/index.ts +++ b/src/plugins/home/server/tutorials/munin_metrics/index.ts @@ -36,6 +36,7 @@ export function muninMetricsSpecProvider(context: TutorialContext): TutorialSche name: i18n.translate('home.tutorials.muninMetrics.nameTitle', { defaultMessage: 'Munin metrics', }), + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/munin.svg', isBeta: true, category: TutorialsCategory.METRICS, shortDescription: i18n.translate('home.tutorials.muninMetrics.shortDescription', { diff --git a/src/plugins/home/server/tutorials/suricata_logs/index.ts b/src/plugins/home/server/tutorials/suricata_logs/index.ts index ac19cf0987b84..a3812fda147f5 100644 --- a/src/plugins/home/server/tutorials/suricata_logs/index.ts +++ b/src/plugins/home/server/tutorials/suricata_logs/index.ts @@ -50,7 +50,7 @@ export function suricataLogsSpecProvider(context: TutorialContext): TutorialSche learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-suricata.html', }, }), - // euiIconType: 'logoSuricata', + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/suricata.svg', artifacts: { dashboards: [ { diff --git a/src/plugins/home/server/tutorials/system_logs/index.ts b/src/plugins/home/server/tutorials/system_logs/index.ts index fc2fa4f49fd5f..ab8184c1b3249 100644 --- a/src/plugins/home/server/tutorials/system_logs/index.ts +++ b/src/plugins/home/server/tutorials/system_logs/index.ts @@ -50,6 +50,7 @@ Unix/Linux based distributions. This module is not available on Windows. \ learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-system.html', }, }), + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/system.svg', artifacts: { dashboards: [ { diff --git a/src/plugins/home/server/tutorials/system_metrics/index.ts b/src/plugins/home/server/tutorials/system_metrics/index.ts index b0355e1118a96..456804c51f838 100644 --- a/src/plugins/home/server/tutorials/system_metrics/index.ts +++ b/src/plugins/home/server/tutorials/system_metrics/index.ts @@ -49,6 +49,7 @@ It collects system wide statistics and statistics per process and filesystem. \ learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-system.html', }, }), + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/system.svg', artifacts: { dashboards: [ { diff --git a/src/plugins/home/server/tutorials/traefik_logs/index.ts b/src/plugins/home/server/tutorials/traefik_logs/index.ts index 423023a3902e5..56f1d56ea0123 100644 --- a/src/plugins/home/server/tutorials/traefik_logs/index.ts +++ b/src/plugins/home/server/tutorials/traefik_logs/index.ts @@ -49,7 +49,7 @@ export function traefikLogsSpecProvider(context: TutorialContext): TutorialSchem learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-traefik.html', }, }), - // euiIconType: 'logoTraefik', + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/traefik.svg', artifacts: { dashboards: [ { diff --git a/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts b/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts index fc36cfe869867..a1dfbc64ec244 100644 --- a/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts +++ b/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts @@ -48,7 +48,7 @@ export function uwsgiMetricsSpecProvider(context: TutorialContext): TutorialSche learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-uwsgi.html', }, }), - // euiIconType: 'logouWSGI', + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/uwsgi.svg', isBeta: false, artifacts: { dashboards: [ diff --git a/src/plugins/home/server/tutorials/vsphere_metrics/index.ts b/src/plugins/home/server/tutorials/vsphere_metrics/index.ts index 3ea57cdbc0e44..908b6440f88c6 100644 --- a/src/plugins/home/server/tutorials/vsphere_metrics/index.ts +++ b/src/plugins/home/server/tutorials/vsphere_metrics/index.ts @@ -48,7 +48,7 @@ export function vSphereMetricsSpecProvider(context: TutorialContext): TutorialSc learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-vsphere.html', }, }), - // euiIconType: 'logoVSphere', + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/vsphere.svg', isBeta: true, artifacts: { application: { diff --git a/src/plugins/home/server/tutorials/zeek_logs/index.ts b/src/plugins/home/server/tutorials/zeek_logs/index.ts index c015545046c99..251825147ded1 100644 --- a/src/plugins/home/server/tutorials/zeek_logs/index.ts +++ b/src/plugins/home/server/tutorials/zeek_logs/index.ts @@ -50,7 +50,7 @@ export function zeekLogsSpecProvider(context: TutorialContext): TutorialSchema { learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-zeek.html', }, }), - // TODO: euiIconType: 'logoZeek', + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/zeek.svg', artifacts: { dashboards: [ { diff --git a/src/plugins/home/server/tutorials/zookeeper_metrics/index.ts b/src/plugins/home/server/tutorials/zookeeper_metrics/index.ts index dcecbb6d4a812..581b4a14a2f38 100644 --- a/src/plugins/home/server/tutorials/zookeeper_metrics/index.ts +++ b/src/plugins/home/server/tutorials/zookeeper_metrics/index.ts @@ -36,6 +36,7 @@ export function zookeeperMetricsSpecProvider(context: TutorialContext): Tutorial name: i18n.translate('home.tutorials.zookeeperMetrics.nameTitle', { defaultMessage: 'Zookeeper metrics', }), + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/zookeeper.svg', isBeta: false, category: TutorialsCategory.METRICS, shortDescription: i18n.translate('home.tutorials.zookeeperMetrics.shortDescription', { diff --git a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx index cbdeef6fbe96c..8a4cc88999bfe 100644 --- a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx +++ b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx @@ -37,6 +37,7 @@ export function redirectWhenMissing({ history, mapping, toastNotifications, + onBeforeRedirect, }: { history: History; /** @@ -48,6 +49,10 @@ export function redirectWhenMissing({ * Toast notifications service to show toasts in error cases. */ toastNotifications: ToastsSetup; + /** + * Optional callback invoked directly before a redirect is triggered + */ + onBeforeRedirect?: (error: SavedObjectNotFound) => void; }) { let localMappingObject: Mapping; @@ -75,6 +80,9 @@ export function redirectWhenMissing({ text: toMountPoint({error.message}), }); + if (onBeforeRedirect) { + onBeforeRedirect(error); + } history.replace(url); }; } diff --git a/src/plugins/saved_objects/kibana.json b/src/plugins/saved_objects/kibana.json index 4081c9a4b21b9..0792955b7c5f1 100644 --- a/src/plugins/saved_objects/kibana.json +++ b/src/plugins/saved_objects/kibana.json @@ -3,5 +3,5 @@ "version": "kibana", "server": false, "ui": true, - "requiredPlugins": [] + "requiredPlugins": ["data"] } diff --git a/src/plugins/saved_objects/public/plugin.ts b/src/plugins/saved_objects/public/plugin.ts index 5092f7a0b7b33..0f5773c00283e 100644 --- a/src/plugins/saved_objects/public/plugin.ts +++ b/src/plugins/saved_objects/public/plugin.ts @@ -17,11 +17,31 @@ * under the License. */ -import { Plugin } from 'src/core/public'; +import { CoreStart, Plugin } from 'src/core/public'; import './index.scss'; +import { createSavedObjectClass } from './saved_object'; +import { DataPublicPluginStart } from '../../data/public'; -export class SavedObjectsPublicPlugin implements Plugin { +export interface SavedObjectsStart { + SavedObjectClass: any; +} + +export interface SavedObjectsStartDeps { + data: DataPublicPluginStart; +} + +export class SavedObjectsPublicPlugin + implements Plugin { public setup() {} - public start() {} + public start(core: CoreStart, { data }: SavedObjectsStartDeps) { + return { + SavedObjectClass: createSavedObjectClass({ + indexPatterns: data.indexPatterns, + savedObjectsClient: core.savedObjects.client, + chrome: core.chrome, + overlays: core.overlays, + }), + }; + } } diff --git a/src/plugins/visualizations/kibana.json b/src/plugins/visualizations/kibana.json index 8e63ea7833327..cd22b1375ae1b 100644 --- a/src/plugins/visualizations/kibana.json +++ b/src/plugins/visualizations/kibana.json @@ -3,7 +3,5 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": [ - "expressions" - ] + "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "usageCollection"] } diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/__snapshots__/visualization_noresults.test.js.snap b/src/plugins/visualizations/public/components/__snapshots__/visualization_noresults.test.js.snap similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/__snapshots__/visualization_noresults.test.js.snap rename to src/plugins/visualizations/public/components/__snapshots__/visualization_noresults.test.js.snap diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/__snapshots__/visualization_requesterror.test.js.snap b/src/plugins/visualizations/public/components/__snapshots__/visualization_requesterror.test.js.snap similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/__snapshots__/visualization_requesterror.test.js.snap rename to src/plugins/visualizations/public/components/__snapshots__/visualization_requesterror.test.js.snap diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/_index.scss b/src/plugins/visualizations/public/components/_index.scss similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/_index.scss rename to src/plugins/visualizations/public/components/_index.scss diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/_visualization.scss b/src/plugins/visualizations/public/components/_visualization.scss similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/_visualization.scss rename to src/plugins/visualizations/public/components/_visualization.scss diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/index.ts b/src/plugins/visualizations/public/components/index.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/index.ts rename to src/plugins/visualizations/public/components/index.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization.test.js b/src/plugins/visualizations/public/components/visualization.test.js similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization.test.js rename to src/plugins/visualizations/public/components/visualization.test.js diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization.tsx b/src/plugins/visualizations/public/components/visualization.tsx similarity index 96% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization.tsx rename to src/plugins/visualizations/public/components/visualization.tsx index 5296de365daec..c17e088d7635b 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization.tsx +++ b/src/plugins/visualizations/public/components/visualization.tsx @@ -19,7 +19,7 @@ import { get } from 'lodash'; import React from 'react'; -import { PersistedState } from '../../../../../../../plugins/visualizations/public'; +import { PersistedState } from '../../../../plugins/visualizations/public'; import { memoizeLast } from '../legacy/memoize'; import { VisualizationChart } from './visualization_chart'; import { VisualizationNoResults } from './visualization_noresults'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_chart.test.js b/src/plugins/visualizations/public/components/visualization_chart.test.js similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_chart.test.js rename to src/plugins/visualizations/public/components/visualization_chart.test.js diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_chart.tsx b/src/plugins/visualizations/public/components/visualization_chart.tsx similarity index 96% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_chart.tsx rename to src/plugins/visualizations/public/components/visualization_chart.tsx index fcfbc8445952c..7d163d2067ee5 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_chart.tsx +++ b/src/plugins/visualizations/public/components/visualization_chart.tsx @@ -20,9 +20,9 @@ import React from 'react'; import * as Rx from 'rxjs'; import { debounceTime, filter, share, switchMap } from 'rxjs/operators'; -import { PersistedState } from '../../../../../../../plugins/visualizations/public'; +import { PersistedState } from '../../../../plugins/visualizations/public'; import { VisualizationController } from '../types'; -import { ResizeChecker } from '../../../../../../../plugins/kibana_utils/public'; +import { ResizeChecker } from '../../../../plugins/kibana_utils/public'; import { ExprVis } from '../expressions/vis'; interface VisualizationChartProps { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_noresults.test.js b/src/plugins/visualizations/public/components/visualization_noresults.test.js similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_noresults.test.js rename to src/plugins/visualizations/public/components/visualization_noresults.test.js diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_noresults.tsx b/src/plugins/visualizations/public/components/visualization_noresults.tsx similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_noresults.tsx rename to src/plugins/visualizations/public/components/visualization_noresults.tsx diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_requesterror.test.js b/src/plugins/visualizations/public/components/visualization_requesterror.test.js similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_requesterror.test.js rename to src/plugins/visualizations/public/components/visualization_requesterror.test.js diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_requesterror.tsx b/src/plugins/visualizations/public/components/visualization_requesterror.tsx similarity index 96% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_requesterror.tsx rename to src/plugins/visualizations/public/components/visualization_requesterror.tsx index 406f24741c911..3e677e609ad9a 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_requesterror.tsx +++ b/src/plugins/visualizations/public/components/visualization_requesterror.tsx @@ -19,7 +19,7 @@ import { EuiIcon, EuiSpacer, EuiText } from '@elastic/eui'; import React from 'react'; -import { SearchError } from '../../../../../../../plugins/data/public'; +import { SearchError } from '../../../../plugins/data/public'; interface VisualizationRequestErrorProps { onInit?: () => void; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_embeddables.scss b/src/plugins/visualizations/public/embeddable/_embeddables.scss similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_embeddables.scss rename to src/plugins/visualizations/public/embeddable/_embeddables.scss diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_index.scss b/src/plugins/visualizations/public/embeddable/_index.scss similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_index.scss rename to src/plugins/visualizations/public/embeddable/_index.scss diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_visualize_lab_disabled.scss b/src/plugins/visualizations/public/embeddable/_visualize_lab_disabled.scss similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/_visualize_lab_disabled.scss rename to src/plugins/visualizations/public/embeddable/_visualize_lab_disabled.scss diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/constants.ts b/src/plugins/visualizations/public/embeddable/constants.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/constants.ts rename to src/plugins/visualizations/public/embeddable/constants.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/disabled_lab_embeddable.tsx b/src/plugins/visualizations/public/embeddable/disabled_lab_embeddable.tsx similarity index 94% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/disabled_lab_embeddable.tsx rename to src/plugins/visualizations/public/embeddable/disabled_lab_embeddable.tsx index fbb2eba3afe79..af8121d8bf033 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/disabled_lab_embeddable.tsx +++ b/src/plugins/visualizations/public/embeddable/disabled_lab_embeddable.tsx @@ -19,7 +19,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { Embeddable, EmbeddableOutput } from '../../../../../../../plugins/embeddable/public'; +import { Embeddable, EmbeddableOutput } from '../../../../plugins/embeddable/public'; import { DisabledLabVisualization } from './disabled_lab_visualization'; import { VisualizeInput } from './visualize_embeddable'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/disabled_lab_visualization.tsx b/src/plugins/visualizations/public/embeddable/disabled_lab_visualization.tsx similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/disabled_lab_visualization.tsx rename to src/plugins/visualizations/public/embeddable/disabled_lab_visualization.tsx diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/events.ts b/src/plugins/visualizations/public/embeddable/events.ts similarity index 90% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/events.ts rename to src/plugins/visualizations/public/embeddable/events.ts index 53d04bf6eb04a..0957895a21403 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/events.ts +++ b/src/plugins/visualizations/public/embeddable/events.ts @@ -17,10 +17,7 @@ * under the License. */ -import { - SELECT_RANGE_TRIGGER, - VALUE_CLICK_TRIGGER, -} from '../../../../../../../plugins/ui_actions/public'; +import { SELECT_RANGE_TRIGGER, VALUE_CLICK_TRIGGER } from '../../../../plugins/ui_actions/public'; export interface VisEventToTrigger { ['brush']: typeof SELECT_RANGE_TRIGGER; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/get_index_pattern.ts b/src/plugins/visualizations/public/embeddable/get_index_pattern.ts similarity index 97% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/get_index_pattern.ts rename to src/plugins/visualizations/public/embeddable/get_index_pattern.ts index 05ce68221eaf0..c12c95145fe44 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/get_index_pattern.ts +++ b/src/plugins/visualizations/public/embeddable/get_index_pattern.ts @@ -22,7 +22,7 @@ import { indexPatterns, IIndexPattern, IndexPatternAttributes, -} from '../../../../../../../plugins/data/public'; +} from '../../../../plugins/data/public'; import { getUISettings, getSavedObjects } from '../services'; export async function getIndexPattern( diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/index.ts b/src/plugins/visualizations/public/embeddable/index.ts similarity index 95% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/index.ts rename to src/plugins/visualizations/public/embeddable/index.ts index a1cd31eebef20..78f9827ffde3e 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/index.ts +++ b/src/plugins/visualizations/public/embeddable/index.ts @@ -20,3 +20,4 @@ export { DisabledLabEmbeddable } from './disabled_lab_embeddable'; export { VisualizeEmbeddable, VisualizeInput } from './visualize_embeddable'; export { VisualizeEmbeddableFactory } from './visualize_embeddable_factory'; export { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; +export { VIS_EVENT_TO_TRIGGER } from './events'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts similarity index 97% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts rename to src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index bcca4bdf67dcf..0c7e732f0b185 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -28,19 +28,16 @@ import { esFilters, Filter, TimefilterContract, -} from '../../../../../../../plugins/data/public'; +} from '../../../../plugins/data/public'; import { EmbeddableInput, EmbeddableOutput, Embeddable, Container, EmbeddableVisTriggerContext, -} from '../../../../../../../plugins/embeddable/public'; -import { dispatchRenderComplete } from '../../../../../../../plugins/kibana_utils/public'; -import { - IExpressionLoaderParams, - ExpressionsStart, -} from '../../../../../../../plugins/expressions/public'; +} from '../../../../plugins/embeddable/public'; +import { dispatchRenderComplete } from '../../../../plugins/kibana_utils/public'; +import { IExpressionLoaderParams, ExpressionsStart } from '../../../../plugins/expressions/public'; import { buildPipeline } from '../legacy/build_pipeline'; import { Vis } from '../vis'; import { getExpressions, getUiActions } from '../services'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx similarity index 97% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable_factory.tsx rename to src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 36d3d8bb07d29..428f1865e6566 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -18,13 +18,13 @@ */ import { i18n } from '@kbn/i18n'; -import { SavedObjectAttributes } from '../../../../../../../core/public'; +import { SavedObjectAttributes } from '../../../../core/public'; import { Container, EmbeddableFactory, EmbeddableOutput, ErrorEmbeddable, -} from '../../../../../../../plugins/embeddable/public'; +} from '../../../../plugins/embeddable/public'; import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; import { VisualizeEmbeddable, VisualizeInput, VisualizeOutput } from './visualize_embeddable'; import { Vis } from '../types'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.ts b/src/plugins/visualizations/public/expressions/vis.ts similarity index 95% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.ts rename to src/plugins/visualizations/public/expressions/vis.ts index 3b0458a6c8dcc..a7d4a28070620 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.ts +++ b/src/plugins/visualizations/public/expressions/vis.ts @@ -29,11 +29,10 @@ import { EventEmitter } from 'events'; import _ from 'lodash'; -import { PersistedState } from '../../../../../../../plugins/visualizations/public'; +import { VisParams, PersistedState } from '../../../../plugins/visualizations/public'; import { getTypes } from '../services'; import { VisType } from '../vis_types'; -import { VisParams } from '../types'; export interface ExprVisState { title?: string; @@ -82,10 +81,11 @@ export class ExprVis extends EventEmitter { private getType(type: string | VisType) { if (_.isString(type)) { - return getTypes().get(type); - if (!this.type) { + const newType = getTypes().get(type); + if (!newType) { throw new Error(`Invalid type "${type}"`); } + return newType; } else { return type; } diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_function.ts b/src/plugins/visualizations/public/expressions/visualization_function.ts similarity index 95% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_function.ts rename to src/plugins/visualizations/public/expressions/visualization_function.ts index d98eda4c50ef9..52177f6ec381a 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_function.ts +++ b/src/plugins/visualizations/public/expressions/visualization_function.ts @@ -19,14 +19,8 @@ import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { - VisResponseValue, - PersistedState, -} from '../../../../../../../plugins/visualizations/public'; -import { - ExpressionFunctionDefinition, - Render, -} from '../../../../../../../plugins/expressions/public'; +import { VisResponseValue, PersistedState } from '../../../../plugins/visualizations/public'; +import { ExpressionFunctionDefinition, Render } from '../../../../plugins/expressions/public'; import { getTypes, getIndexPatterns, getFilterManager } from '../services'; interface Arguments { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_renderer.tsx b/src/plugins/visualizations/public/expressions/visualization_renderer.tsx similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_renderer.tsx rename to src/plugins/visualizations/public/expressions/visualization_renderer.tsx diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/_index.scss b/src/plugins/visualizations/public/index.scss similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/_index.scss rename to src/plugins/visualizations/public/index.scss diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index c08dbf890b8da..7df420e7ba585 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -17,15 +17,37 @@ * under the License. */ -import { PluginInitializerContext } from '../../../core/public'; -import { VisualizationsPublicPlugin } from './plugin'; +import './index.scss'; + +import { PublicContract } from '@kbn/utility-types'; +import { PluginInitializerContext } from 'src/core/public'; +import { VisualizationsPlugin, VisualizationsSetup, VisualizationsStart } from './plugin'; +import { VisualizeEmbeddableFactory, VisualizeEmbeddable } from './embeddable'; +import { ExprVis as ExprVisClass } from './expressions/vis'; export function plugin(initializerContext: PluginInitializerContext) { - return new VisualizationsPublicPlugin(initializerContext); + return new VisualizationsPlugin(initializerContext); } -export { VisualizationsPublicPlugin as Plugin }; -export * from './plugin'; -export * from './types'; +/** @public static code */ +export { Vis } from './vis'; +export { TypesService } from './vis_types/types_service'; +export { VISUALIZE_EMBEDDABLE_TYPE, VIS_EVENT_TO_TRIGGER } from './embeddable'; +/** @public types */ +export { VisualizationsSetup, VisualizationsStart }; +export { VisTypeAlias, VisType } from './vis_types'; +export { VisParams, SerializedVis, SerializedVisData, VisData } from './vis'; +export type VisualizeEmbeddableFactoryContract = PublicContract; +export type VisualizeEmbeddableContract = PublicContract; +export { VisualizeInput } from './embeddable'; +export type ExprVis = ExprVisClass; +export { SchemaConfig } from './legacy/build_pipeline'; export { PersistedState } from './persisted_state'; +export { + VisualizationController, + SavedVisState, + ISavedVis, + VisSavedObject, + VisResponseValue, +} from './types'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__snapshots__/build_pipeline.test.ts.snap b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__snapshots__/build_pipeline.test.ts.snap rename to src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_update_objs/gauge_objs.js b/src/plugins/visualizations/public/legacy/__tests__/vis_update_objs/gauge_objs.js similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_update_objs/gauge_objs.js rename to src/plugins/visualizations/public/legacy/__tests__/vis_update_objs/gauge_objs.js diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts similarity index 98% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts rename to src/plugins/visualizations/public/legacy/build_pipeline.test.ts index d5c532b53a53e..5476ce6df0390 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts @@ -27,10 +27,8 @@ import { Schemas, } from './build_pipeline'; import { Vis } from '..'; -import { dataPluginMock } from '../../../../../../../plugins/data/public/mocks'; -import { IAggConfig } from '../../../../../../../plugins/data/public'; - -jest.mock('ui/new_platform'); +import { dataPluginMock } from '../../../../plugins/data/public/mocks'; +import { IAggConfig } from '../../../../plugins/data/public'; describe('visualize loader pipeline helpers: build pipeline', () => { describe('prepareJson', () => { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts similarity index 99% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts rename to src/plugins/visualizations/public/legacy/build_pipeline.ts index 05ae26892b9bd..18af94c919247 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.ts @@ -19,13 +19,13 @@ import { get } from 'lodash'; import moment from 'moment'; -import { SerializedFieldFormat } from '../../../../../../../plugins/expressions/public'; +import { SerializedFieldFormat } from '../../../../plugins/expressions/public'; import { IAggConfig, fieldFormats, search, TimefilterContract, -} from '../../../../../../../plugins/data/public'; +} from '../../../../plugins/data/public'; import { Vis, VisParams } from '../types'; const { isDateHistogramBucketAggConfig } = search.aggs; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/memoize.test.ts b/src/plugins/visualizations/public/legacy/memoize.test.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/memoize.test.ts rename to src/plugins/visualizations/public/legacy/memoize.test.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/memoize.ts b/src/plugins/visualizations/public/legacy/memoize.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/memoize.ts rename to src/plugins/visualizations/public/legacy/memoize.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/vis_update.js b/src/plugins/visualizations/public/legacy/vis_update.js similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/vis_update.js rename to src/plugins/visualizations/public/legacy/vis_update.js diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/vis_update_state.js b/src/plugins/visualizations/public/legacy/vis_update_state.js similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/vis_update_state.js rename to src/plugins/visualizations/public/legacy/vis_update_state.js diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/vis_update_state.test.js b/src/plugins/visualizations/public/legacy/vis_update_state.test.js similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/vis_update_state.test.js rename to src/plugins/visualizations/public/legacy/vis_update_state.test.js diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts index af7688d019f65..f4983a4313c4d 100644 --- a/src/plugins/visualizations/public/mocks.ts +++ b/src/plugins/visualizations/public/mocks.ts @@ -16,22 +16,60 @@ * specific language governing permissions and limitations * under the License. */ -import { VisualizationsSetup, VisualizationsStart } from '.'; -export type Setup = jest.Mocked; -export type Start = jest.Mocked; +import { PluginInitializerContext } from '../../../core/public'; +import { VisualizationsSetup, VisualizationsStart } from './'; +import { VisualizationsPlugin } from './plugin'; +import { coreMock } from '../../../core/public/mocks'; +import { embeddablePluginMock } from '../../../plugins/embeddable/public/mocks'; +import { expressionsPluginMock } from '../../../plugins/expressions/public/mocks'; +import { dataPluginMock } from '../../../plugins/data/public/mocks'; +import { usageCollectionPluginMock } from '../../../plugins/usage_collection/public/mocks'; +import { uiActionsPluginMock } from '../../../plugins/ui_actions/public/mocks'; -const createSetupContract = (): Setup => { - const setupContract: Setup = undefined; - return setupContract; -}; +const createSetupContract = (): VisualizationsSetup => ({ + createBaseVisualization: jest.fn(), + createReactVisualization: jest.fn(), + registerAlias: jest.fn(), + hideTypes: jest.fn(), +}); + +const createStartContract = (): VisualizationsStart => ({ + get: jest.fn(), + all: jest.fn(), + getAliases: jest.fn(), + savedVisualizationsLoader: {} as any, + showNewVisModal: jest.fn(), + createVis: jest.fn(), + convertFromSerializedVis: jest.fn(), + convertToSerializedVis: jest.fn(), +}); + +const createInstance = async () => { + const plugin = new VisualizationsPlugin({} as PluginInitializerContext); + + const setup = plugin.setup(coreMock.createSetup(), { + data: dataPluginMock.createSetupContract(), + expressions: expressionsPluginMock.createSetupContract(), + embeddable: embeddablePluginMock.createSetupContract(), + usageCollection: usageCollectionPluginMock.createSetupContract(), + }); + const doStart = () => + plugin.start(coreMock.createStart(), { + data: dataPluginMock.createStartContract(), + expressions: expressionsPluginMock.createStartContract(), + uiActions: uiActionsPluginMock.createStartContract(), + }); -const createStartContract = (): Start => { - const startContract: Start = undefined; - return startContract; + return { + plugin, + setup, + doStart, + }; }; -export const expressionsPluginMock = { +export const visualizationsPluginMock = { createSetupContract, createStartContract, + createInstance, }; diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index cceb63122820d..d3e7b759a4416 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -18,23 +18,81 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; -import { ExpressionsSetup, ExpressionsStart } from '../../expressions/public'; +import { TypesService, TypesSetup, TypesStart } from './vis_types'; +import { + setUISettings, + setTypes, + setI18n, + setCapabilities, + setHttp, + setIndexPatterns, + setSavedObjects, + setUsageCollector, + setFilterManager, + setExpressions, + setUiActions, + setSavedVisualizationsLoader, + setTimeFilter, + setAggs, + setChrome, + setOverlays, +} from './services'; +import { VISUALIZE_EMBEDDABLE_TYPE, VisualizeEmbeddableFactory } from './embeddable'; +import { ExpressionsSetup, ExpressionsStart } from '../../../plugins/expressions/public'; +import { EmbeddableSetup } from '../../../plugins/embeddable/public'; +import { visualization as visualizationFunction } from './expressions/visualization_function'; +import { visualization as visualizationRenderer } from './expressions/visualization_renderer'; import { range as rangeExpressionFunction } from './expression_functions/range'; import { visDimension as visDimensionExpressionFunction } from './expression_functions/vis_dimension'; +import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../plugins/data/public'; +import { UsageCollectionSetup } from '../../../plugins/usage_collection/public'; +import { createSavedVisLoader, SavedVisualizationsLoader } from './saved_visualizations'; +import { SerializedVis, Vis } from './vis'; +import { showNewVisModal } from './wizard'; +import { UiActionsStart } from '../../../plugins/ui_actions/public'; +import { + convertFromSerializedVis, + convertToSerializedVis, +} from './saved_visualizations/_saved_vis'; + +/** + * Interface for this plugin's returned setup/start contracts. + * + * @public + */ + +export type VisualizationsSetup = TypesSetup; + +export interface VisualizationsStart extends TypesStart { + savedVisualizationsLoader: SavedVisualizationsLoader; + createVis: (visType: string, visState?: SerializedVis) => Vis; + convertToSerializedVis: typeof convertToSerializedVis; + convertFromSerializedVis: typeof convertFromSerializedVis; + showNewVisModal: typeof showNewVisModal; +} export interface VisualizationsSetupDeps { expressions: ExpressionsSetup; + embeddable: EmbeddableSetup; + usageCollection: UsageCollectionSetup; + data: DataPublicPluginSetup; } export interface VisualizationsStartDeps { + data: DataPublicPluginStart; expressions: ExpressionsStart; + uiActions: UiActionsStart; } -export type VisualizationsSetup = void; - -export type VisualizationsStart = void; - -export class VisualizationsPublicPlugin +/** + * Visualizations Plugin - public + * + * This plugin's stateful contracts are returned from the `setup` and `start` methods + * below. The interfaces for these contracts are provided above. + * + * @internal + */ +export class VisualizationsPlugin implements Plugin< VisualizationsSetup, @@ -42,18 +100,73 @@ export class VisualizationsPublicPlugin VisualizationsSetupDeps, VisualizationsStartDeps > { + private readonly types: TypesService = new TypesService(); + constructor(initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup, { expressions }: VisualizationsSetupDeps): VisualizationsSetup { + public setup( + core: CoreSetup, + { expressions, embeddable, usageCollection, data }: VisualizationsSetupDeps + ): VisualizationsSetup { + setUISettings(core.uiSettings); + setUsageCollector(usageCollection); + + expressions.registerFunction(visualizationFunction); + expressions.registerRenderer(visualizationRenderer); expressions.registerFunction(rangeExpressionFunction); expressions.registerFunction(visDimensionExpressionFunction); - return undefined; + const embeddableFactory = new VisualizeEmbeddableFactory(); + embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory); + + return { + ...this.types.setup(), + }; } - public start(core: CoreStart, { expressions }: VisualizationsStartDeps): VisualizationsStart { - return undefined; + public start( + core: CoreStart, + { data, expressions, uiActions }: VisualizationsStartDeps + ): VisualizationsStart { + const types = this.types.start(); + setI18n(core.i18n); + setTypes(types); + setCapabilities(core.application.capabilities); + setHttp(core.http); + setSavedObjects(core.savedObjects); + setIndexPatterns(data.indexPatterns); + setFilterManager(data.query.filterManager); + setExpressions(expressions); + setUiActions(uiActions); + setTimeFilter(data.query.timefilter.timefilter); + setAggs(data.search.aggs); + setOverlays(core.overlays); + setChrome(core.chrome); + const savedVisualizationsLoader = createSavedVisLoader({ + savedObjectsClient: core.savedObjects.client, + indexPatterns: data.indexPatterns, + chrome: core.chrome, + overlays: core.overlays, + visualizationTypes: types, + }); + setSavedVisualizationsLoader(savedVisualizationsLoader); + + return { + ...types, + showNewVisModal, + /** + * creates new instance of Vis + * @param {IIndexPattern} indexPattern - index pattern to use + * @param {VisState} visState - visualization configuration + */ + createVis: (visType: string, visState?: SerializedVis) => new Vis(visType, visState), + convertToSerializedVis, + convertFromSerializedVis, + savedVisualizationsLoader, + }; } - public stop() {} + public stop() { + this.types.stop(); + } } diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/_saved_vis.ts b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts similarity index 94% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/_saved_vis.ts rename to src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts index c9906428ccb31..bc96e08f4b9da 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/_saved_vis.ts +++ b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts @@ -28,17 +28,13 @@ import { createSavedObjectClass, SavedObject, SavedObjectKibanaServices, -} from '../../../../../../../plugins/saved_objects/public'; +} from '../../../../plugins/saved_objects/public'; // @ts-ignore import { updateOldState } from '../legacy/vis_update_state'; import { extractReferences, injectReferences } from './saved_visualization_references'; -import { - IIndexPattern, - ISearchSource, - SearchSource, -} from '../../../../../../../plugins/data/public'; +import { IIndexPattern, ISearchSource, SearchSource } from '../../../../plugins/data/public'; import { ISavedVis, SerializedVis } from '../types'; -import { createSavedSearchesLoader } from '../../../../../../../plugins/discover/public'; +import { createSavedSearchesLoader } from '../../../../plugins/discover/public'; import { getChrome, getOverlays, getIndexPatterns, getSavedObjects } from '../services'; export const convertToSerializedVis = async (savedVis: ISavedVis): Promise => { @@ -159,5 +155,5 @@ export function createSavedVisClass(services: SavedObjectKibanaServices) { } } - return SavedVis; + return SavedVis as new (opts: Record | string) => SavedObject; } diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.test.ts b/src/plugins/visualizations/public/saved_visualizations/find_list_items.test.ts similarity index 97% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.test.ts rename to src/plugins/visualizations/public/saved_visualizations/find_list_items.test.ts index d1def09978dbb..4a50590e26251 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.test.ts +++ b/src/plugins/visualizations/public/saved_visualizations/find_list_items.test.ts @@ -18,8 +18,8 @@ */ import { findListItems } from './find_list_items'; -import { coreMock } from '../../../../../../../core/public/mocks'; -import { SavedObjectsClientContract } from '../../../../../../../core/public'; +import { coreMock } from '../../../../core/public/mocks'; +import { SavedObjectsClientContract } from '../../../../core/public'; import { VisTypeAlias } from '../vis_types'; describe('saved_visualizations', () => { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.ts b/src/plugins/visualizations/public/saved_visualizations/find_list_items.ts similarity index 92% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.ts rename to src/plugins/visualizations/public/saved_visualizations/find_list_items.ts index 02db90a762e89..c0203a7441a61 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/find_list_items.ts +++ b/src/plugins/visualizations/public/saved_visualizations/find_list_items.ts @@ -18,11 +18,8 @@ */ import _ from 'lodash'; -import { - SavedObjectAttributes, - SavedObjectsClientContract, -} from '../../../../../../../core/public'; -import { SavedObjectLoader } from '../../../../../../../plugins/saved_objects/public'; +import { SavedObjectAttributes, SavedObjectsClientContract } from '../../../../core/public'; +import { SavedObjectLoader } from '../../../../plugins/saved_objects/public'; import { VisTypeAlias } from '../vis_types'; import { VisualizationsAppExtension } from '../vis_types/vis_type_alias_registry'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/index.ts b/src/plugins/visualizations/public/saved_visualizations/index.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/index.ts rename to src/plugins/visualizations/public/saved_visualizations/index.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.test.ts b/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.test.ts rename to src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.ts b/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.ts similarity index 99% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.ts rename to src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.ts index b995d340d44d9..a14595524100b 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.ts +++ b/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { SavedObjectAttributes, SavedObjectReference } from '../../../../../../../core/public'; +import { SavedObjectAttributes, SavedObjectReference } from '../../../../core/public'; import { VisSavedObject } from '../types'; export function extractReferences({ diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualizations.ts b/src/plugins/visualizations/public/saved_visualizations/saved_visualizations.ts similarity index 92% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualizations.ts rename to src/plugins/visualizations/public/saved_visualizations/saved_visualizations.ts index fc0f77d54059c..e5d0c41712a91 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualizations.ts +++ b/src/plugins/visualizations/public/saved_visualizations/saved_visualizations.ts @@ -19,7 +19,7 @@ import { SavedObjectLoader, SavedObjectKibanaServices, -} from '../../../../../../../plugins/saved_objects/public'; +} from '../../../../plugins/saved_objects/public'; import { findListItems } from './find_list_items'; import { createSavedVisClass } from './_saved_vis'; import { TypesStart } from '../vis_types'; @@ -76,5 +76,9 @@ export function createSavedVisLoader(services: SavedObjectKibanaServicesWithVisu } } const SavedVis = createSavedVisClass(services); - return new SavedObjectLoaderVisualize(SavedVis, savedObjectsClient, services.chrome); + return new SavedObjectLoaderVisualize( + SavedVis, + savedObjectsClient, + services.chrome + ) as SavedObjectLoader & { findListItems: (search: string, size: number) => any }; } diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts b/src/plugins/visualizations/public/services.ts similarity index 83% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts rename to src/plugins/visualizations/public/services.ts index 23cdeae7d15ff..c4668fa4b0c79 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts +++ b/src/plugins/visualizations/public/services.ts @@ -18,7 +18,6 @@ */ import { - ApplicationStart, Capabilities, ChromeStart, HttpStart, @@ -26,18 +25,18 @@ import { IUiSettingsClient, OverlayStart, SavedObjectsStart, -} from '../../../../../../core/public'; +} from '../../../core/public'; import { TypesStart } from './vis_types'; -import { createGetterSetter } from '../../../../../../plugins/kibana_utils/public'; +import { createGetterSetter } from '../../../plugins/kibana_utils/public'; import { DataPublicPluginStart, FilterManager, IndexPatternsContract, TimefilterContract, -} from '../../../../../../plugins/data/public'; -import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/public'; -import { ExpressionsStart } from '../../../../../../plugins/expressions/public'; -import { UiActionsStart } from '../../../../../../plugins/ui_actions/public'; +} from '../../../plugins/data/public'; +import { UsageCollectionSetup } from '../../../plugins/usage_collection/public'; +import { ExpressionsStart } from '../../../plugins/expressions/public'; +import { UiActionsStart } from '../../../plugins/ui_actions/public'; import { SavedVisualizationsLoader } from './saved_visualizations'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); @@ -83,5 +82,3 @@ export const [getAggs, setAggs] = createGetterSetter('Overlays'); export const [getChrome, setChrome] = createGetterSetter('Chrome'); - -export const [getApplication, setApplication] = createGetterSetter('Application'); diff --git a/src/plugins/visualizations/public/types.ts b/src/plugins/visualizations/public/types.ts index 6487266956119..54528a33414c3 100644 --- a/src/plugins/visualizations/public/types.ts +++ b/src/plugins/visualizations/public/types.ts @@ -17,6 +17,42 @@ * under the License. */ +import { SavedObject } from '../../../plugins/saved_objects/public'; +import { ISearchSource, AggConfigOptions } from '../../../plugins/data/public'; +import { SerializedVis, Vis, VisParams } from './vis'; + +export { Vis, SerializedVis, VisParams }; + +export interface VisualizationController { + render(visData: any, visParams: any): Promise; + destroy(): void; + isLoaded?(): Promise | void; +} + +export type VisualizationControllerConstructor = new ( + el: HTMLElement, + vis: Vis +) => VisualizationController; + +export interface SavedVisState { + type: string; + params: VisParams; + aggs: AggConfigOptions[]; +} + +export interface ISavedVis { + id?: string; + title: string; + description?: string; + visState: SavedVisState; + searchSource?: ISearchSource; + uiStateJSON?: string; + savedSearchRefName?: string; + savedSearchId?: string; +} + +export interface VisSavedObject extends SavedObject, ISavedVis {} + export interface VisResponseValue { visType: string; visData: object; diff --git a/src/plugins/visualizations/public/vis.test.ts b/src/plugins/visualizations/public/vis.test.ts new file mode 100644 index 0000000000000..fc9327903fc90 --- /dev/null +++ b/src/plugins/visualizations/public/vis.test.ts @@ -0,0 +1,124 @@ +/* + * 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 { Vis } from './vis'; +// @ts-ignore +import fixturesStubbedLogstashIndexPatternProvider from '../../../fixtures/stubbed_logstash_index_pattern'; + +jest.mock('./services', () => { + class MockVisualizationController { + constructor() {} + + render(): Promise { + return new Promise(resolve => { + resolve(); + }); + } + + destroy() {} + } + + // eslint-disable-next-line + const { BaseVisType } = require('./vis_types/base_vis_type'); + + const visType = new BaseVisType({ + name: 'pie', + title: 'pie', + icon: 'pie-icon', + visualization: MockVisualizationController, + }); + + return { + getTypes: () => ({ get: () => visType }), + getAggs: () => ({ + createAggConfigs: (indexPattern: any, cfg: any) => ({ + aggs: cfg.map((aggConfig: any) => ({ ...aggConfig, toJSON: () => aggConfig })), + }), + }), + }; +}); + +describe('Vis Class', function() { + let vis: Vis; + const stateFixture = { + type: 'pie', + title: 'pie', + data: { + aggs: [ + { type: 'avg' as any, schema: 'metric', params: { field: 'bytes' } }, + { type: 'terms' as any, schema: 'segment', params: { field: 'machine.os' } }, + { type: 'terms' as any, schema: 'segment', params: { field: 'geo.src' } }, + ], + searchSource: { + getField: (name: string) => { + if (name === 'index') { + return fixturesStubbedLogstashIndexPatternProvider(); + } + }, + createCopy: jest.fn(), + }, + }, + params: { isDonut: true }, + }; + + beforeEach(function() { + vis = new Vis('test', stateFixture as any); + }); + + const verifyVis = function(visToVerify: Vis) { + expect(visToVerify).toHaveProperty('data'); + expect(visToVerify.data).toHaveProperty('aggs'); + expect(visToVerify.data.aggs!.aggs).toHaveLength(3); + + expect(visToVerify).toHaveProperty('type'); + + expect(visToVerify).toHaveProperty('params'); + expect(visToVerify.params).toHaveProperty('isDonut', true); + }; + + describe('initialization', function() { + it('should set the state', function() { + verifyVis(vis); + }); + }); + + describe('getState()', function() { + it('should get a state that represents the... er... state', function() { + const state = vis.serialize(); + expect(state).toHaveProperty('type', 'pie'); + + expect(state).toHaveProperty('params'); + expect(state.params).toHaveProperty('isDonut', true); + + expect(state.data).toHaveProperty('aggs'); + expect(state.data.aggs).toHaveLength(3); + }); + }); + + describe('isHierarchical()', function() { + it('should return false for non-hierarchical vis (like histogram)', function() { + expect(vis.isHierarchical()).toBe(false); + }); + + it('should return true for hierarchical vis (like pie)', function() { + vis.type.hierarchicalData = true; + expect(vis.isHierarchical()).toBe(true); + }); + }); +}); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts b/src/plugins/visualizations/public/vis.ts similarity index 97% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts rename to src/plugins/visualizations/public/vis.ts index 91b6a2368f5ef..3cab4faf2a27f 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts +++ b/src/plugins/visualizations/public/vis.ts @@ -28,7 +28,7 @@ */ import { isFunction, defaults, cloneDeep } from 'lodash'; -import { PersistedState } from '../../../../../../../src/plugins/visualizations/public'; +import { PersistedState } from './persisted_state'; // @ts-ignore import { updateVisualizationConfig } from './legacy/vis_update'; import { getTypes, getAggs } from './services'; @@ -38,7 +38,7 @@ import { IndexPattern, ISearchSource, AggConfigOptions, -} from '../../../../../../plugins/data/public'; +} from '../../../plugins/data/public'; export interface SerializedVisData { expression?: string; @@ -49,7 +49,7 @@ export interface SerializedVisData { } export interface SerializedVis { - id: string; + id?: string; title: string; description?: string; type: string; @@ -72,7 +72,7 @@ export interface VisParams { export class Vis { public readonly type: VisType; - public readonly id: string; + public readonly id?: string; public title: string = ''; public description: string = ''; public params: VisParams = {}; diff --git a/test/plugin_functional/plugins/kbn_tp_top_nav/index.js b/src/plugins/visualizations/public/vis_types/base_vis_type.test.ts similarity index 67% rename from test/plugin_functional/plugins/kbn_tp_top_nav/index.js rename to src/plugins/visualizations/public/vis_types/base_vis_type.test.ts index b4c3e05c28b66..ac1242b2a1321 100644 --- a/test/plugin_functional/plugins/kbn_tp_top_nav/index.js +++ b/src/plugins/visualizations/public/vis_types/base_vis_type.test.ts @@ -17,15 +17,19 @@ * under the License. */ -export default function(kibana) { - return new kibana.Plugin({ - uiExports: { - app: { - title: 'Top Nav Menu test', - description: 'This is a sample plugin for the functional tests.', - main: 'plugins/kbn_tp_top_nav/app', - }, - hacks: ['plugins/kbn_tp_top_nav/initialize'], - }, +import { BaseVisType } from './base_vis_type'; + +describe('BaseVisType', () => { + describe('constructor', () => { + test('should throw if image and icon are missing', () => { + expect(() => { + new BaseVisType({ + name: 'test', + title: 'test', + description: 'test', + visualization: {} as any, + }); + }).toThrow(); + }); }); -} +}); diff --git a/src/plugins/visualizations/public/vis_types/base_vis_type.ts b/src/plugins/visualizations/public/vis_types/base_vis_type.ts new file mode 100644 index 0000000000000..2464bb72d2695 --- /dev/null +++ b/src/plugins/visualizations/public/vis_types/base_vis_type.ts @@ -0,0 +1,110 @@ +/* + * 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 _ from 'lodash'; +import { VisualizationControllerConstructor } from '../types'; + +export interface BaseVisTypeOptions { + name: string; + title: string; + description?: string; + icon?: string; + image?: string; + stage?: 'experimental' | 'beta' | 'production'; + feedbackMessage?: string; + options?: Record; + visualization: VisualizationControllerConstructor; + visConfig?: Record; + editor?: any; + editorConfig?: Record; + hidden?: boolean; + requestHandler?: string | unknown; + responseHandler?: string | unknown; + hierarchicalData?: boolean | unknown; + setup?: unknown; + useCustomNoDataScreen?: boolean; +} + +export class BaseVisType { + name: string; + title: string; + description: string; + icon?: string; + image?: string; + stage: 'experimental' | 'beta' | 'production'; + feedbackMessage: string; + options: Record; + visualization: VisualizationControllerConstructor; + visConfig: Record; + editor: any; + editorConfig: Record; + hidden: boolean; + requiresSearch: boolean; + requestHandler: string | unknown; + responseHandler: string | unknown; + hierarchicalData: boolean | unknown; + setup?: unknown; + useCustomNoDataScreen: boolean; + + constructor(opts: BaseVisTypeOptions) { + if (!opts.icon && !opts.image) { + throw new Error('vis_type must define its icon or image'); + } + + const defaultOptions = { + // controls the visualize editor + showTimePicker: true, + showQueryBar: true, + showFilterBar: true, + showIndexSelection: true, + hierarchicalData: false, // we should get rid of this i guess ? + }; + + this.name = opts.name; + this.description = opts.description || ''; + this.title = opts.title; + this.icon = opts.icon; + this.image = opts.image; + this.visualization = opts.visualization; + this.visConfig = _.defaultsDeep({}, opts.visConfig, { defaults: {} }); + this.editor = opts.editor; + this.editorConfig = _.defaultsDeep({}, opts.editorConfig, { collections: {} }); + this.options = _.defaultsDeep({}, opts.options, defaultOptions); + this.stage = opts.stage || 'production'; + this.feedbackMessage = opts.feedbackMessage || ''; + this.hidden = opts.hidden || false; + this.requestHandler = opts.requestHandler || 'courier'; + this.responseHandler = opts.responseHandler || 'none'; + this.setup = opts.setup; + this.requiresSearch = this.requestHandler !== 'none'; + this.hierarchicalData = opts.hierarchicalData || false; + this.useCustomNoDataScreen = opts.useCustomNoDataScreen || false; + } + + shouldMarkAsExperimentalInUI() { + return this.stage === 'experimental'; + } + + public get schemas() { + if (this.editorConfig && this.editorConfig.schemas) { + return this.editorConfig.schemas; + } + return []; + } +} diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/index.ts b/src/plugins/visualizations/public/vis_types/index.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/index.ts rename to src/plugins/visualizations/public/vis_types/index.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/react_vis_type.js b/src/plugins/visualizations/public/vis_types/react_vis_controller.tsx similarity index 70% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/react_vis_type.js rename to src/plugins/visualizations/public/vis_types/react_vis_controller.tsx index e8ee7bc6e5445..643e6ffcb730b 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/react_vis_type.js +++ b/src/plugins/visualizations/public/vis_types/react_vis_controller.tsx @@ -19,21 +19,26 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { getUISettings, getI18n } from '../services'; -import { BaseVisType } from './base_vis_type'; +import { Vis, VisualizationController } from '../types'; +import { getI18n, getUISettings } from '../services'; -class ReactVisController { - constructor(element, vis) { +export class ReactVisController implements VisualizationController { + private el: HTMLElement; + private vis: Vis; + + constructor(element: HTMLElement, vis: Vis) { this.el = element; this.vis = vis; } - render(visData, visParams, updateStatus) { - this.visData = visData; - + public render(visData: any, visParams: any): Promise { const I18nContext = getI18n().Context; - return new Promise(resolve => { + return new Promise((resolve, reject) => { + if (!this.vis.type || !this.vis.type.visConfig || !this.vis.type.visConfig.component) { + reject('Missing component for ReactVisType'); + } + const Component = this.vis.type.visConfig.component; const config = getUISettings(); render( @@ -44,7 +49,6 @@ class ReactVisController { visData={visData} visParams={visParams} renderComplete={resolve} - updateStatus={updateStatus} /> , this.el @@ -52,20 +56,7 @@ class ReactVisController { }); } - destroy() { + public destroy() { unmountComponentAtNode(this.el); } } - -export class ReactVisType extends BaseVisType { - constructor(opts) { - super({ - ...opts, - visualization: ReactVisController, - }); - - if (!this.visConfig.component) { - throw new Error('Missing component for ReactVisType'); - } - } -} diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/react_vis_type.js b/src/plugins/visualizations/public/vis_types/react_vis_type.test.ts similarity index 55% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/react_vis_type.js rename to src/plugins/visualizations/public/vis_types/react_vis_type.test.ts index 2474a58870424..134106bb3d42a 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/vis_types/react_vis_type.js +++ b/src/plugins/visualizations/public/vis_types/react_vis_type.test.ts @@ -17,57 +17,30 @@ * under the License. */ -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { ReactVisType } from '../../../vis_types/react_vis_type'; +import { cloneDeep } from 'lodash'; +import { ReactVisType } from './react_vis_type'; -describe('React Vis Type', function() { +describe('React Vis Type', () => { const visConfig = { name: 'test', title: 'test', description: 'test', icon: 'test', visConfig: { component: 'test' }, - type: { visConfig: { component: 'test' } }, }; - beforeEach(ngMock.module('kibana')); - describe('initialization', () => { it('should throw if component is not set', () => { expect(() => { - new ReactVisType({}); - }).to.throwError(); + const missingConfig = cloneDeep(visConfig); + delete missingConfig.visConfig.component; + new ReactVisType(missingConfig); + }).toThrow(); }); it('creates react controller', () => { const visType = new ReactVisType(visConfig); - expect(visType.visualization).to.not.be.an('undefined'); - }); - }); - - describe('controller render method', () => { - let vis; - beforeEach(() => { - const visType = new ReactVisType(visConfig); - const Vis = visType.visualization; - - vis = new Vis(window.document.body, {}); - }); - - it('rejects if data is not provided', () => { - vis - .render() - .then(() => { - expect('promise was not rejected').to.equal(false); - }) - .catch(() => {}); - }); - - it('renders the component', () => { - expect(() => { - vis.render({}); - }).to.not.throwError(); + expect(visType.visualization).not.toBeUndefined(); }); }); }); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/mocks.ts b/src/plugins/visualizations/public/vis_types/react_vis_type.ts similarity index 65% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/mocks.ts rename to src/plugins/visualizations/public/vis_types/react_vis_type.ts index 10510bff0c97e..68979abe52a3c 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/mocks.ts +++ b/src/plugins/visualizations/public/vis_types/react_vis_type.ts @@ -17,5 +17,18 @@ * under the License. */ -// eslint-disable-next-line -export * from '../../../../../../plugins/embeddable/public/mocks'; +import { BaseVisType, BaseVisTypeOptions } from './base_vis_type'; +import { ReactVisController } from './react_vis_controller'; + +export class ReactVisType extends BaseVisType { + constructor(opts: Omit) { + super({ + ...opts, + visualization: ReactVisController, + }); + + if (!this.visConfig.component) { + throw new Error('Missing component for ReactVisType'); + } + } +} diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/types_service.ts b/src/plugins/visualizations/public/vis_types/types_service.ts similarity index 98% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/types_service.ts rename to src/plugins/visualizations/public/vis_types/types_service.ts index 6bcaa9a3e1dac..321f96180fd68 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/types_service.ts +++ b/src/plugins/visualizations/public/vis_types/types_service.ts @@ -30,8 +30,8 @@ export interface VisType { description?: string; visualization: any; isAccessible?: boolean; - requestHandler: string; - responseHandler: string; + requestHandler: string | unknown; + responseHandler: string | unknown; icon?: IconType; image?: string; stage: 'experimental' | 'beta' | 'production'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/vis_type_alias_registry.ts b/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts similarity index 98% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/vis_type_alias_registry.ts rename to src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts index 12b02ee9e6b32..040fa22352a3a 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_types/vis_type_alias_registry.ts +++ b/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts @@ -17,7 +17,7 @@ * under the License. */ -interface VisualizationListItem { +export interface VisualizationListItem { editUrl: string; icon: string; id: string; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap b/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap rename to src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/_dialog.scss b/src/plugins/visualizations/public/wizard/_dialog.scss similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/_dialog.scss rename to src/plugins/visualizations/public/wizard/_dialog.scss diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/_index.scss b/src/plugins/visualizations/public/wizard/_index.scss similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/_index.scss rename to src/plugins/visualizations/public/wizard/_index.scss diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/index.ts b/src/plugins/visualizations/public/wizard/index.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/index.ts rename to src/plugins/visualizations/public/wizard/index.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.test.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx similarity index 99% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.test.tsx rename to src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx index 2712019e42609..5637aeafc6f14 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.test.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { TypesStart, VisType } from '../vis_types'; import { NewVisModal } from './new_vis_modal'; -import { SavedObjectsStart } from '../../../../../../../core/public'; +import { SavedObjectsStart } from '../../../../core/public'; describe('NewVisModal', () => { const { location } = window; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx similarity index 97% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.tsx rename to src/plugins/visualizations/public/wizard/new_vis_modal.tsx index 7c10001eddb50..448077819bb8d 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/new_vis_modal.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx @@ -23,11 +23,11 @@ import { EuiModal, EuiOverlayMask } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { METRIC_TYPE, UiStatsMetricType } from '@kbn/analytics'; -import { IUiSettingsClient, SavedObjectsStart } from '../../../../../../../core/public'; +import { IUiSettingsClient, SavedObjectsStart } from '../../../../core/public'; import { SearchSelection } from './search_selection'; import { TypeSelection } from './type_selection'; import { TypesStart, VisType, VisTypeAlias } from '../vis_types'; -import { UsageCollectionSetup } from '../../../../../../../plugins/usage_collection/public'; +import { UsageCollectionSetup } from '../../../../plugins/usage_collection/public'; interface TypeSelectionProps { isOpen: boolean; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/search_selection/index.ts b/src/plugins/visualizations/public/wizard/search_selection/index.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/search_selection/index.ts rename to src/plugins/visualizations/public/wizard/search_selection/index.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/search_selection/search_selection.tsx b/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx similarity index 96% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/search_selection/search_selection.tsx rename to src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx index f8eb191dd5f92..c9fb592d1f936 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/search_selection/search_selection.tsx +++ b/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx @@ -21,9 +21,9 @@ import { EuiModalBody, EuiModalHeader, EuiModalHeaderTitle } from '@elastic/eui' import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { IUiSettingsClient, SavedObjectsStart } from '../../../../../../../../core/public'; +import { IUiSettingsClient, SavedObjectsStart } from '../../../../../core/public'; -import { SavedObjectFinderUi } from '../../../../../../../../plugins/saved_objects/public'; +import { SavedObjectFinderUi } from '../../../../../plugins/saved_objects/public'; import { VisType } from '../../vis_types'; interface SearchSelectionProps { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/show_new_vis.tsx b/src/plugins/visualizations/public/wizard/show_new_vis.tsx similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/show_new_vis.tsx rename to src/plugins/visualizations/public/wizard/show_new_vis.tsx diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/index.ts b/src/plugins/visualizations/public/wizard/type_selection/index.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/index.ts rename to src/plugins/visualizations/public/wizard/type_selection/index.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/new_vis_help.test.tsx b/src/plugins/visualizations/public/wizard/type_selection/new_vis_help.test.tsx similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/new_vis_help.test.tsx rename to src/plugins/visualizations/public/wizard/type_selection/new_vis_help.test.tsx diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/new_vis_help.tsx b/src/plugins/visualizations/public/wizard/type_selection/new_vis_help.tsx similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/new_vis_help.tsx rename to src/plugins/visualizations/public/wizard/type_selection/new_vis_help.tsx diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/type_selection.tsx b/src/plugins/visualizations/public/wizard/type_selection/type_selection.tsx similarity index 98% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/type_selection.tsx rename to src/plugins/visualizations/public/wizard/type_selection/type_selection.tsx index c17c994f12a3f..acf562d5bf363 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/type_selection.tsx +++ b/src/plugins/visualizations/public/wizard/type_selection/type_selection.tsx @@ -35,8 +35,8 @@ import { EuiTitle, } from '@elastic/eui'; -import { memoizeLast } from '../../../../../../visualizations/public/np_ready/public/legacy/memoize'; -import { VisTypeAlias } from '../../../../../../visualizations/public'; +import { memoizeLast } from '../../legacy/memoize'; +import { VisTypeAlias } from '../../vis_types/vis_type_alias_registry'; import { NewVisHelp } from './new_vis_help'; import { VisHelpText } from './vis_help_text'; import { VisTypeIcon } from './vis_type_icon'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/vis_help_text.tsx b/src/plugins/visualizations/public/wizard/type_selection/vis_help_text.tsx similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/vis_help_text.tsx rename to src/plugins/visualizations/public/wizard/type_selection/vis_help_text.tsx diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/vis_type_icon.tsx b/src/plugins/visualizations/public/wizard/type_selection/vis_type_icon.tsx similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/wizard/type_selection/vis_type_icon.tsx rename to src/plugins/visualizations/public/wizard/type_selection/vis_type_icon.tsx diff --git a/tasks/config/run.js b/tasks/config/run.js index 50417ebd8333d..dca0f69c35668 100644 --- a/tasks/config/run.js +++ b/tasks/config/run.js @@ -58,6 +58,7 @@ module.exports = function(grunt) { '--env.name=development', '--plugins.initialize=false', '--optimize.bundleFilter=tests', + '--optimize.validateSyntaxOfNodeModules=false', '--server.port=5610', '--migrations.skip=true', ]; diff --git a/test/accessibility/apps/discover.ts b/test/accessibility/apps/discover.ts index cf3d37d29b491..086b13ecee2b3 100644 --- a/test/accessibility/apps/discover.ts +++ b/test/accessibility/apps/discover.ts @@ -63,7 +63,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { await a11y.testAppSnapshot(); }); - it.skip('Click on new to clear the search', async () => { + it('Click on new to clear the search', async () => { await PageObjects.discover.clickNewSearchButton(); await a11y.testAppSnapshot(); }); @@ -121,7 +121,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { await a11y.testAppSnapshot(); }); - it.skip('Add more fields from sidebar', async () => { + it('Add more fields from sidebar', async () => { for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) { await PageObjects.discover.clickFieldListItem(columnName); await PageObjects.discover.clickFieldListPlusFilter(columnName, value); diff --git a/test/api_integration/services/supertest.js b/test/api_integration/services/supertest.ts similarity index 83% rename from test/api_integration/services/supertest.js rename to test/api_integration/services/supertest.ts index b53b4ae0ef32f..d5505c080468a 100644 --- a/test/api_integration/services/supertest.js +++ b/test/api_integration/services/supertest.ts @@ -16,18 +16,18 @@ * specific language governing permissions and limitations * under the License. */ - +import { FtrProviderContext } from 'test/functional/ftr_provider_context'; import { format as formatUrl } from 'url'; import supertestAsPromised from 'supertest-as-promised'; -export function KibanaSupertestProvider({ getService }) { +export function KibanaSupertestProvider({ getService }: FtrProviderContext) { const config = getService('config'); const kibanaServerUrl = formatUrl(config.get('servers.kibana')); return supertestAsPromised(kibanaServerUrl); } -export function ElasticsearchSupertestProvider({ getService }) { +export function ElasticsearchSupertestProvider({ getService }: FtrProviderContext) { const config = getService('config'); const elasticSearchServerUrl = formatUrl(config.get('servers.elasticsearch')); return supertestAsPromised(elasticSearchServerUrl); diff --git a/test/common/fixtures/plugins/newsfeed/newsfeed_simulation.ts b/test/common/fixtures/plugins/newsfeed/newsfeed_simulation.ts index 4bf92868b0eca..5aa44b48f9d59 100644 --- a/test/common/fixtures/plugins/newsfeed/newsfeed_simulation.ts +++ b/test/common/fixtures/plugins/newsfeed/newsfeed_simulation.ts @@ -44,7 +44,7 @@ export async function initPlugin(server: Hapi.Server, path: string) { ], }, }, - handler: newsfeedHandler, + handler: newsfeedHandler as Hapi.Lifecycle.Method, }); server.route({ diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js index 6cba92349ffc0..850b2773b5025 100644 --- a/test/functional/apps/discover/_discover.js +++ b/test/functional/apps/discover/_discover.js @@ -199,5 +199,22 @@ export default function({ getService, getPageObjects }) { expect(rowData.startsWith('Sep 22, 2015 @ 16:50:13.253')).to.be.ok(); }); }); + describe('usage of discover:searchOnPageLoad', () => { + it('should fetch data from ES initially when discover:searchOnPageLoad is false', async function() { + await kibanaServer.uiSettings.replace({ 'discover:searchOnPageLoad': false }); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.header.awaitKibanaChrome(); + + expect(await PageObjects.discover.getNrOfFetches()).to.be(0); + }); + + it('should not fetch data from ES initially when discover:searchOnPageLoad is true', async function() { + await kibanaServer.uiSettings.replace({ 'discover:searchOnPageLoad': true }); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.header.awaitKibanaChrome(); + + expect(await PageObjects.discover.getNrOfFetches()).to.be(1); + }); + }); }); } diff --git a/test/functional/apps/discover/_field_visualize.ts b/test/functional/apps/discover/_field_visualize.ts index 4a72d5bb34716..46238bf143290 100644 --- a/test/functional/apps/discover/_field_visualize.ts +++ b/test/functional/apps/discover/_field_visualize.ts @@ -17,13 +17,16 @@ * under the License. */ +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); + const filterBar = getService('filterBar'); const inspector = getService('inspector'); const kibanaServer = getService('kibanaServer'); const log = getService('log'); + const queryBar = getService('queryBar'); const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); const defaultSettings = { defaultIndex: 'logstash-*', @@ -76,5 +79,76 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { await inspector.expectTableData(expectedTableData); await inspector.close(); }); + + it('should preserve app filters in visualize', async () => { + await filterBar.addFilter('bytes', 'is between', '3500', '4000'); + await PageObjects.discover.clickFieldListItem('geo.src'); + log.debug('visualize a geo.src field with filter applied'); + await PageObjects.discover.clickFieldListItemVisualize('geo.src'); + await PageObjects.header.waitUntilLoadingHasFinished(); + + expect(await filterBar.hasFilter('bytes', '3,500 to 4,000')).to.be(true); + const expectedTableData = [ + ['CN', '133'], + ['IN', '120'], + ['US', '58'], + ['ID', '28'], + ['BD', '25'], + ['BR', '22'], + ['EG', '14'], + ['NG', '14'], + ['PK', '13'], + ['IR', '12'], + ['PH', '12'], + ['JP', '11'], + ['RU', '11'], + ['DE', '8'], + ['FR', '8'], + ['MX', '8'], + ['TH', '8'], + ['TR', '8'], + ['CA', '6'], + ['SA', '6'], + ]; + await inspector.open(); + await inspector.expectTableData(expectedTableData); + await inspector.close(); + }); + + it('should preserve query in visualize', async () => { + await queryBar.setQuery('machine.os : ios'); + await queryBar.submitQuery(); + await PageObjects.discover.clickFieldListItem('geo.dest'); + log.debug('visualize a geo.dest field with query applied'); + await PageObjects.discover.clickFieldListItemVisualize('geo.dest'); + await PageObjects.header.waitUntilLoadingHasFinished(); + + expect(await queryBar.getQueryString()).to.equal('machine.os : ios'); + const expectedTableData = [ + ['CN', '519'], + ['IN', '495'], + ['US', '275'], + ['ID', '82'], + ['PK', '75'], + ['BR', '71'], + ['NG', '54'], + ['BD', '51'], + ['JP', '47'], + ['MX', '47'], + ['IR', '44'], + ['PH', '44'], + ['RU', '42'], + ['ET', '33'], + ['TH', '33'], + ['EG', '32'], + ['VN', '32'], + ['DE', '31'], + ['FR', '30'], + ['GB', '30'], + ]; + await inspector.open(); + await inspector.expectTableData(expectedTableData); + await inspector.close(); + }); }); } diff --git a/test/functional/apps/management/_scripted_fields.js b/test/functional/apps/management/_scripted_fields.js index 65291c3c4772c..d85d5a952d3ec 100644 --- a/test/functional/apps/management/_scripted_fields.js +++ b/test/functional/apps/management/_scripted_fields.js @@ -388,7 +388,7 @@ export default function({ getService, getPageObjects }) { await log.debug('filter by "Sep 17, 2015 @ 23:00" in the expanded scripted field list'); await PageObjects.discover.clickFieldListPlusFilter( scriptedPainlessFieldName2, - '2015-09-17 23:00' + '1442531297065' ); await PageObjects.header.waitUntilLoadingHasFinished(); diff --git a/test/functional/apps/visualize/_area_chart.js b/test/functional/apps/visualize/_area_chart.js index ebbeb01cbc917..8f2012d7f184d 100644 --- a/test/functional/apps/visualize/_area_chart.js +++ b/test/functional/apps/visualize/_area_chart.js @@ -554,5 +554,76 @@ export default function({ getService, getPageObjects }) { }); }); }); + + describe('date histogram interval', () => { + before(async () => { + await PageObjects.visualize.loadSavedVisualization('Visualization AreaChart'); + await PageObjects.visChart.waitForVisualization(); + }); + + beforeEach(async () => { + const fromTime = 'Sep 20, 2015 @ 00:00:00.000'; + const toTime = 'Sep 20, 2015 @ 23:30:00.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + }); + + it('should update collapsed accordion label when time range is changed', async () => { + const accordionLabel = await find.byCssSelector( + '[data-test-subj="visEditorAggAccordion2"] .visEditorSidebar__aggGroupAccordionButtonContent' + ); + let accordionLabelText = await accordionLabel.getVisibleText(); + expect(accordionLabelText).to.include.string('per 30 minutes'); + const fromTime = 'Sep 20, 2015 @ 08:30:00.000'; + const toTime = 'Sep 20, 2015 @ 23:30:00.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + accordionLabelText = await accordionLabel.getVisibleText(); + expect(accordionLabelText).to.include.string('per 10 minutes'); + }); + + describe('expanded accordion', () => { + before(async () => await PageObjects.visEditor.toggleAccordion('visEditorAggAccordion2')); + + it('should update label inside the opened accordion when scaled to milliseconds', async () => { + const isHelperScaledLabelExists = await find.existsByCssSelector( + '[data-test-subj="currentlyScaledText"]' + ); + expect(isHelperScaledLabelExists).to.be(false); + await PageObjects.visEditor.setInterval('Millisecond'); + const helperScaledLabelText = await testSubjects.getVisibleText('currentlyScaledText'); + expect(helperScaledLabelText).to.include.string('to 10 minutes'); + }); + + it('should display updated scaled label text after time range is changed', async () => { + await PageObjects.visEditor.setInterval('Millisecond'); + const isHelperScaledLabelExists = await find.existsByCssSelector( + '[data-test-subj="currentlyScaledText"]' + ); + expect(isHelperScaledLabelExists).to.be(true); + let helperScaledLabelText = await testSubjects.getVisibleText('currentlyScaledText'); + expect(helperScaledLabelText).to.include.string('to 10 minutes'); + const fromTime = 'Sep 20, 2015 @ 22:30:00.000'; + const toTime = 'Sep 20, 2015 @ 23:30:00.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + helperScaledLabelText = await testSubjects.getVisibleText('currentlyScaledText'); + expect(helperScaledLabelText).to.include.string('to 30 seconds'); + }); + + it('should update scaled label text after custom interval is set and time range is changed', async () => { + await PageObjects.visEditor.setInterval('10s', { type: 'custom' }); + await testSubjects.clickWhenNotDisabled('visualizeEditorRenderButton'); + const isHelperScaledLabelExists = await find.existsByCssSelector( + '[data-test-subj="currentlyScaledText"]' + ); + expect(isHelperScaledLabelExists).to.be(true); + let helperScaledLabelText = await testSubjects.getVisibleText('currentlyScaledText'); + expect(helperScaledLabelText).to.include.string('to 10 minutes'); + const fromTime = 'Sep 20, 2015 @ 21:30:00.000'; + const toTime = 'Sep 20, 2015 @ 23:30:00.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + helperScaledLabelText = await testSubjects.getVisibleText('currentlyScaledText'); + expect(helperScaledLabelText).to.include.string('to minute'); + }); + }); + }); }); } diff --git a/test/functional/fixtures/es_archiver/visualize/data.json b/test/functional/fixtures/es_archiver/visualize/data.json index 845e9a5e08825..abca5a98bf7fd 100644 --- a/test/functional/fixtures/es_archiver/visualize/data.json +++ b/test/functional/fixtures/es_archiver/visualize/data.json @@ -69,6 +69,27 @@ } } +{ + "type": "doc", + "value": { + "id": "visualization:Visualization-AreaChart", + "index": ".kibana", + "source": { + "type": "visualization", + "visualization": { + "description": "AreaChart", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}" + }, + "title": "Visualization AreaChart", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Visualization AreaChart\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"labels\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"now-15m\",\"to\":\"now\"},\"useNormalizedEsInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}}]}" + } + } + } +} + { "type": "doc", "value": { diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index a126cfb1bce4b..10652ce3ec4b2 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -112,6 +112,7 @@ export function DiscoverPageProvider({ getService, getPageObjects }: FtrProvider public async clickNewSearchButton() { await testSubjects.click('discoverNewButton'); + await header.waitUntilLoadingHasFinished(); } public async clickSaveSearchButton() { @@ -207,7 +208,7 @@ export function DiscoverPageProvider({ getService, getPageObjects }: FtrProvider public async getAllFieldNames() { const sidebar = await testSubjects.find('discover-sidebar'); const $ = await sidebar.parseDomContent(); - return $('.sidebar-item[attr-field]') + return $('.dscSidebar__item[attr-field]') .toArray() .map(field => $(field) @@ -249,13 +250,17 @@ export function DiscoverPageProvider({ getService, getPageObjects }: FtrProvider } public async expectMissingFieldListItemVisualize(field: string) { - await testSubjects.missingOrFail(`fieldVisualize-${field}`, { allowHidden: true }); + await testSubjects.missingOrFail(`fieldVisualize-${field}`); } public async clickFieldListPlusFilter(field: string, value: string) { - // this method requires the field details to be open from clickFieldListItem() + const plusFilterTestSubj = `plus-${field}-${value}`; + if (!(await testSubjects.exists(plusFilterTestSubj))) { + // field has to be open + await this.clickFieldListItem(field); + } // testSubjects.find doesn't handle spaces in the data-test-subj value - await testSubjects.click(`plus-${field}-${value}`); + await testSubjects.click(plusFilterTestSubj); await header.waitUntilLoadingHasFinished(); } @@ -300,6 +305,11 @@ export function DiscoverPageProvider({ getService, getPageObjects }: FtrProvider 'true' ); } + public async getNrOfFetches() { + const el = await find.byCssSelector('[data-fetch-counter]'); + const nr = await el.getAttribute('data-fetch-counter'); + return Number(nr); + } } return new DiscoverPage(); diff --git a/test/plugin_functional/config.js b/test/plugin_functional/config.js index 7017c01cc5634..c7fa0f40e1d0c 100644 --- a/test/plugin_functional/config.js +++ b/test/plugin_functional/config.js @@ -32,7 +32,6 @@ export default async function({ readConfigFile }) { return { testFiles: [ - require.resolve('./test_suites/app_plugins'), require.resolve('./test_suites/custom_visualizations'), require.resolve('./test_suites/panel_actions'), require.resolve('./test_suites/embeddable_explorer'), diff --git a/test/plugin_functional/plugins/core_plugin_static_assets/kibana.json b/test/plugin_functional/plugins/core_plugin_static_assets/kibana.json new file mode 100644 index 0000000000000..6f9fb94e9b49c --- /dev/null +++ b/test/plugin_functional/plugins/core_plugin_static_assets/kibana.json @@ -0,0 +1,7 @@ +{ + "id": "corePluginStaticAssets", + "version": "0.0.1", + "kibanaVersion": "kibana", + "server": false, + "ui": true +} diff --git a/test/plugin_functional/plugins/core_plugin_static_assets/package.json b/test/plugin_functional/plugins/core_plugin_static_assets/package.json new file mode 100644 index 0000000000000..304e1b11fde42 --- /dev/null +++ b/test/plugin_functional/plugins/core_plugin_static_assets/package.json @@ -0,0 +1,17 @@ +{ + "name": "corePluginStaticAssets", + "version": "1.0.0", + "main": "target/test/plugin_functional/plugins/core_plugin_static_assets", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "kbn": "node ../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.7.2" + } +} diff --git a/test/plugin_functional/plugins/core_plugin_static_assets/public/assets/chart.svg b/test/plugin_functional/plugins/core_plugin_static_assets/public/assets/chart.svg new file mode 100644 index 0000000000000..44553960a5cce --- /dev/null +++ b/test/plugin_functional/plugins/core_plugin_static_assets/public/assets/chart.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/types.ts b/test/plugin_functional/plugins/core_plugin_static_assets/public/index.ts similarity index 87% rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/types.ts rename to test/plugin_functional/plugins/core_plugin_static_assets/public/index.ts index 302bf5165777c..2bdb40cf19cb5 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/types.ts +++ b/test/plugin_functional/plugins/core_plugin_static_assets/public/index.ts @@ -17,7 +17,6 @@ * under the License. */ -export interface IndexPatternRef { - id: string; - title: string; -} +import { CorePluginStaticAssets } from './plugin'; + +export const plugin = () => new CorePluginStaticAssets(); diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/index.js b/test/plugin_functional/plugins/core_plugin_static_assets/public/plugin.tsx similarity index 80% rename from test/plugin_functional/plugins/kbn_tp_custom_visualizations/index.js rename to test/plugin_functional/plugins/core_plugin_static_assets/public/plugin.tsx index b2497a824ba2b..d9f3d62937584 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/index.js +++ b/test/plugin_functional/plugins/core_plugin_static_assets/public/plugin.tsx @@ -17,10 +17,11 @@ * under the License. */ -export default function(kibana) { - return new kibana.Plugin({ - uiExports: { - hacks: ['plugins/kbn_tp_custom_visualizations/self_changing_vis/self_changing_vis'], - }, - }); +import { Plugin, CoreSetup } from 'kibana/public'; + +export class CorePluginStaticAssets implements Plugin { + public setup(core: CoreSetup, deps: {}) {} + + public start() {} + public stop() {} } diff --git a/test/plugin_functional/plugins/core_plugin_static_assets/tsconfig.json b/test/plugin_functional/plugins/core_plugin_static_assets/tsconfig.json new file mode 100644 index 0000000000000..4a564ee1e5578 --- /dev/null +++ b/test/plugin_functional/plugins/core_plugin_static_assets/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../../tsconfig.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "../../../../typings/**/*" + ], + "exclude": [] +} diff --git a/test/plugin_functional/plugins/kbn_top_nav/kibana.json b/test/plugin_functional/plugins/kbn_top_nav/kibana.json new file mode 100644 index 0000000000000..b274e80b9ef65 --- /dev/null +++ b/test/plugin_functional/plugins/kbn_top_nav/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "kbn_top_nav", + "version": "0.0.1", + "kibanaVersion": "kibana", + "configPath": ["kbn_top_nav"], + "server": false, + "ui": true, + "requiredPlugins": ["navigation"] +} \ No newline at end of file diff --git a/test/plugin_functional/plugins/kbn_top_nav/package.json b/test/plugin_functional/plugins/kbn_top_nav/package.json new file mode 100644 index 0000000000000..510d681a4a75c --- /dev/null +++ b/test/plugin_functional/plugins/kbn_top_nav/package.json @@ -0,0 +1,18 @@ +{ + "name": "kbn_top_nav", + "version": "1.0.0", + "main": "target/test/plugin_functional/plugins/kbn_top_nav", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "kbn": "node ../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.7.2" + } +} + diff --git a/test/plugin_functional/plugins/kbn_tp_top_nav/public/top_nav.tsx b/test/plugin_functional/plugins/kbn_top_nav/public/application.tsx similarity index 71% rename from test/plugin_functional/plugins/kbn_tp_top_nav/public/top_nav.tsx rename to test/plugin_functional/plugins/kbn_top_nav/public/application.tsx index f77db4fe1654e..0f65e6159796b 100644 --- a/test/plugin_functional/plugins/kbn_tp_top_nav/public/top_nav.tsx +++ b/test/plugin_functional/plugins/kbn_top_nav/public/application.tsx @@ -18,11 +18,15 @@ */ import React from 'react'; -import './initialize'; -import { npStart } from 'ui/new_platform'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { AppMountParameters } from 'kibana/public'; +import { AppPluginDependencies } from './types'; -export const AppWithTopNav = () => { - const { TopNavMenu } = npStart.plugins.navigation.ui; +export const renderApp = ( + depsStart: AppPluginDependencies, + { appBasePath, element }: AppMountParameters +) => { + const { TopNavMenu } = depsStart.navigation.ui; const config = [ { id: 'new', @@ -32,10 +36,12 @@ export const AppWithTopNav = () => { testId: 'demoNewButton', }, ]; - - return ( + render( Hey - + , + element ); + + return () => unmountComponentAtNode(element); }; diff --git a/test/plugin_functional/plugins/kbn_tp_sample_app_plugin/public/app.js b/test/plugin_functional/plugins/kbn_top_nav/public/index.ts similarity index 75% rename from test/plugin_functional/plugins/kbn_tp_sample_app_plugin/public/app.js rename to test/plugin_functional/plugins/kbn_top_nav/public/index.ts index a7a516bb0cdbd..bd478f1dd3bdb 100644 --- a/test/plugin_functional/plugins/kbn_tp_sample_app_plugin/public/app.js +++ b/test/plugin_functional/plugins/kbn_top_nav/public/index.ts @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -import 'ui/autoload/all'; -import chrome from 'ui/chrome'; +import { PluginInitializer } from 'kibana/public'; +import { TopNavTestPlugin, TopNavTestPluginSetup, TopNavTestPluginStart } from './plugin'; -chrome.setRootTemplate('
Super simple app plugin
'); +export const plugin: PluginInitializer = () => + new TopNavTestPlugin(); diff --git a/test/plugin_functional/plugins/kbn_top_nav/public/plugin.tsx b/test/plugin_functional/plugins/kbn_top_nav/public/plugin.tsx new file mode 100644 index 0000000000000..a433de98357fb --- /dev/null +++ b/test/plugin_functional/plugins/kbn_top_nav/public/plugin.tsx @@ -0,0 +1,65 @@ +/* + * 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, AppMountParameters } from 'kibana/public'; +import { NavigationPublicPluginSetup } from '../../../../../src/plugins/navigation/public'; +import { AppPluginDependencies } from './types'; + +export class TopNavTestPlugin implements Plugin { + public setup(core: CoreSetup, { navigation }: { navigation: NavigationPublicPluginSetup }) { + const customExtension = { + id: 'registered-prop', + label: 'Registered Button', + description: 'Registered Demo', + run() {}, + testId: 'demoRegisteredNewButton', + }; + + navigation.registerMenuItem(customExtension); + + const customDiscoverExtension = { + id: 'registered-discover-prop', + label: 'Registered Discover Button', + description: 'Registered Discover Demo', + run() {}, + testId: 'demoDiscoverRegisteredNewButton', + appName: 'discover', + }; + + navigation.registerMenuItem(customDiscoverExtension); + + core.application.register({ + id: 'topNavMenu', + title: 'Top nav menu example', + async mount(params: AppMountParameters) { + const { renderApp } = await import('./application'); + const services = await core.getStartServices(); + return renderApp(services[1] as AppPluginDependencies, params); + }, + }); + + return {}; + } + + public start() {} + public stop() {} +} + +export type TopNavTestPluginSetup = ReturnType; +export type TopNavTestPluginStart = ReturnType; diff --git a/test/plugin_functional/test_suites/app_plugins/index.js b/test/plugin_functional/plugins/kbn_top_nav/public/types.ts similarity index 81% rename from test/plugin_functional/test_suites/app_plugins/index.js rename to test/plugin_functional/plugins/kbn_top_nav/public/types.ts index 83faa7377c7ac..c70a78bedb54f 100644 --- a/test/plugin_functional/test_suites/app_plugins/index.js +++ b/test/plugin_functional/plugins/kbn_top_nav/public/types.ts @@ -17,8 +17,8 @@ * under the License. */ -export default function({ loadTestFile }) { - describe('app plugins', () => { - loadTestFile(require.resolve('./app_navigation')); - }); +import { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public'; + +export interface AppPluginDependencies { + navigation: NavigationPublicPluginStart; } diff --git a/test/plugin_functional/plugins/kbn_tp_top_nav/tsconfig.json b/test/plugin_functional/plugins/kbn_top_nav/tsconfig.json similarity index 100% rename from test/plugin_functional/plugins/kbn_tp_top_nav/tsconfig.json rename to test/plugin_functional/plugins/kbn_top_nav/tsconfig.json diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/kibana.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/kibana.json new file mode 100644 index 0000000000000..622cbd80090ba --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "kbn_tp_custom_visualizations", + "version": "0.0.1", + "kibanaVersion": "kibana", + "requiredPlugins": [ + "visualizations" + ], + "server": false, + "ui": true +} diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json index 38e6fe8f76531..4971ff1731198 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json @@ -1,6 +1,7 @@ { "name": "kbn_tp_custom_visualizations", "version": "1.0.0", + "main": "target/test/plugin_functional/plugins/kbn_tp_custom_visualizations", "kibana": { "version": "kibana", "templateVersion": "1.0.0" @@ -9,5 +10,13 @@ "dependencies": { "@elastic/eui": "22.1.1", "react": "^16.12.0" + }, + "scripts": { + "kbn": "node ../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "@kbn/plugin-helpers": "9.0.2", + "typescript": "3.7.2" } } diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy.ts b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/index.ts similarity index 68% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy.ts rename to test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/index.ts index 5357c2458e3b0..cb821a2698479 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy.ts +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/index.ts @@ -17,8 +17,14 @@ * under the License. */ -// eslint-disable-next-line -import { npSetup, npStart } from 'ui/new_platform'; +import { PluginInitializer } from 'kibana/public'; +import { + CustomVisualizationsPublicPlugin, + CustomVisualizationsSetup, + CustomVisualizationsStart, +} from './plugin'; -export const setup = npSetup.plugins.embeddable; -export const start = npStart.plugins.embeddable; +export { CustomVisualizationsPublicPlugin as Plugin }; + +export const plugin: PluginInitializer = () => + new CustomVisualizationsPublicPlugin(); diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/plugin.ts b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/plugin.ts new file mode 100644 index 0000000000000..1be4aa9ee42ae --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/plugin.ts @@ -0,0 +1,61 @@ +/* + * 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/public'; +import { VisualizationsSetup } from '../../../../../src/plugins/visualizations/public'; +import { SelfChangingEditor } from './self_changing_vis/self_changing_editor'; +import { SelfChangingComponent } from './self_changing_vis/self_changing_components'; + +export interface SetupDependencies { + visualizations: VisualizationsSetup; +} + +export class CustomVisualizationsPublicPlugin + implements Plugin { + public setup(core: CoreSetup, setupDeps: SetupDependencies) { + setupDeps.visualizations.createReactVisualization({ + name: 'self_changing_vis', + title: 'Self Changing Vis', + icon: 'controlsHorizontal', + description: + 'This visualization is able to change its own settings, that you could also set in the editor.', + visConfig: { + component: SelfChangingComponent, + defaults: { + counter: 0, + }, + }, + editorConfig: { + optionTabs: [ + { + name: 'options', + title: 'Options', + editor: SelfChangingEditor, + }, + ], + }, + requestHandler: 'none', + }); + } + + public start() {} + public stop() {} +} + +export type CustomVisualizationsSetup = ReturnType; +export type CustomVisualizationsStart = ReturnType; diff --git a/src/legacy/core_plugins/visualizations/index.ts b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.tsx similarity index 59% rename from src/legacy/core_plugins/visualizations/index.ts rename to test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.tsx index a2779cfe4346d..2f01908122457 100644 --- a/src/legacy/core_plugins/visualizations/index.ts +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.tsx @@ -17,18 +17,32 @@ * under the License. */ -import { resolve } from 'path'; -import { LegacyPluginInitializer } from '../../../../src/legacy/types'; +import React, { useEffect } from 'react'; -export const visualizations: LegacyPluginInitializer = kibana => - new kibana.Plugin({ - id: 'visualizations', - publicDir: resolve(__dirname, 'public'), - require: [], - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - }, +import { EuiBadge } from '@elastic/eui'; + +interface SelfChangingComponentProps { + renderComplete: () => {}; + visParams: { + counter: number; + }; +} + +export function SelfChangingComponent(props: SelfChangingComponentProps) { + useEffect(() => { + props.renderComplete(); }); -// eslint-disable-next-line import/no-default-export -export default visualizations; + return ( +
+ {}} + data-test-subj="counter" + onClickAriaLabel="Increase counter" + color="primary" + > + {props.visParams.counter} + +
+ ); +} diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.js b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx similarity index 76% rename from test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.js rename to test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx index fa3a0c8b9f6fe..d3f66d708603c 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.js +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx @@ -20,10 +20,15 @@ import React from 'react'; import { EuiFieldNumber, EuiFormRow } from '@elastic/eui'; +import { VisOptionsProps } from '../../../../../../src/legacy/core_plugins/vis_default_editor/public/vis_options_props'; -export class SelfChangingEditor extends React.Component { - onCounterChange = ev => { - this.props.setValue('counter', parseInt(ev.target.value)); +interface CounterParams { + counter: number; +} + +export class SelfChangingEditor extends React.Component> { + onCounterChange = (ev: any) => { + this.props.setValue('counter', parseInt(ev.target.value, 10)); }; render() { diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_vis.js b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_vis.js index 643d15c982792..7aa12ea7a1130 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_vis.js +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_vis.js @@ -20,9 +20,9 @@ import { SelfChangingEditor } from './self_changing_editor'; import { SelfChangingComponent } from './self_changing_components'; -import { setup as visualizations } from '../../../../../../src/legacy/core_plugins/visualizations/public/np_ready/public/legacy'; +import { npSetup } from '../../../../../../src/legacy/ui/public/new_platform'; -visualizations.createReactVisualization({ +npSetup.plugins.visualizations.createReactVisualization({ name: 'self_changing_vis', title: 'Self Changing Vis', icon: 'controlsHorizontal', diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/tsconfig.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/tsconfig.json new file mode 100644 index 0000000000000..d8096d9aab27a --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true, + "types": [ + "node", + "jest", + "react" + ] + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "../../../../typings/**/*", + ], + "exclude": [] +} \ No newline at end of file diff --git a/test/plugin_functional/plugins/kbn_tp_sample_app_plugin/package.json b/test/plugin_functional/plugins/kbn_tp_sample_app_plugin/package.json deleted file mode 100644 index 2537bb9a7ed5c..0000000000000 --- a/test/plugin_functional/plugins/kbn_tp_sample_app_plugin/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "kbn_tp_sample_app_plugin", - "version": "1.0.0", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0" -} diff --git a/test/plugin_functional/plugins/kbn_tp_top_nav/package.json b/test/plugin_functional/plugins/kbn_tp_top_nav/package.json deleted file mode 100644 index 7102d24d3292d..0000000000000 --- a/test/plugin_functional/plugins/kbn_tp_top_nav/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "kbn_tp_top_nav", - "version": "1.0.0", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0" -} diff --git a/test/plugin_functional/plugins/kbn_tp_top_nav/public/app.js b/test/plugin_functional/plugins/kbn_tp_top_nav/public/app.js deleted file mode 100644 index e7f97e68c086d..0000000000000 --- a/test/plugin_functional/plugins/kbn_tp_top_nav/public/app.js +++ /dev/null @@ -1,54 +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 React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; - -import { uiModules } from 'ui/modules'; -import chrome from 'ui/chrome'; - -// This is required so some default styles and required scripts/Angular modules are loaded, -// or the timezone setting is correctly applied. -import 'ui/autoload/all'; - -import { AppWithTopNav } from './top_nav'; - -const app = uiModules.get('apps/topnavDemoPlugin', ['kibana']); - -app.config($locationProvider => { - $locationProvider.html5Mode({ - enabled: false, - requireBase: false, - rewriteLinks: false, - }); -}); - -function RootController($scope, $element) { - const domNode = $element[0]; - - // render react to DOM - render(, domNode); - - // unmount react on controller destroy - $scope.$on('$destroy', () => { - unmountComponentAtNode(domNode); - }); -} - -chrome.setRootController('topnavDemoPlugin', RootController); diff --git a/test/plugin_functional/plugins/kbn_tp_top_nav/public/initialize.js b/test/plugin_functional/plugins/kbn_tp_top_nav/public/initialize.js deleted file mode 100644 index d46e47f6d248a..0000000000000 --- a/test/plugin_functional/plugins/kbn_tp_top_nav/public/initialize.js +++ /dev/null @@ -1,41 +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 { npSetup } from 'ui/new_platform'; - -const customExtension = { - id: 'registered-prop', - label: 'Registered Button', - description: 'Registered Demo', - run() {}, - testId: 'demoRegisteredNewButton', -}; - -npSetup.plugins.navigation.registerMenuItem(customExtension); - -const customDiscoverExtension = { - id: 'registered-discover-prop', - label: 'Registered Discover Button', - description: 'Registered Discover Demo', - run() {}, - testId: 'demoDiscoverRegisteredNewButton', - appName: 'discover', -}; - -npSetup.plugins.navigation.registerMenuItem(customDiscoverExtension); diff --git a/test/plugin_functional/test_suites/app_plugins/app_navigation.js b/test/plugin_functional/test_suites/app_plugins/app_navigation.js deleted file mode 100644 index bb39e52287556..0000000000000 --- a/test/plugin_functional/test_suites/app_plugins/app_navigation.js +++ /dev/null @@ -1,38 +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 expect from '@kbn/expect'; - -export default function({ getService, getPageObjects }) { - const appsMenu = getService('appsMenu'); - const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['common', 'header', 'home']); - - describe('app navigation', function describeIndexTests() { - before(async () => { - await PageObjects.common.navigateToApp('settings'); - }); - - it('should show nav link that navigates to the app', async () => { - await appsMenu.clickLink('Test Plugin App'); - const pluginContent = await testSubjects.find('pluginContent'); - expect(await pluginContent.getVisibleText()).to.be('Super simple app plugin'); - }); - }); -} diff --git a/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts b/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts index 82267d73782af..8ddd0ff96ba8f 100644 --- a/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts +++ b/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts @@ -25,6 +25,7 @@ import '../../../../test/plugin_functional/plugins/core_provider_plugin/types'; export default function({ getService, getPageObjects }: PluginFunctionalProviderContext) { const PageObjects = getPageObjects(['common']); const browser = getService('browser'); + const supertest = getService('supertest'); describe('ui plugins', function() { describe('loading', function describeIndexTests() { @@ -97,5 +98,47 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider ).to.be('/core_plugin_b/system_request says: "System request? false"'); }); }); + + describe('Plugin static assets', function() { + it('exposes static assets from "public/assets" folder', async () => { + await supertest.get('/plugins/corePluginStaticAssets/assets/chart.svg').expect(200); + }); + + it('returns 404 if not found', async function() { + await supertest.get('/plugins/corePluginStaticAssets/assets/not-a-chart.svg').expect(404); + }); + + it('does not expose folder content', async function() { + await supertest.get('/plugins/corePluginStaticAssets/assets/').expect(403); + }); + + it('does not allow file tree traversing', async function() { + await supertest.get('/plugins/corePluginStaticAssets/assets/../../kibana.json').expect(404); + }); + + it('generates "etag" & "last-modified" headers', async () => { + const response = await supertest + .get('/plugins/corePluginStaticAssets/assets/chart.svg') + .expect(200); + + expect(response.header).to.have.property('etag'); + expect(response.header).to.have.property('last-modified'); + }); + + it('generates the same "etag" & "last-modified" for the same asset', async () => { + const firstResponse = await supertest + .get('/plugins/corePluginStaticAssets/assets/chart.svg') + .expect(200); + + expect(firstResponse.header).to.have.property('etag'); + + const secondResponse = await supertest + .get('/plugins/corePluginStaticAssets/assets/chart.svg') + .expect(200); + + expect(secondResponse.header.etag).to.be(firstResponse.header.etag); + expect(secondResponse.header['last-modified']).to.be(firstResponse.header['last-modified']); + }); + }); }); } diff --git a/test/plugin_functional/test_suites/custom_visualizations/self_changing_vis.js b/test/plugin_functional/test_suites/custom_visualizations/self_changing_vis.js index ef6f0a626bd15..83258a1ca3bdc 100644 --- a/test/plugin_functional/test_suites/custom_visualizations/self_changing_vis.js +++ b/test/plugin_functional/test_suites/custom_visualizations/self_changing_vis.js @@ -28,11 +28,7 @@ export default function({ getService, getPageObjects }) { return await testSubjects.getVisibleText('counter'); } - async function getEditorValue() { - return await testSubjects.getAttribute('counterEditor', 'value'); - } - - describe.skip('self changing vis', function describeIndexTests() { + describe('self changing vis', function describeIndexTests() { before(async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('self_changing_vis'); @@ -45,16 +41,17 @@ export default function({ getService, getPageObjects }) { const isApplyEnabled = await PageObjects.visEditor.isApplyEnabled(); expect(isApplyEnabled).to.be(true); await PageObjects.visEditor.clickGo(); + await renderable.waitForRender(); const counter = await getCounterValue(); expect(counter).to.be('10'); }); - it('should allow changing params from within the vis', async () => { + it.skip('should allow changing params from within the vis', async () => { await testSubjects.click('counter'); await renderable.waitForRender(); const visValue = await getCounterValue(); expect(visValue).to.be('11'); - const editorValue = await getEditorValue(); + const editorValue = await testSubjects.getAttribute('counterEditor', 'value'); expect(editorValue).to.be('11'); // If changing a param from within the vis it should immediately apply and not bring editor in an unchanged state const isApplyEnabled = await PageObjects.visEditor.isApplyEnabled(); diff --git a/typings/index.d.ts b/typings/index.d.ts index 1c58a92a046df..6d97aca4024c3 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -23,6 +23,27 @@ declare module '*.html' { export default template; } +declare module '*.png' { + const content: string; + // eslint-disable-next-line import/no-default-export + export default content; +} + +declare module '*.svg' { + const content: string; + // eslint-disable-next-line import/no-default-export + export default content; +} + +// allow JSON files to be imported directly without lint errors +// see: https://github.com/palantir/tslint/issues/1264#issuecomment-228433367 +// and: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#arbitrary-expressions-are-forbidden-in-export-assignments-in-ambient-contexts +declare module '*.json' { + const json: any; + // eslint-disable-next-line import/no-default-export + export default json; +} + type MethodKeysOf = { [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never; }[keyof T]; @@ -37,3 +58,7 @@ type DeeplyMockedKeys = { : DeeplyMockedKeys; } & T; + +type Writable = { + -readonly [K in keyof T]: T[K]; +}; diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 2a28e349ace99..ae8d61769b14c 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -16,7 +16,7 @@ "xpack.features": "plugins/features", "xpack.fileUpload": "plugins/file_upload", "xpack.graph": ["legacy/plugins/graph", "plugins/graph"], - "xpack.grokDebugger": "legacy/plugins/grokdebugger", + "xpack.grokDebugger": "plugins/grokdebugger", "xpack.idxMgmt": "plugins/index_management", "xpack.indexLifecycleMgmt": "legacy/plugins/index_lifecycle_management", "xpack.infra": "plugins/infra", diff --git a/x-pack/index.js b/x-pack/index.js index fb14b3dc10a4d..6fab13d726fa6 100644 --- a/x-pack/index.js +++ b/x-pack/index.js @@ -10,7 +10,6 @@ import { monitoring } from './legacy/plugins/monitoring'; import { reporting } from './legacy/plugins/reporting'; import { security } from './legacy/plugins/security'; import { tilemap } from './legacy/plugins/tilemap'; -import { grokdebugger } from './legacy/plugins/grokdebugger'; import { dashboardMode } from './legacy/plugins/dashboard_mode'; import { logstash } from './legacy/plugins/logstash'; import { beats } from './legacy/plugins/beats_management'; @@ -44,7 +43,6 @@ module.exports = function(kibana) { spaces(kibana), security(kibana), tilemap(kibana), - grokdebugger(kibana), dashboardMode(kibana), logstash(kibana), beats(kibana), diff --git a/x-pack/legacy/plugins/apm/index.ts b/x-pack/legacy/plugins/apm/index.ts index d1f7ce325d23e..d2383acd45eba 100644 --- a/x-pack/legacy/plugins/apm/index.ts +++ b/x-pack/legacy/plugins/apm/index.ts @@ -105,10 +105,17 @@ export const apm: LegacyPluginInitializer = kibana => { privileges: { all: { app: ['apm', 'kibana'], - api: ['apm', 'apm_write', 'actions-read', 'alerting-read'], + api: [ + 'apm', + 'apm_write', + 'actions-read', + 'actions-all', + 'alerting-read', + 'alerting-all' + ], catalogue: ['apm'], savedObject: { - all: ['action', 'action_task_params'], + all: ['alert', 'action', 'action_task_params'], read: [] }, ui: [ @@ -124,13 +131,27 @@ export const apm: LegacyPluginInitializer = kibana => { }, read: { app: ['apm', 'kibana'], - api: ['apm', 'actions-read', 'alerting-read'], + api: [ + 'apm', + 'actions-read', + 'actions-all', + 'alerting-read', + 'alerting-all' + ], catalogue: ['apm'], savedObject: { - all: ['action', 'action_task_params'], + all: ['alert', 'action', 'action_task_params'], read: [] }, - ui: ['show', 'alerting:show', 'actions:show'] + ui: [ + 'show', + 'alerting:show', + 'actions:show', + 'alerting:save', + 'actions:save', + 'alerting:delete', + 'actions:delete' + ] } } }); diff --git a/x-pack/legacy/plugins/apm/mappings.json b/x-pack/legacy/plugins/apm/mappings.json index 5d14ae03f9a33..1e906dd2a5967 100644 --- a/x-pack/legacy/plugins/apm/mappings.json +++ b/x-pack/legacy/plugins/apm/mappings.json @@ -9,7 +9,7 @@ "properties": { "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -19,15 +19,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -35,7 +35,7 @@ "properties": { "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -43,15 +43,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } } @@ -65,7 +65,7 @@ "properties": { "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -75,15 +75,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -91,15 +91,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -107,15 +107,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } } @@ -129,7 +129,7 @@ "properties": { "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -139,15 +139,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -155,15 +155,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -171,15 +171,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } } @@ -193,7 +193,7 @@ "properties": { "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -203,15 +203,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -219,15 +219,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -235,15 +235,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } } @@ -257,7 +257,7 @@ "properties": { "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -267,15 +267,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -283,15 +283,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -299,15 +299,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } } @@ -321,7 +321,7 @@ "properties": { "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -331,15 +331,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -347,15 +347,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -363,15 +363,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } } @@ -385,7 +385,7 @@ "properties": { "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -395,15 +395,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -411,15 +411,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -427,15 +427,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } } @@ -449,7 +449,7 @@ "properties": { "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -459,15 +459,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -475,15 +475,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } }, @@ -491,15 +491,15 @@ "properties": { "composite": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "name": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 }, "version": { "type": "keyword", - "ignore_above": 256 + "ignore_above": 1024 } } } diff --git a/x-pack/legacy/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap index 88d9d7864576f..2b1f835a14f4a 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap +++ b/x-pack/legacy/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap @@ -15,6 +15,10 @@ exports[`Home component should render services 1`] = ` "chrome": Object { "setBreadcrumbs": [Function], }, + "docLinks": Object { + "DOC_LINK_VERSION": "0", + "ELASTIC_WEBSITE_URL": "https://www.elastic.co/", + }, "http": Object { "basePath": Object { "prepend": [Function], @@ -27,9 +31,6 @@ exports[`Home component should render services 1`] = ` }, }, }, - "packageInfo": Object { - "version": "0", - }, "plugins": Object {}, } } @@ -55,6 +56,10 @@ exports[`Home component should render traces 1`] = ` "chrome": Object { "setBreadcrumbs": [Function], }, + "docLinks": Object { + "DOC_LINK_VERSION": "0", + "ELASTIC_WEBSITE_URL": "https://www.elastic.co/", + }, "http": Object { "basePath": Object { "prepend": [Function], @@ -67,9 +72,6 @@ exports[`Home component should render traces 1`] = ` }, }, }, - "packageInfo": Object { - "version": "0", - }, "plugins": Object {}, } } diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/index.tsx index 77ae67b71e1b6..c3d426a6275a7 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/index.tsx @@ -28,8 +28,10 @@ export function ServiceDetails({ tab }: Props) { const canSaveAlerts = !!plugin.core.application.capabilities.apm[ 'alerting:save' ]; + const isAlertingPluginEnabled = 'alerting' in plugin.plugins; - const isAlertingAvailable = canReadAlerts || canSaveAlerts; + const isAlertingAvailable = + isAlertingPluginEnabled && (canReadAlerts || canSaveAlerts); return (
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx index 54a1b4347e29b..7bdc6aebbd9a0 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx @@ -13,6 +13,7 @@ import React, { useRef, useState } from 'react'; +import { debounce } from 'lodash'; import { isRumAgentName } from '../../../../../../../plugins/apm/common/agent_name'; import { AGENT_NAME } from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; import { @@ -171,8 +172,13 @@ export function Cytoscape({ } }); }; + // debounce hover tracking so it doesn't spam telemetry with redundant events + const trackNodeEdgeHover = debounce( + () => trackApmEvent({ metric: 'service_map_node_or_edge_hover' }), + 1000 + ); const mouseoverHandler: cytoscape.EventHandler = event => { - trackApmEvent({ metric: 'service_map_node_or_edge_hover' }); + trackNodeEdgeHover(); event.target.addClass('hover'); event.target.connectedEdges().addClass('nodeHover'); }; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx index de058d6ef973a..c1afa433cb614 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx @@ -58,7 +58,7 @@ export function NoServicesMessage({ historicalDataFound, status }: Props) {

{i18n.translate('xpack.apm.servicesTable.7xUpgradeServerMessage', { defaultMessage: `Upgrading from a pre-7.x version? Make sure you've also upgraded - your APM server instance(s) to at least 7.0.` + your APM Server instance(s) to at least 7.0.` })}

diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap index 209b88f73b9e2..227becb9a9c4f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap @@ -19,7 +19,7 @@ exports[`NoServicesMessage status: pending and historicalDataFound: false 1`] =

Upgrading from a pre-7.x version? Make sure you've also upgraded - your APM server instance(s) to at least 7.0. + your APM Server instance(s) to at least 7.0.

You may also have old data that needs to be migrated. @@ -64,7 +64,7 @@ exports[`NoServicesMessage status: success and historicalDataFound: false 1`] =

Upgrading from a pre-7.x version? Make sure you've also upgraded - your APM server instance(s) to at least 7.0. + your APM Server instance(s) to at least 7.0.

You may also have old data that needs to be migrated. diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap index d85c83661ea49..dd0c50af2b03f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap @@ -72,7 +72,7 @@ NodeList [ >

Upgrading from a pre-7.x version? Make sure you've also upgraded - your APM server instance(s) to at least 7.0. + your APM Server instance(s) to at least 7.0.

You may also have old data that needs to be migrated. diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx new file mode 100644 index 0000000000000..938962cc9dd18 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx @@ -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 React from 'react'; +import { storiesOf } from '@storybook/react'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { TraceAPIResponse } from '../../../../../../../../../plugins/apm/server/lib/traces/get_trace'; +import { WaterfallContainer } from './index'; +import { + location, + urlParams, + simpleTrace, + traceWithErrors, + traceChildStartBeforeParent +} from './waterfallContainer.stories.data'; +import { getWaterfall } from './Waterfall/waterfall_helpers/waterfall_helpers'; + +storiesOf('app/TransactionDetails/Waterfall', module).add( + 'simple', + () => { + const waterfall = getWaterfall( + simpleTrace as TraceAPIResponse, + '975c8d5bfd1dd20b' + ); + return ( + + ); + }, + { info: { source: false } } +); + +storiesOf('app/TransactionDetails/Waterfall', module).add( + 'with errors', + () => { + const waterfall = getWaterfall( + (traceWithErrors as unknown) as TraceAPIResponse, + '975c8d5bfd1dd20b' + ); + return ( + + ); + }, + { info: { source: false } } +); + +storiesOf('app/TransactionDetails/Waterfall', module).add( + 'child starts before parent', + () => { + const waterfall = getWaterfall( + traceChildStartBeforeParent as TraceAPIResponse, + '975c8d5bfd1dd20b' + ); + return ( + + ); + }, + { info: { source: false } } +); diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts new file mode 100644 index 0000000000000..835183e73b298 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts @@ -0,0 +1,1647 @@ +/* + * 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 { Location } from 'history'; +import { IUrlParams } from '../../../../../context/UrlParamsContext/types'; + +export const location = { + pathname: '/services/opbeans-go/transactions/view', + search: + '?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0&kuery=service.name%253A%2520%2522opbeans-java%2522%2520or%2520service.name%2520%253A%2520%2522opbeans-go%2522&traceId=513d33fafe99bbe6134749310c9b5322&transactionId=975c8d5bfd1dd20b&transactionName=GET%20%2Fapi%2Forders&transactionType=request', + hash: '' +} as Location; + +export const urlParams = { + start: '2020-03-22T15:16:38.742Z', + end: '2020-03-23T15:16:38.742Z', + rangeFrom: 'now-24h', + rangeTo: 'now', + refreshPaused: true, + refreshInterval: 0, + page: 0, + transactionId: '975c8d5bfd1dd20b', + traceId: '513d33fafe99bbe6134749310c9b5322', + kuery: 'service.name: "opbeans-java" or service.name : "opbeans-go"', + transactionName: 'GET /api/orders', + transactionType: 'request', + processorEvent: 'transaction', + serviceName: 'opbeans-go' +} as IUrlParams; + +export const simpleTrace = { + trace: { + items: [ + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1 + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT' + }, + internal: { + sampler: { + value: 46 + } + }, + source: { + ip: '172.19.0.13' + }, + processor: { + name: 'transaction', + event: 'transaction' + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: '172.19.0.9', + full: 'http://172.19.0.9:3000/api/orders' + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + type: 'apm-server', + version: '8.0.0', + version_major: 8 + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0' + }, + service: { + node: { + name: + '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2' + }, + language: { + name: 'Java', + version: '10.0.2' + }, + version: 'None' + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux' + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64' + }, + http: { + request: { + headers: { + Accept: ['*/*'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['172.19.0.9:3000'], + 'Accept-Encoding': ['gzip, deflate'] + }, + method: 'get', + socket: { + encrypted: false, + remote_address: '172.19.0.13' + }, + body: { + original: '[REDACTED]' + } + }, + response: { + headers: { + 'Transfer-Encoding': ['chunked'], + Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], + 'Content-Type': ['application/json;charset=ISO-8859-1'] + }, + status_code: 200, + finished: true, + headers_sent: true + }, + version: '1.1' + }, + client: { + ip: '172.19.0.13' + }, + transaction: { + duration: { + us: 18842 + }, + result: 'HTTP 2xx', + name: 'DispatcherServlet#doGet', + id: '49809ad3c26adf74', + span_count: { + dropped: 0, + started: 1 + }, + type: 'request', + sampled: true + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Other', + device: { + name: 'Other' + } + }, + timestamp: { + us: 1584975868785000 + } + }, + { + parent: { + id: 'fc107f7b556eb49b' + }, + agent: { + name: 'go', + version: '1.7.2' + }, + processor: { + name: 'transaction', + event: 'transaction' + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: + 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29' + }, + environment: 'production', + framework: { + name: 'gin', + version: 'v1.4.0' + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1' + }, + language: { + name: 'go', + version: 'go1.14.1' + }, + version: 'None' + }, + transaction: { + duration: { + us: 16597 + }, + result: 'HTTP 2xx', + name: 'GET /api/orders', + id: '975c8d5bfd1dd20b', + span_count: { + dropped: 0, + started: 1 + }, + type: 'request', + sampled: true + }, + timestamp: { + us: 1584975868787052 + } + }, + { + parent: { + id: 'daae24d83c269918' + }, + agent: { + name: 'python', + version: '5.5.2' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + timestamp: { + us: 1584975868788603 + }, + processor: { + name: 'transaction', + event: 'transaction' + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders' + }, + '@timestamp': '2020-03-23T15:04:28.788Z', + service: { + node: { + name: + 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51' + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13' + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10' + }, + language: { + name: 'python', + version: '3.6.10' + }, + version: 'None' + }, + transaction: { + result: 'HTTP 2xx', + duration: { + us: 14648 + }, + name: 'GET opbeans.views.orders', + span_count: { + dropped: 0, + started: 1 + }, + id: '6fb0ff7365b87298', + type: 'request', + sampled: true + } + }, + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + parent: { + id: '49809ad3c26adf74' + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1 + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT' + }, + internal: { + sampler: { + value: 44 + } + }, + destination: { + address: 'opbeans-go', + port: 3000 + }, + processor: { + name: 'transaction', + event: 'span' + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + type: 'apm-server', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + version: '8.0.0', + version_major: 8 + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0' + }, + service: { + node: { + name: + '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2' + }, + language: { + name: 'Java', + version: '10.0.2' + }, + version: 'None' + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux' + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64' + }, + connection: { + hash: + "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}" + }, + transaction: { + id: '49809ad3c26adf74' + }, + timestamp: { + us: 1584975868785273 + }, + span: { + duration: { + us: 17530 + }, + subtype: 'http', + name: 'GET opbeans-go', + destination: { + service: { + resource: 'opbeans-go:3000', + name: 'http://opbeans-go:3000', + type: 'external' + } + }, + http: { + response: { + status_code: 200 + }, + url: { + original: 'http://opbeans-go:3000/api/orders' + } + }, + id: 'fc107f7b556eb49b', + type: 'external' + } + }, + { + parent: { + id: '975c8d5bfd1dd20b' + }, + agent: { + name: 'go', + version: '1.7.2' + }, + processor: { + name: 'transaction', + event: 'span' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: + 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29' + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1' + }, + language: { + name: 'go', + version: 'go1.14.1' + }, + version: 'None' + }, + transaction: { + id: '975c8d5bfd1dd20b' + }, + timestamp: { + us: 1584975868787174 + }, + span: { + duration: { + us: 16250 + }, + subtype: 'http', + destination: { + service: { + resource: 'opbeans-python:3000', + name: 'http://opbeans-python:3000', + type: 'external' + } + }, + name: 'GET opbeans-python:3000', + http: { + response: { + status_code: 200 + }, + url: { + original: 'http://opbeans-python:3000/api/orders' + } + }, + id: 'daae24d83c269918', + type: 'external' + } + }, + { + container: { + id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51' + }, + parent: { + id: '6fb0ff7365b87298' + }, + agent: { + name: 'python', + version: '5.5.2' + }, + processor: { + name: 'transaction', + event: 'span' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.790Z', + service: { + node: { + name: + 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51' + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13' + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10' + }, + language: { + name: 'python', + version: '3.6.10' + }, + version: 'None' + }, + transaction: { + id: '6fb0ff7365b87298' + }, + timestamp: { + us: 1584975868790080 + }, + span: { + duration: { + us: 2519 + }, + subtype: 'postgresql', + name: 'SELECT FROM opbeans_order', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db' + } + }, + action: 'query', + id: 'c9407abb4d08ead1', + type: 'db', + sync: true, + db: { + statement: + 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', + type: 'sql' + } + } + } + ], + exceedsMax: false, + errorDocs: [] + }, + errorsPerTransaction: {} +}; + +export const traceWithErrors = { + trace: { + items: [ + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1 + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT' + }, + internal: { + sampler: { + value: 46 + } + }, + source: { + ip: '172.19.0.13' + }, + processor: { + name: 'transaction', + event: 'transaction' + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: '172.19.0.9', + full: 'http://172.19.0.9:3000/api/orders' + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + type: 'apm-server', + version: '8.0.0', + version_major: 8 + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0' + }, + service: { + node: { + name: + '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2' + }, + language: { + name: 'Java', + version: '10.0.2' + }, + version: 'None' + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux' + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64' + }, + http: { + request: { + headers: { + Accept: ['*/*'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['172.19.0.9:3000'], + 'Accept-Encoding': ['gzip, deflate'] + }, + method: 'get', + socket: { + encrypted: false, + remote_address: '172.19.0.13' + }, + body: { + original: '[REDACTED]' + } + }, + response: { + headers: { + 'Transfer-Encoding': ['chunked'], + Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], + 'Content-Type': ['application/json;charset=ISO-8859-1'] + }, + status_code: 200, + finished: true, + headers_sent: true + }, + version: '1.1' + }, + client: { + ip: '172.19.0.13' + }, + transaction: { + duration: { + us: 18842 + }, + result: 'HTTP 2xx', + name: 'DispatcherServlet#doGet', + id: '49809ad3c26adf74', + span_count: { + dropped: 0, + started: 1 + }, + type: 'request', + sampled: true + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Other', + device: { + name: 'Other' + } + }, + timestamp: { + us: 1584975868785000 + } + }, + { + parent: { + id: 'fc107f7b556eb49b' + }, + agent: { + name: 'go', + version: '1.7.2' + }, + processor: { + name: 'transaction', + event: 'transaction' + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: + 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29' + }, + environment: 'production', + framework: { + name: 'gin', + version: 'v1.4.0' + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1' + }, + language: { + name: 'go', + version: 'go1.14.1' + }, + version: 'None' + }, + transaction: { + duration: { + us: 16597 + }, + result: 'HTTP 2xx', + name: 'GET /api/orders', + id: '975c8d5bfd1dd20b', + span_count: { + dropped: 0, + started: 1 + }, + type: 'request', + sampled: true + }, + timestamp: { + us: 1584975868787052 + } + }, + { + parent: { + id: 'daae24d83c269918' + }, + agent: { + name: 'python', + version: '5.5.2' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + timestamp: { + us: 1584975868788603 + }, + processor: { + name: 'transaction', + event: 'transaction' + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders' + }, + '@timestamp': '2020-03-23T15:04:28.788Z', + service: { + node: { + name: + 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51' + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13' + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10' + }, + language: { + name: 'python', + version: '3.6.10' + }, + version: 'None' + }, + transaction: { + result: 'HTTP 2xx', + duration: { + us: 14648 + }, + name: 'GET opbeans.views.orders', + span_count: { + dropped: 0, + started: 1 + }, + id: '6fb0ff7365b87298', + type: 'request', + sampled: true + } + }, + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + parent: { + id: '49809ad3c26adf74' + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1 + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT' + }, + internal: { + sampler: { + value: 44 + } + }, + destination: { + address: 'opbeans-go', + port: 3000 + }, + processor: { + name: 'transaction', + event: 'span' + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + type: 'apm-server', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + version: '8.0.0', + version_major: 8 + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0' + }, + service: { + node: { + name: + '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2' + }, + language: { + name: 'Java', + version: '10.0.2' + }, + version: 'None' + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux' + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64' + }, + connection: { + hash: + "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}" + }, + transaction: { + id: '49809ad3c26adf74' + }, + timestamp: { + us: 1584975868785273 + }, + span: { + duration: { + us: 17530 + }, + subtype: 'http', + name: 'GET opbeans-go', + destination: { + service: { + resource: 'opbeans-go:3000', + name: 'http://opbeans-go:3000', + type: 'external' + } + }, + http: { + response: { + status_code: 200 + }, + url: { + original: 'http://opbeans-go:3000/api/orders' + } + }, + id: 'fc107f7b556eb49b', + type: 'external' + } + }, + { + parent: { + id: '975c8d5bfd1dd20b' + }, + agent: { + name: 'go', + version: '1.7.2' + }, + processor: { + name: 'transaction', + event: 'span' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: + 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29' + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1' + }, + language: { + name: 'go', + version: 'go1.14.1' + }, + version: 'None' + }, + transaction: { + id: '975c8d5bfd1dd20b' + }, + timestamp: { + us: 1584975868787174 + }, + span: { + duration: { + us: 16250 + }, + subtype: 'http', + destination: { + service: { + resource: 'opbeans-python:3000', + name: 'http://opbeans-python:3000', + type: 'external' + } + }, + name: 'GET opbeans-python:3000', + http: { + response: { + status_code: 200 + }, + url: { + original: 'http://opbeans-python:3000/api/orders' + } + }, + id: 'daae24d83c269918', + type: 'external' + } + }, + { + container: { + id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51' + }, + parent: { + id: '6fb0ff7365b87298' + }, + agent: { + name: 'python', + version: '5.5.2' + }, + processor: { + name: 'transaction', + event: 'span' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.790Z', + service: { + node: { + name: + 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51' + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13' + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10' + }, + language: { + name: 'python', + version: '3.6.10' + }, + version: 'None' + }, + transaction: { + id: '6fb0ff7365b87298' + }, + timestamp: { + us: 1584975868790080 + }, + span: { + duration: { + us: 2519 + }, + subtype: 'postgresql', + name: 'SELECT FROM opbeans_order', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db' + } + }, + action: 'query', + id: 'c9407abb4d08ead1', + type: 'db', + sync: true, + db: { + statement: + 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', + type: 'sql' + } + } + } + ], + exceedsMax: false, + errorDocs: [ + { + parent: { + id: '975c8d5bfd1dd20b' + }, + agent: { + name: 'go', + version: '1.7.2' + }, + error: { + culprit: 'logrusMiddleware', + log: { + level: 'error', + message: 'GET //api/products (502)' + }, + id: '1f3cb98206b5c54225cb7c8908a658da', + grouping_key: '4dba2ff58fe6c036a5dee2ce411e512a' + }, + processor: { + name: 'error', + event: 'error' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T16:04:28.787Z', + service: { + node: { + name: + 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29' + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1' + }, + language: { + name: 'go', + version: 'go1.14.1' + }, + version: 'None' + }, + transaction: { + id: '975c8d5bfd1dd20b', + sampled: false + }, + timestamp: { + us: 1584975868787052 + } + }, + { + parent: { + id: '6fb0ff7365b87298' + }, + agent: { + name: 'python', + version: '5.5.2' + }, + error: { + culprit: 'logrusMiddleware', + log: { + level: 'error', + message: 'GET //api/products (502)' + }, + id: '1f3cb98206b5c54225cb7c8908a658d2', + grouping_key: '4dba2ff58fe6c036a5dee2ce411e512a' + }, + processor: { + name: 'error', + event: 'error' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T16:04:28.790Z', + service: { + node: { + name: + 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29' + }, + environment: 'production', + name: 'opbeans-python', + runtime: { + name: 'gc', + version: 'go1.14.1' + }, + version: 'None' + }, + transaction: { + id: '6fb0ff7365b87298', + sampled: false + }, + timestamp: { + us: 1584975868790000 + } + } + ] + }, + errorsPerTransaction: { + '975c8d5bfd1dd20b': 1, + '6fb0ff7365b87298': 1 + } +}; + +export const traceChildStartBeforeParent = { + trace: { + items: [ + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1 + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT' + }, + internal: { + sampler: { + value: 46 + } + }, + source: { + ip: '172.19.0.13' + }, + processor: { + name: 'transaction', + event: 'transaction' + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: '172.19.0.9', + full: 'http://172.19.0.9:3000/api/orders' + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + type: 'apm-server', + version: '8.0.0', + version_major: 8 + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0' + }, + service: { + node: { + name: + '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2' + }, + language: { + name: 'Java', + version: '10.0.2' + }, + version: 'None' + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux' + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64' + }, + http: { + request: { + headers: { + Accept: ['*/*'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['172.19.0.9:3000'], + 'Accept-Encoding': ['gzip, deflate'] + }, + method: 'get', + socket: { + encrypted: false, + remote_address: '172.19.0.13' + }, + body: { + original: '[REDACTED]' + } + }, + response: { + headers: { + 'Transfer-Encoding': ['chunked'], + Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], + 'Content-Type': ['application/json;charset=ISO-8859-1'] + }, + status_code: 200, + finished: true, + headers_sent: true + }, + version: '1.1' + }, + client: { + ip: '172.19.0.13' + }, + transaction: { + duration: { + us: 18842 + }, + result: 'HTTP 2xx', + name: 'DispatcherServlet#doGet', + id: '49809ad3c26adf74', + span_count: { + dropped: 0, + started: 1 + }, + type: 'request', + sampled: true + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Other', + device: { + name: 'Other' + } + }, + timestamp: { + us: 1584975868785000 + } + }, + { + parent: { + id: 'fc107f7b556eb49b' + }, + agent: { + name: 'go', + version: '1.7.2' + }, + processor: { + name: 'transaction', + event: 'transaction' + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: + 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29' + }, + environment: 'production', + framework: { + name: 'gin', + version: 'v1.4.0' + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1' + }, + language: { + name: 'go', + version: 'go1.14.1' + }, + version: 'None' + }, + transaction: { + duration: { + us: 16597 + }, + result: 'HTTP 2xx', + name: 'GET /api/orders', + id: '975c8d5bfd1dd20b', + span_count: { + dropped: 0, + started: 1 + }, + type: 'request', + sampled: true + }, + timestamp: { + us: 1584975868787052 + } + }, + { + parent: { + id: 'daae24d83c269918' + }, + agent: { + name: 'python', + version: '5.5.2' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + timestamp: { + us: 1584975868780000 + }, + processor: { + name: 'transaction', + event: 'transaction' + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders' + }, + '@timestamp': '2020-03-23T15:04:28.788Z', + service: { + node: { + name: + 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51' + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13' + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10' + }, + language: { + name: 'python', + version: '3.6.10' + }, + version: 'None' + }, + transaction: { + result: 'HTTP 2xx', + duration: { + us: 1464 + }, + name: 'I started before my parent 😰', + span_count: { + dropped: 0, + started: 1 + }, + id: '6fb0ff7365b87298', + type: 'request', + sampled: true + } + }, + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + parent: { + id: '49809ad3c26adf74' + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1 + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT' + }, + internal: { + sampler: { + value: 44 + } + }, + destination: { + address: 'opbeans-go', + port: 3000 + }, + processor: { + name: 'transaction', + event: 'span' + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + type: 'apm-server', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + version: '8.0.0', + version_major: 8 + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0' + }, + service: { + node: { + name: + '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e' + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2' + }, + language: { + name: 'Java', + version: '10.0.2' + }, + version: 'None' + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux' + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64' + }, + connection: { + hash: + "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}" + }, + transaction: { + id: '49809ad3c26adf74' + }, + timestamp: { + us: 1584975868785273 + }, + span: { + duration: { + us: 17530 + }, + subtype: 'http', + name: 'GET opbeans-go', + destination: { + service: { + resource: 'opbeans-go:3000', + name: 'http://opbeans-go:3000', + type: 'external' + } + }, + http: { + response: { + status_code: 200 + }, + url: { + original: 'http://opbeans-go:3000/api/orders' + } + }, + id: 'fc107f7b556eb49b', + type: 'external' + } + }, + { + parent: { + id: '975c8d5bfd1dd20b' + }, + agent: { + name: 'go', + version: '1.7.2' + }, + processor: { + name: 'transaction', + event: 'span' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: + 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29' + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1' + }, + language: { + name: 'go', + version: 'go1.14.1' + }, + version: 'None' + }, + transaction: { + id: '975c8d5bfd1dd20b' + }, + timestamp: { + us: 1584975868787174 + }, + span: { + duration: { + us: 16250 + }, + subtype: 'http', + destination: { + service: { + resource: 'opbeans-python:3000', + name: 'http://opbeans-python:3000', + type: 'external' + } + }, + name: 'I am his 👇🏻 parent 😡', + http: { + response: { + status_code: 200 + }, + url: { + original: 'http://opbeans-python:3000/api/orders' + } + }, + id: 'daae24d83c269918', + type: 'external' + } + }, + { + container: { + id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51' + }, + parent: { + id: '6fb0ff7365b87298' + }, + agent: { + name: 'python', + version: '5.5.2' + }, + processor: { + name: 'transaction', + event: 'span' + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322' + }, + '@timestamp': '2020-03-23T15:04:28.790Z', + service: { + node: { + name: + 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51' + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13' + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10' + }, + language: { + name: 'python', + version: '3.6.10' + }, + version: 'None' + }, + transaction: { + id: '6fb0ff7365b87298' + }, + timestamp: { + us: 1584975868781000 + }, + span: { + duration: { + us: 2519 + }, + subtype: 'postgresql', + name: 'I am using my parents skew 😇', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db' + } + }, + action: 'query', + id: 'c9407abb4d08ead1', + type: 'db', + sync: true, + db: { + statement: + 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', + type: 'sql' + } + } + } + ], + exceedsMax: false, + errorDocs: [] + }, + errorsPerTransaction: {} +}; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx index 9fcab049e224f..8c2829a515f83 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; import { EuiLink, EuiLinkAnchorProps } from '@elastic/eui'; +import React from 'react'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; // union type constisting of valid guide sections that we link to @@ -17,8 +17,11 @@ interface Props extends EuiLinkAnchorProps { } export function ElasticDocsLink({ section, path, children, ...rest }: Props) { - const { version } = useApmPluginContext().packageInfo; - const href = `https://www.elastic.co/guide/en${section}/${version}${path}`; + const { docLinks } = useApmPluginContext().core; + const baseUrl = docLinks.ELASTIC_WEBSITE_URL; + const version = docLinks.DOC_LINK_VERSION; + const href = `${baseUrl}guide/en${section}/${version}${path}`; + return typeof children === 'function' ? ( children(href) ) : ( diff --git a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx b/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx index 8775dc98c3e1a..cc2e382611628 100644 --- a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx +++ b/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx @@ -12,6 +12,10 @@ const mockCore = { chrome: { setBreadcrumbs: () => {} }, + docLinks: { + DOC_LINK_VERSION: '0', + ELASTIC_WEBSITE_URL: 'https://www.elastic.co/' + }, http: { basePath: { prepend: (path: string) => `/basepath${path}` @@ -36,7 +40,6 @@ const mockConfig: ConfigSchema = { export const mockApmPluginContextValue = { config: mockConfig, core: mockCore, - packageInfo: { version: '0' }, plugins: {} }; diff --git a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/index.tsx b/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/index.tsx index d8934ba4b0151..acc3886586889 100644 --- a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/index.tsx +++ b/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/index.tsx @@ -5,7 +5,7 @@ */ import { createContext } from 'react'; -import { AppMountContext, PackageInfo } from 'kibana/public'; +import { AppMountContext } from 'kibana/public'; import { ApmPluginSetupDeps, ConfigSchema } from '../../new-platform/plugin'; export type AppMountContextBasePath = AppMountContext['core']['http']['basePath']; @@ -13,7 +13,6 @@ export type AppMountContextBasePath = AppMountContext['core']['http']['basePath' export interface ApmPluginContextValue { config: ConfigSchema; core: AppMountContext['core']; - packageInfo: PackageInfo; plugins: ApmPluginSetupDeps; } 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 e30bed1810c1d..80a45ba66c4fa 100644 --- a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx +++ b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx @@ -4,50 +4,49 @@ * you may not use this file except in compliance with the Elastic License. */ +import { ApmRoute } from '@elastic/apm-rum-react'; +import { i18n } from '@kbn/i18n'; import React from 'react'; import ReactDOM from 'react-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 { i18n } from '@kbn/i18n'; -import { AlertType } from '../../../../../plugins/apm/common/alert_types'; import { CoreSetup, CoreStart, - PackageInfo, Plugin, PluginInitializerContext } from '../../../../../../src/core/public'; -import { featureCatalogueEntry } from './featureCatalogueEntry'; import { DataPublicPluginSetup } from '../../../../../../src/plugins/data/public'; import { HomePublicPluginSetup } from '../../../../../../src/plugins/home/public'; +import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { PluginSetupContract as AlertingPluginPublicSetup } from '../../../../../plugins/alerting/public'; +import { AlertType } from '../../../../../plugins/apm/common/alert_types'; import { LicensingPluginSetup } from '../../../../../plugins/licensing/public'; +import { + AlertsContextProvider, + TriggersAndActionsUIPublicPluginSetup +} from '../../../../../plugins/triggers_actions_ui/public'; +import { APMIndicesPermission } from '../components/app/APMIndicesPermission'; import { routes } from '../components/app/Main/route_config'; import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange'; import { UpdateBreadcrumbs } from '../components/app/Main/UpdateBreadcrumbs'; +import { ErrorRateAlertTrigger } from '../components/shared/ErrorRateAlertTrigger'; +import { TransactionDurationAlertTrigger } from '../components/shared/TransactionDurationAlertTrigger'; 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 { createCallApmApi } from '../services/rest/createCallApmApi'; import { createStaticIndexPattern } from '../services/rest/index_pattern'; 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 { setReadonlyBadge } from './updateBadge'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; -import { APMIndicesPermission } from '../components/app/APMIndicesPermission'; -import { - TriggersAndActionsUIPublicPluginSetup, - AlertsContextProvider -} from '../../../../../plugins/triggers_actions_ui/public'; -import { ErrorRateAlertTrigger } from '../components/shared/ErrorRateAlertTrigger'; -import { TransactionDurationAlertTrigger } from '../components/shared/TransactionDurationAlertTrigger'; -import { createCallApmApi } from '../services/rest/createCallApmApi'; export const REACT_APP_ROOT_ID = 'react-apm-root'; @@ -77,6 +76,7 @@ export type ApmPluginSetup = void; export type ApmPluginStart = void; export interface ApmPluginSetupDeps { + alerting?: AlertingPluginPublicSetup; data: DataPublicPluginSetup; home: HomePublicPluginSetup; licensing: LicensingPluginSetup; @@ -124,14 +124,6 @@ export class ApmPlugin // Until then we use a shim to get it from legacy injectedMetadata: const config = getConfigFromInjectedMetadata(); - // Once we're actually an NP plugin we'll get the package info from the - // initializerContext like: - // - // const packageInfo = this.initializerContext.env.packageInfo - // - // Until then we use a shim to get it from legacy metadata: - const packageInfo = metadata as PackageInfo; - // render APM feedback link in global help menu setHelpExtension(core); setReadonlyBadge(core); @@ -140,7 +132,6 @@ export class ApmPlugin const apmPluginContextValue = { config, core, - packageInfo, plugins }; diff --git a/x-pack/legacy/plugins/canvas/.storybook/middleware.js b/x-pack/legacy/plugins/canvas/.storybook/middleware.js index 46ae7ac90f364..8bbd2b6c1a22f 100644 --- a/x-pack/legacy/plugins/canvas/.storybook/middleware.js +++ b/x-pack/legacy/plugins/canvas/.storybook/middleware.js @@ -9,5 +9,5 @@ const path = require('path'); // Extend the Storybook Middleware to include a route to access Legacy UI assets module.exports = function(router) { - router.get('/ui', serve(path.resolve(__dirname, '../../../../../src/legacy/ui/public/assets'))); + router.get('/ui', serve(path.resolve(__dirname, '../../../../../src/core/server/core_app/assets'))); }; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts index 538aa9f74e2a6..f5836fe91e040 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore -import { MAP_SAVED_OBJECT_TYPE } from '../../../maps/common/constants'; -import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/visualizations/public'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../../plugins/maps/public'; +import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../../src/plugins/visualizations/public'; import { LENS_EMBEDDABLE_TYPE } from '../../../../../plugins/lens/common/constants'; import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/constants'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.ts index 60026adc0998a..2985a68cf855c 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.ts @@ -5,8 +5,8 @@ */ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; -import { TimeRange } from 'src/plugins/data/public'; -import { EmbeddableInput } from 'src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +import { TimeRange, Filter as DataFilter } from 'src/plugins/data/public'; +import { EmbeddableInput } from 'src/plugins/embeddable/public'; import { getQueryFilters } from '../../../public/lib/build_embeddable_filters'; import { Filter, TimeRange as TimeRangeArg } from '../../../types'; import { @@ -15,7 +15,6 @@ import { EmbeddableExpression, } from '../../expression_types'; import { getFunctionHelp } from '../../../i18n'; -import { Filter as DataFilter } from '../../../../../../../src/plugins/data/public'; interface Arguments { id: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts index 78240eee7ce13..4b045b0c5edcf 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts @@ -5,8 +5,8 @@ */ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; -import { TimeRange } from 'src/plugins/data/public'; -import { EmbeddableInput } from 'src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +import { TimeRange, Filter as DataFilter } from 'src/plugins/data/public'; +import { EmbeddableInput } from 'src/plugins/embeddable/public'; import { getQueryFilters } from '../../../public/lib/build_embeddable_filters'; import { Filter, MapCenter, TimeRange as TimeRangeArg } from '../../../types'; import { @@ -15,7 +15,6 @@ import { EmbeddableExpression, } from '../../expression_types'; import { getFunctionHelp } from '../../../i18n'; -import { Filter as DataFilter } from '../../../../../../../src/plugins/data/public'; interface Arguments { id: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts index 9777eaebb36ed..d98fea2ec1be8 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts @@ -5,7 +5,7 @@ */ import { ExpressionFunctionDefinition } from 'src/plugins/expressions'; -import { VisualizeInput } from 'src/legacy/core_plugins/visualizations/public'; +import { VisualizeInput } from 'src/plugins/visualizations/public'; import { EmbeddableTypes, EmbeddableExpressionType, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx index 3cdb6eb460224..817be6e144fc8 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx @@ -14,7 +14,6 @@ import { EmbeddablePanel, EmbeddableFactoryNotFoundError, } from '../../../../../../../src/plugins/embeddable/public'; -import { start } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { EmbeddableExpression } from '../../expression_types/embeddable'; import { RendererStrings } from '../../../i18n'; import { getSavedObjectFinder } from '../../../../../../../src/plugins/saved_objects/public'; @@ -39,8 +38,8 @@ const renderEmbeddable = (embeddableObject: IEmbeddable, domNode: HTMLElement) = ({ const uniqueId = handlers.getElementId(); if (!embeddablesRegistry[uniqueId]) { - const factory = Array.from(start.getEmbeddableFactories()).find( + const factory = Array.from(npStart.plugins.embeddable.getEmbeddableFactories()).find( embeddableFactory => embeddableFactory.type === embeddableType ) as EmbeddableFactory; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts index be0dd6a79292f..4c8de2afd81ad 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { VisualizeInput } from 'src/legacy/core_plugins/visualizations/public'; +import { VisualizeInput } from 'src/plugins/visualizations/public'; export function toExpression(input: VisualizeInput): string { const expressionParts = [] as string[]; diff --git a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx index 576c7c4794b08..08cd3084c35cf 100644 --- a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx @@ -5,13 +5,12 @@ */ import React from 'react'; - +import { npStart } from 'ui/new_platform'; import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutBody, EuiTitle } from '@elastic/eui'; import { SavedObjectFinderUi, SavedObjectMetaData, } from '../../../../../../../src/plugins/saved_objects/public/'; -import { start } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { ComponentStrings } from '../../../i18n'; import { CoreStart } from '../../../../../../../src/core/public'; @@ -27,7 +26,7 @@ export interface Props { export class AddEmbeddableFlyout extends React.Component { onAddPanel = (id: string, savedObjectType: string, name: string) => { - const embeddableFactories = start.getEmbeddableFactories(); + const embeddableFactories = npStart.plugins.embeddable.getEmbeddableFactories(); // Find the embeddable type from the saved object type const found = Array.from(embeddableFactories).find(embeddableFactory => { @@ -43,7 +42,7 @@ export class AddEmbeddableFlyout extends React.Component { }; render() { - const embeddableFactories = start.getEmbeddableFactories(); + const embeddableFactories = npStart.plugins.embeddable.getEmbeddableFactories(); const availableSavedObjects = Array.from(embeddableFactories) .filter(factory => { diff --git a/x-pack/legacy/plugins/graph/index.ts b/x-pack/legacy/plugins/graph/index.ts index 53d32a836cfa1..5c7f8fa46c18b 100644 --- a/x-pack/legacy/plugins/graph/index.ts +++ b/x-pack/legacy/plugins/graph/index.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { i18n } from '@kbn/i18n'; - // @ts-ignore import migrations from './migrations'; import mappings from './mappings.json'; @@ -30,40 +28,5 @@ export const graph: LegacyPluginInitializer = kibana => { .default('configAndData'), }).default(); }, - - init(server) { - server.plugins.xpack_main.registerFeature({ - id: 'graph', - name: i18n.translate('xpack.graph.featureRegistry.graphFeatureName', { - defaultMessage: 'Graph', - }), - order: 1200, - icon: 'graphApp', - navLinkId: 'graph', - app: ['graph', 'kibana'], - catalogue: ['graph'], - validLicenses: ['platinum', 'enterprise', 'trial'], - privileges: { - all: { - app: ['graph', 'kibana'], - catalogue: ['graph'], - savedObject: { - all: ['graph-workspace'], - read: ['index-pattern'], - }, - ui: ['save', 'delete'], - }, - read: { - app: ['graph', 'kibana'], - catalogue: ['graph'], - savedObject: { - all: [], - read: ['index-pattern', 'graph-workspace'], - }, - ui: [], - }, - }, - }); - }, }); }; diff --git a/x-pack/legacy/plugins/grokdebugger/index.js b/x-pack/legacy/plugins/grokdebugger/index.js deleted file mode 100644 index 7803aed739b99..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/index.js +++ /dev/null @@ -1,31 +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 { resolve } from 'path'; -import { PLUGIN } from './common/constants'; -import { registerGrokdebuggerRoutes } from './server/routes/api/grokdebugger'; -import { registerLicenseChecker } from './server/lib/register_license_checker'; - -export const grokdebugger = kibana => - new kibana.Plugin({ - id: PLUGIN.ID, - publicDir: resolve(__dirname, 'public'), - require: ['kibana', 'elasticsearch', 'xpack_main'], - configPrefix: 'xpack.grokdebugger', - config(Joi) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - uiExports: { - devTools: ['plugins/grokdebugger/register'], - home: ['plugins/grokdebugger/register_feature'], - }, - init: server => { - registerLicenseChecker(server); - registerGrokdebuggerRoutes(server); - }, - }); diff --git a/x-pack/legacy/plugins/grokdebugger/public/register.js b/x-pack/legacy/plugins/grokdebugger/public/register.js deleted file mode 100644 index 74679d65e52d2..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/public/register.js +++ /dev/null @@ -1,33 +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 { i18n } from '@kbn/i18n'; -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -import { npSetup, npStart } from 'ui/new_platform'; - -npSetup.plugins.devTools.register({ - order: 6, - title: i18n.translate('xpack.grokDebugger.displayName', { - defaultMessage: 'Grok Debugger', - }), - id: 'grokdebugger', - enableRouting: false, - disabled: !xpackInfo.get('features.grokdebugger.enableLink', false), - tooltipContent: xpackInfo.get('features.grokdebugger.message'), - async mount(context, { element }) { - const licenseCheck = { - showPage: xpackInfo.get('features.grokdebugger.enableLink'), - message: xpackInfo.get('features.grokdebugger.message'), - }; - if (!licenseCheck.showPage) { - npStart.core.notifications.toasts.addDanger(licenseCheck.message); - window.location.hash = '/dev_tools'; - return () => {}; - } - const { renderApp } = await import('./render_app'); - return renderApp(element, npStart); - }, -}); diff --git a/x-pack/legacy/plugins/grokdebugger/public/register_feature.ts b/x-pack/legacy/plugins/grokdebugger/public/register_feature.ts deleted file mode 100644 index 97d2e53ce7836..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/public/register_feature.ts +++ /dev/null @@ -1,34 +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 { i18n } from '@kbn/i18n'; -import { npSetup } from 'ui/new_platform'; -import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; - -const { - plugins: { home }, -} = npSetup; - -home.featureCatalogue.register({ - id: 'grokdebugger', - title: i18n.translate('xpack.grokDebugger.registryProviderTitle', { - defaultMessage: '{grokLogParsingTool} Debugger', - values: { - grokLogParsingTool: 'Grok', - }, - }), - description: i18n.translate('xpack.grokDebugger.registryProviderDescription', { - defaultMessage: - 'Simulate and debug {grokLogParsingTool} patterns for data transformation on ingestion.', - values: { - grokLogParsingTool: 'grok', - }, - }), - icon: 'grokApp', - path: '/app/kibana#/dev_tools/grokdebugger', - showOnHomePage: false, - category: FeatureCatalogueCategory.ADMIN, -}); diff --git a/x-pack/legacy/plugins/grokdebugger/server/lib/call_with_request_factory/call_with_request_factory.js b/x-pack/legacy/plugins/grokdebugger/server/lib/call_with_request_factory/call_with_request_factory.js deleted file mode 100644 index 7359a831994f9..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/server/lib/call_with_request_factory/call_with_request_factory.js +++ /dev/null @@ -1,18 +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 { once } from 'lodash'; - -const callWithRequest = once(server => { - const cluster = server.plugins.elasticsearch.getCluster('data'); - return cluster.callWithRequest; -}); - -export const callWithRequestFactory = (server, request) => { - return (...args) => { - return callWithRequest(server)(request, ...args); - }; -}; diff --git a/x-pack/legacy/plugins/grokdebugger/server/lib/check_license/__tests__/check_license.js b/x-pack/legacy/plugins/grokdebugger/server/lib/check_license/__tests__/check_license.js deleted file mode 100644 index 7e32d68a67ece..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/server/lib/check_license/__tests__/check_license.js +++ /dev/null @@ -1,90 +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 expect from '@kbn/expect'; -import { set } from 'lodash'; -import { checkLicense } from '../check_license'; - -describe('check_license', function() { - let mockLicenseInfo; - beforeEach(() => (mockLicenseInfo = {})); - - describe('license information is undefined', () => { - beforeEach(() => (mockLicenseInfo = undefined)); - - it('should set enableLink to false', () => { - expect(checkLicense(mockLicenseInfo).enableLink).to.be(false); - }); - - it('should set enableAPIRoute to false', () => { - expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is not available', () => { - beforeEach(() => (mockLicenseInfo.isAvailable = () => false)); - - it('should set enableLink to false', () => { - expect(checkLicense(mockLicenseInfo).enableLink).to.be(false); - }); - - it('should set enableAPIRoute to false', () => { - expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is available', () => { - beforeEach( - () => - (mockLicenseInfo = { - isAvailable: () => true, - license: { - getType: () => 'foobar', - }, - }) - ); - - describe('& license is active', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - - it('should set enableLink to true', () => { - expect(checkLicense(mockLicenseInfo).enableLink).to.be(true); - }); - - it('should set enableAPIRoute to true', () => { - expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(true); - }); - - it('should NOT set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.be(undefined); - }); - }); - - describe('& license is expired', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - - it('should set enableLink to false', () => { - expect(checkLicense(mockLicenseInfo).enableLink).to.be(false); - }); - - it('should set enableAPIRoute to false', () => { - expect(checkLicense(mockLicenseInfo).enableAPIRoute).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/grokdebugger/server/lib/check_license/check_license.js b/x-pack/legacy/plugins/grokdebugger/server/lib/check_license/check_license.js deleted file mode 100644 index c7a8d2bbca059..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/server/lib/check_license/check_license.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 { i18n } from '@kbn/i18n'; - -export function checkLicense(xpackLicenseInfo) { - // If, for some reason, we cannot get the license information - // from Elasticsearch, assume worst case and disable the Watcher UI - if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { - return { - enableLink: false, - enableAPIRoute: false, - message: i18n.translate('xpack.grokDebugger.unavailableLicenseInformationMessage', { - defaultMessage: - 'You cannot use the {grokLogParsingTool} Debugger because license information is not available at this time.', - values: { - grokLogParsingTool: 'Grok', - }, - }), - }; - } - - const isLicenseActive = xpackLicenseInfo.license.isActive(); - const licenseType = xpackLicenseInfo.license.getType(); - - // License is not valid - if (!isLicenseActive) { - return { - enableLink: false, - enableAPIRoute: false, - message: i18n.translate('xpack.grokDebugger.licenseHasExpiredMessage', { - defaultMessage: - 'You cannot use the {grokLogParsingTool} Debugger because your {licenseType} license has expired.', - values: { - licenseType, - grokLogParsingTool: 'Grok', - }, - }), - }; - } - - // License is valid and active - return { - enableLink: true, - enableAPIRoute: true, - }; -} diff --git a/x-pack/legacy/plugins/grokdebugger/server/lib/error_wrappers/wrap_es_error.js b/x-pack/legacy/plugins/grokdebugger/server/lib/error_wrappers/wrap_es_error.js deleted file mode 100644 index dfcd4e3b1e17c..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/server/lib/error_wrappers/wrap_es_error.js +++ /dev/null @@ -1,18 +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 Boom from 'boom'; - -/** - * Wraps ES errors into a Boom error response and returns it - * This also handles the permissions issue gracefully - * - * @param err Object ES error - * @return Object Boom error response - */ -export function wrapEsError(err) { - return Boom.boomify(err, { statusCode: err.statusCode }); -} diff --git a/x-pack/legacy/plugins/grokdebugger/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js b/x-pack/legacy/plugins/grokdebugger/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js deleted file mode 100644 index 135317f050774..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js +++ /dev/null @@ -1,64 +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 expect from '@kbn/expect'; -import Boom from 'boom'; -import { licensePreRoutingFactory } from '../license_pre_routing_factory'; - -describe('license_pre_routing_factory', () => { - describe('#grokDebuggerFeaturePreRoutingFactory', () => { - let mockServer; - let mockLicenseCheckResults; - - beforeEach(() => { - mockServer = { - plugins: { - xpack_main: { - info: { - feature: () => ({ - getLicenseCheckResults: () => mockLicenseCheckResults, - }), - }, - }, - }, - }; - }); - - describe('isAvailable is false', () => { - beforeEach(() => { - mockLicenseCheckResults = { - isAvailable: false, - }; - }); - - it('replies with 403', async () => { - const licensePreRouting = licensePreRoutingFactory(mockServer); - const stubRequest = {}; - expect(() => licensePreRouting(stubRequest)).to.throwException(response => { - expect(response).to.be.an(Error); - expect(response.isBoom).to.be(true); - expect(response.output.statusCode).to.be(403); - }); - }); - }); - - describe('isAvailable is true', () => { - beforeEach(() => { - mockLicenseCheckResults = { - isAvailable: true, - }; - }); - - it('replies with forbidden', async () => { - const licensePreRouting = licensePreRoutingFactory(mockServer); - const stubRequest = {}; - expect(() => licensePreRouting(stubRequest)).to.throwException(response => { - expect(response).to.eql(Boom.forbidden()); - }); - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/grokdebugger/server/lib/license_pre_routing_factory/license_pre_routing_factory.js b/x-pack/legacy/plugins/grokdebugger/server/lib/license_pre_routing_factory/license_pre_routing_factory.js deleted file mode 100644 index 88c844de3e2ca..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/server/lib/license_pre_routing_factory/license_pre_routing_factory.js +++ /dev/null @@ -1,24 +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 Boom from 'boom'; -import { PLUGIN } from '../../../common/constants'; - -export const licensePreRoutingFactory = server => { - const xpackMainPlugin = server.plugins.xpack_main; - - // License checking and enable/disable logic - function licensePreRouting() { - const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults(); - if (!licenseCheckResults.enableAPIRoute) { - throw Boom.forbidden(licenseCheckResults.message); - } - - return null; - } - - return licensePreRouting; -}; diff --git a/x-pack/legacy/plugins/grokdebugger/server/lib/register_license_checker/register_license_checker.js b/x-pack/legacy/plugins/grokdebugger/server/lib/register_license_checker/register_license_checker.js deleted file mode 100644 index ee9a9a3ebdece..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/server/lib/register_license_checker/register_license_checker.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status'; -import { checkLicense } from '../check_license'; -import { PLUGIN } from '../../../common/constants'; - -export function registerLicenseChecker(server) { - const xpackMainPlugin = server.plugins.xpack_main; - const grokdebuggerPlugin = server.plugins[PLUGIN.ID]; - - mirrorPluginStatus(xpackMainPlugin, grokdebuggerPlugin); - xpackMainPlugin.status.once('green', () => { - // Register a function that is called whenever the xpack info changes, - // to re-compute the license check results for this plugin - xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense); - }); -} diff --git a/x-pack/legacy/plugins/grokdebugger/server/routes/api/grokdebugger/register_grok_simulate_route.js b/x-pack/legacy/plugins/grokdebugger/server/routes/api/grokdebugger/register_grok_simulate_route.js deleted file mode 100644 index 14ea1e7acb668..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/server/routes/api/grokdebugger/register_grok_simulate_route.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { wrapEsError } from '../../../lib/error_wrappers'; -import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; -import { GrokdebuggerRequest } from '../../../models/grokdebugger_request'; -import { GrokdebuggerResponse } from '../../../models/grokdebugger_response'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; - -function simulateGrok(callWithRequest, ingestJson) { - return callWithRequest('ingest.simulate', { - body: ingestJson, - }); -} - -export function registerGrokSimulateRoute(server) { - const licensePreRouting = licensePreRoutingFactory(server); - - server.route({ - path: '/api/grokdebugger/simulate', - method: 'POST', - handler: request => { - const callWithRequest = callWithRequestFactory(server, request); - const grokdebuggerRequest = GrokdebuggerRequest.fromDownstreamJSON(request.payload); - return simulateGrok(callWithRequest, grokdebuggerRequest.upstreamJSON) - .then(simulateResponseFromES => { - const grokdebuggerResponse = GrokdebuggerResponse.fromUpstreamJSON( - simulateResponseFromES - ); - return { grokdebuggerResponse }; - }) - .catch(e => wrapEsError(e)); - }, - config: { - pre: [licensePreRouting], - }, - }); -} diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index d18174baacdb9..c2ab1c72af545 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -17,7 +17,7 @@ import { import { Subscription } from 'rxjs'; import { ReactExpressionRendererType } from '../../../../../../../src/plugins/expressions/public'; -import { VIS_EVENT_TO_TRIGGER } from '../../../../../../../src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/events'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../../../../src/plugins/visualizations/public'; import { Embeddable as AbstractEmbeddable, diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index 2bde698e23562..1caea1b4b728f 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -22,7 +22,7 @@ import { ErrorEmbeddable, EmbeddableInput, IContainer, -} from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +} from '../../../../../../../src/plugins/embeddable/public'; import { Embeddable } from './embeddable'; import { SavedObjectIndexStore, DOC_TYPE } from '../../persistence'; import { getEditPath } from '../../../../../../plugins/lens/common'; diff --git a/x-pack/legacy/plugins/grokdebugger/server/lib/register_license_checker/index.js b/x-pack/legacy/plugins/lens/public/helpers/index.ts similarity index 77% rename from x-pack/legacy/plugins/grokdebugger/server/lib/register_license_checker/index.js rename to x-pack/legacy/plugins/lens/public/helpers/index.ts index 7b0f97c38d129..f464b5dcc97a3 100644 --- a/x-pack/legacy/plugins/grokdebugger/server/lib/register_license_checker/index.js +++ b/x-pack/legacy/plugins/lens/public/helpers/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { registerLicenseChecker } from './register_license_checker'; +export { addEmbeddableToDashboardUrl, getUrlVars } from './url_helper'; diff --git a/x-pack/legacy/plugins/lens/public/helpers/url_helper.test.ts b/x-pack/legacy/plugins/lens/public/helpers/url_helper.test.ts new file mode 100644 index 0000000000000..9c59c9a96d00f --- /dev/null +++ b/x-pack/legacy/plugins/lens/public/helpers/url_helper.test.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +jest.mock('../legacy_imports', () => ({ + DashboardConstants: { + ADD_EMBEDDABLE_ID: 'addEmbeddableId', + ADD_EMBEDDABLE_TYPE: 'addEmbeddableType', + }, +})); + +import { addEmbeddableToDashboardUrl, getUrlVars } from './url_helper'; + +describe('Dashboard URL Helper', () => { + it('addEmbeddableToDashboardUrl', () => { + const id = '123eb456cd'; + const urlVars = { + x: '1', + y: '2', + z: '3', + }; + const url = + "/pep/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!())"; + expect(addEmbeddableToDashboardUrl(url, id, urlVars)).toEqual( + `/pep/app/kibana#/dashboard?_a=%28description%3A%27%27%2Cfilters%3A%21%28%29%29&_g=%28refreshInterval%3A%28pause%3A%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3Anow-15m%2Cto%3Anow%29%29&addEmbeddableId=${id}&addEmbeddableType=lens&x=1&y=2&z=3` + ); + }); + + it('getUrlVars', () => { + let url = + "http://localhost:5601/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; + expect(getUrlVars(url)).toEqual({ + _g: '(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))', + _a: "(description:'',filters:!()", + }); + url = 'http://mybusiness.mydomain.com/app/kibana#/dashboard?x=y&y=z'; + expect(getUrlVars(url)).toEqual({ + x: 'y', + y: 'z', + }); + url = 'http://localhost:5601/app/kibana#/dashboard/777182'; + expect(getUrlVars(url)).toEqual({}); + url = + 'http://localhost:5601/app/kibana#/dashboard/777182?title=Some%20Dashboard%20With%20Spaces'; + expect(getUrlVars(url)).toEqual({ title: 'Some Dashboard With Spaces' }); + }); +}); diff --git a/x-pack/legacy/plugins/lens/public/helpers/url_helper.ts b/x-pack/legacy/plugins/lens/public/helpers/url_helper.ts new file mode 100644 index 0000000000000..fca44195b98c4 --- /dev/null +++ b/x-pack/legacy/plugins/lens/public/helpers/url_helper.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { parseUrl, stringify } from 'query-string'; +import { DashboardConstants } from '../legacy_imports'; + +type UrlVars = Record; + +/** + * Return query params from URL + * @param url given url + */ +export function getUrlVars(url: string): Record { + const vars: UrlVars = {}; + for (const [, key, value] of url.matchAll(/[?&]+([^=&]+)=([^&]*)/gi)) { + vars[key] = decodeURIComponent(value); + } + return vars; +} + +/** * + * Returns dashboard URL with added embeddableType and embeddableId query params + * eg. + * input: url: /lol/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)), embeddableId: 12345 + * output: /lol/app/kibana#/dashboard?addEmbeddableType=lens&addEmbeddableId=12345&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)) + * @param url dasbhoard absolute url + * @param embeddableId id of the saved visualization + * @param urlVars url query params + */ +export function addEmbeddableToDashboardUrl(url: string, embeddableId: string, urlVars: UrlVars) { + const dashboardParsedUrl = parseUrl(url); + const keys = Object.keys(urlVars).sort(); + + keys.forEach(key => { + dashboardParsedUrl.query[key] = urlVars[key]; + }); + dashboardParsedUrl.query[DashboardConstants.ADD_EMBEDDABLE_TYPE] = 'lens'; + dashboardParsedUrl.query[DashboardConstants.ADD_EMBEDDABLE_ID] = embeddableId; + const query = stringify(dashboardParsedUrl.query); + + return `${dashboardParsedUrl.url}?${query}`; +} diff --git a/x-pack/legacy/plugins/lens/public/legacy_imports.ts b/x-pack/legacy/plugins/lens/public/legacy_imports.ts index d53842d209e2b..857443ae0fa1c 100644 --- a/x-pack/legacy/plugins/lens/public/legacy_imports.ts +++ b/x-pack/legacy/plugins/lens/public/legacy_imports.ts @@ -4,5 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { setup as visualizations } from '../../../../../src/legacy/core_plugins/visualizations/public/np_ready/public/legacy'; -export { VisualizationsSetup } from '../../../../../src/legacy/core_plugins/visualizations/public'; +import { npSetup } from 'ui/new_platform'; +export const { visualizations } = npSetup.plugins; +export { VisualizationsSetup } from '../../../../../src/plugins/visualizations/public'; +export { DashboardConstants } from '../../../../../src/legacy/core_plugins/kibana/public/dashboard'; diff --git a/x-pack/legacy/plugins/lens/public/plugin.tsx b/x-pack/legacy/plugins/lens/public/plugin.tsx index fad1371199e6a..45817fdc3c05f 100644 --- a/x-pack/legacy/plugins/lens/public/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/plugin.tsx @@ -8,10 +8,14 @@ import React from 'react'; import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { HashRouter, Route, RouteComponentProps, Switch } from 'react-router-dom'; import { render, unmountComponentAtNode } from 'react-dom'; -import { AppMountParameters, CoreSetup, CoreStart } from 'src/core/public'; -import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public'; import rison, { RisonObject, RisonValue } from 'rison-node'; import { isObject } from 'lodash'; + +import { AppMountParameters, CoreSetup, CoreStart } from 'src/core/public'; +import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public'; +import { EmbeddableSetup, EmbeddableStart } from 'src/plugins/embeddable/public'; +import { ExpressionsSetup, ExpressionsStart } from 'src/plugins/expressions/public'; +import { KibanaLegacySetup } from 'src/plugins/kibana_legacy/public'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { EditorFrameService } from './editor_frame_service'; import { IndexPatternDatasource } from './indexpattern_datasource'; @@ -19,7 +23,6 @@ import { addHelpMenuToAppChrome } from './help_menu_util'; import { SavedObjectIndexStore } from './persistence'; import { XyVisualization } from './xy_visualization'; import { MetricVisualization } from './metric_visualization'; -import { ExpressionsSetup, ExpressionsStart } from '../../../../../src/plugins/expressions/public'; import { DatatableVisualization } from './datatable_visualization'; import { App } from './app_plugin'; import { @@ -30,17 +33,12 @@ import { } from './lens_ui_telemetry'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; -import { KibanaLegacySetup } from '../../../../../src/plugins/kibana_legacy/public'; import { NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../../../../plugins/lens/common'; -import { - addEmbeddableToDashboardUrl, - getUrlVars, - getLensUrlFromDashboardAbsoluteUrl, -} from '../../../../../src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper'; -import { EmbeddableSetup, EmbeddableStart } from '../../../../../src/plugins/embeddable/public'; +import { addEmbeddableToDashboardUrl, getUrlVars } from './helpers'; import { EditorFrameStart } from './types'; import { getLensAliasConfig } from './vis_type_alias'; -import { VisualizationsSetup } from './legacy_imports'; +import { VisualizationsSetup, DashboardConstants } from './legacy_imports'; + export interface LensPluginSetupDependencies { kibanaLegacy: KibanaLegacySetup; expressions: ExpressionsSetup; @@ -144,40 +142,24 @@ export class LensPlugin { routeProps.history.push(`/lens/edit/${id}`); } else if (addToDashboardMode && id) { routeProps.history.push(`/lens/edit/${id}`); - const url = coreStart.chrome.navLinks.get('kibana:dashboard'); - if (!url) { + const lastDashboardLink = coreStart.chrome.navLinks.get('kibana:dashboard'); + if (!lastDashboardLink || !lastDashboardLink.url) { throw new Error('Cannot get last dashboard url'); } - const lastDashboardAbsoluteUrl = url.url; - const basePath = coreStart.http.basePath.get(); - const lensUrl = getLensUrlFromDashboardAbsoluteUrl( - lastDashboardAbsoluteUrl, - basePath, - id - ); - if (!lastDashboardAbsoluteUrl || !lensUrl) { - throw new Error('Cannot get last dashboard url'); - } - window.history.pushState({}, '', lensUrl); - const urlVars = getUrlVars(lastDashboardAbsoluteUrl); + const urlVars = getUrlVars(lastDashboardLink.url); updateUrlTime(urlVars); // we need to pass in timerange in query params directly - const dashboardParsedUrl = addEmbeddableToDashboardUrl( - lastDashboardAbsoluteUrl, - basePath, - id, - urlVars - ); - if (!dashboardParsedUrl) { - throw new Error('Problem parsing dashboard url'); - } - window.history.pushState({}, '', dashboardParsedUrl); + const dashboardUrl = addEmbeddableToDashboardUrl(lastDashboardLink.url, id, urlVars); + window.history.pushState({}, '', dashboardUrl); } }; const renderEditor = (routeProps: RouteComponentProps<{ id?: string }>) => { trackUiEvent('loaded'); const addToDashboardMode = - !!routeProps.location.search && routeProps.location.search.includes('addToDashboard'); + !!routeProps.location.search && + routeProps.location.search.includes( + DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM + ); return ( ({ aliasUrl: getBasePath(), diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx index d6abee101db31..cdc5fc2ff1c17 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx @@ -39,7 +39,12 @@ function sampleArgs() { formatHint: { id: 'number', params: { pattern: '0,0.000' } }, }, { id: 'b', name: 'b', formatHint: { id: 'number', params: { pattern: '000,0' } } }, - { id: 'c', name: 'c', formatHint: { id: 'string' } }, + { + id: 'c', + name: 'c', + formatHint: { id: 'string' }, + meta: { type: 'date-histogram', aggConfigParams: { interval: '10s' } }, + }, { id: 'd', name: 'ColD', formatHint: { id: 'string' } }, ], rows: [ @@ -179,6 +184,7 @@ describe('xy_expression', () => { Object { "max": 1546491600000, "min": 1546405200000, + "minInterval": 10000, } `); }); diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx index eaf3acf7bb2a7..a7d4b2a217f37 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx @@ -29,12 +29,13 @@ import { EuiIcon, EuiText, IconType, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { EmbeddableVisTriggerContext } from '../../../../../../src/plugins/embeddable/public'; -import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/events'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public'; import { LensMultiTable, FormatFactory } from '../types'; import { XYArgs, SeriesType, visualizationTypes } from './types'; import { VisualizationContainer } from '../visualization_container'; import { isHorizontalChart } from './state_helpers'; import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; +import { parseInterval } from '../../../../../../src/plugins/data/common'; import { getExecuteTriggerActions } from './services'; type InferPropType = T extends React.FunctionComponent ? P : T; @@ -210,11 +211,14 @@ export function XYChart({ const shouldRotate = isHorizontalChart(layers); const xTitle = (xAxisColumn && xAxisColumn.name) || args.xTitle; + const interval = parseInterval(xAxisColumn?.meta?.aggConfigParams?.interval); + const xDomain = data.dateRange && layers.every(l => l.xScaleType === 'time') ? { min: data.dateRange.fromDate.getTime(), max: data.dateRange.toDate.getTime(), + minInterval: interval?.asMilliseconds(), } : undefined; return ( diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts index 48ab957089361..b4a8ff90c3512 100644 --- a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts +++ b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts @@ -5,8 +5,14 @@ */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ +import { Filter, Query, TimeRange } from 'src/plugins/data/public'; +import { AnyAction } from 'redux'; import { LAYER_TYPE } from '../../common/constants'; import { DataMeta, MapFilters } from '../../common/descriptor_types'; +import { + MapCenterAndZoom, + MapRefreshConfig, +} from '../../../../../plugins/maps/common/descriptor_types'; export type SyncContext = { startLoading(dataId: string, requestToken: symbol, meta: DataMeta): void; @@ -24,3 +30,34 @@ export function updateSourceProp( value: unknown, newLayerType?: LAYER_TYPE ): void; + +export function setGotoWithCenter(config: MapCenterAndZoom): AnyAction; + +export function replaceLayerList(layerList: unknown[]): AnyAction; + +export type QueryGroup = { + filters: Filter[]; + query?: Query; + timeFilters?: TimeRange; + refresh?: boolean; +}; + +export function setQuery(query: QueryGroup): AnyAction; + +export function setRefreshConfig(config: MapRefreshConfig): AnyAction; + +export function disableScrollZoom(): AnyAction; + +export function disableInteractive(): AnyAction; + +export function disableTooltipControl(): AnyAction; + +export function hideToolbarOverlay(): AnyAction; + +export function hideLayerControl(): AnyAction; + +export function hideViewControl(): AnyAction; + +export function setHiddenLayers(hiddenLayerIds: string[]): AnyAction; + +export function addLayerWithoutDataSync(layerDescriptor: unknown): AnyAction; diff --git a/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts b/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts new file mode 100644 index 0000000000000..233918847de08 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.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. + */ + +import { AnyAction } from 'redux'; + +export function setOpenTOCDetails(layerIds?: string[]): AnyAction; + +export function setIsLayerTOCOpen(open: boolean): AnyAction; + +export function setReadOnly(readOnly: boolean): AnyAction; diff --git a/x-pack/legacy/plugins/grokdebugger/server/lib/call_with_request_factory/index.js b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts similarity index 71% rename from x-pack/legacy/plugins/grokdebugger/server/lib/call_with_request_factory/index.js rename to x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts index 787814d87dff9..920888404b97d 100644 --- a/x-pack/legacy/plugins/grokdebugger/server/lib/call_with_request_factory/index.js +++ b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { callWithRequestFactory } from './call_with_request_factory'; +export function getInitialLayers(layerListJSON?: string, initialLayers?: unknown[]): unknown[]; diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 519ba0b1e3d96..bc97643689e12 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -310,9 +310,15 @@ app.controller( const layerListConfigOnly = copyPersistentState(layerList); const savedLayerList = savedMap.getLayerList(); - const oldConfig = savedLayerList ? savedLayerList : initialLayerListConfig; - return !_.isEqual(layerListConfigOnly, oldConfig); + return !savedLayerList + ? !_.isEqual(layerListConfigOnly, initialLayerListConfig) + : // savedMap stores layerList as a JSON string using JSON.stringify. + // JSON.stringify removes undefined properties from objects. + // savedMap.getLayerList converts the JSON string back into Javascript array of objects. + // Need to perform the same process for layerListConfigOnly to compare apples to apples + // and avoid undefined properties in layerListConfigOnly triggering unsaved changes. + !_.isEqual(JSON.parse(JSON.stringify(layerListConfigOnly)), savedLayerList); } function isOnMapNow() { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts new file mode 100644 index 0000000000000..00a9400109dc1 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { Filter } from 'src/plugins/data/public'; +import { RenderToolTipContent } from '../../layers/tooltips/tooltip_property'; + +export const GisMap: React.ComponentType<{ + addFilters: ((filters: Filter[]) => void) | null; + renderTooltipContent?: RenderToolTipContent; +}>; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap index a52c118bca8cd..3b3d82c92fbb7 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap @@ -2,7 +2,7 @@ exports[`FeatureProperties should not show filter button 1`] = ` (this._node = node)}> +
(this._node = node)} + > {rows}
); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/index.js b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/index.js index e51e59ec41e18..04de5f71f5bfc 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/index.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/index.js @@ -7,7 +7,7 @@ import { connect } from 'react-redux'; import { LayerControl } from './view'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FLYOUT_STATE } from '../../../../../../../plugins/maps/public/reducers/ui.js'; +import { FLYOUT_STATE } from '../../../../../../../plugins/maps/public/reducers/ui'; import { updateFlyout, setIsLayerTOCOpen } from '../../../actions/ui_actions'; import { setSelectedLayer } from '../../../actions/map_actions'; import { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.js b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.js index ececc5a90ab89..588445d0b4992 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.js @@ -8,7 +8,7 @@ import _ from 'lodash'; import { connect } from 'react-redux'; import { TOCEntry } from './view'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FLYOUT_STATE } from '../../../../../../../../../plugins/maps/public/reducers/ui.js'; +import { FLYOUT_STATE } from '../../../../../../../../../plugins/maps/public/reducers/ui'; import { updateFlyout, hideTOCDetails, showTOCDetails } from '../../../../../actions/ui_actions'; import { getIsReadOnly, getOpenTOCDetails } from '../../../../../selectors/ui_selectors'; import { diff --git a/x-pack/legacy/plugins/grokdebugger/server/lib/license_pre_routing_factory/index.js b/x-pack/legacy/plugins/maps/public/embeddable/index.ts similarity index 76% rename from x-pack/legacy/plugins/grokdebugger/server/lib/license_pre_routing_factory/index.js rename to x-pack/legacy/plugins/maps/public/embeddable/index.ts index 0743e443955f4..a410a6699a01f 100644 --- a/x-pack/legacy/plugins/grokdebugger/server/lib/license_pre_routing_factory/index.js +++ b/x-pack/legacy/plugins/maps/public/embeddable/index.ts @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { licensePreRoutingFactory } from './license_pre_routing_factory'; +export * from './map_embeddable'; +export * from './map_embeddable_factory'; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx similarity index 69% rename from x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js rename to x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx index 9af1a135794c0..3c9069c7a836f 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx @@ -10,16 +10,29 @@ import { Provider } from 'react-redux'; import { render, unmountComponentAtNode } from 'react-dom'; import 'mapbox-gl/dist/mapbox-gl.css'; -import { Embeddable } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { APPLY_FILTER_TRIGGER } from '../../../../../../src/plugins/ui_actions/public'; -import { esFilters } from '../../../../../../src/plugins/data/public'; - import { I18nContext } from 'ui/i18n'; +import { npStart } from 'ui/new_platform'; +import { Subscription } from 'rxjs'; +import { Unsubscribe } from 'redux'; +import { + Embeddable, + IContainer, + EmbeddableInput, + EmbeddableOutput, +} from '../../../../../../src/plugins/embeddable/public'; +import { APPLY_FILTER_TRIGGER } from '../../../../../../src/plugins/ui_actions/public'; +import { + esFilters, + IIndexPattern, + TimeRange, + Filter, + Query, + RefreshInterval, +} from '../../../../../../src/plugins/data/public'; import { GisMap } from '../connected_components/gis_map'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { createMapStore } from '../../../../../plugins/maps/public/reducers/store'; -import { npStart } from 'ui/new_platform'; +import { createMapStore, MapStore } from '../../../../../plugins/maps/public/reducers/store'; import { setGotoWithCenter, replaceLayerList, @@ -33,20 +46,70 @@ import { hideViewControl, setHiddenLayers, } from '../actions/map_actions'; +import { MapCenterAndZoom } from '../../../../../plugins/maps/common/descriptor_types'; import { setReadOnly, setIsLayerTOCOpen, setOpenTOCDetails } from '../actions/ui_actions'; import { getIsLayerTOCOpen, getOpenTOCDetails } from '../selectors/ui_selectors'; import { getInspectorAdapters, setEventHandlers, + EventHandlers, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; import { getMapCenter, getMapZoom, getHiddenLayerIds } from '../selectors/map_selectors'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; +import { RenderToolTipContent } from '../layers/tooltips/tooltip_property'; + +interface MapEmbeddableConfig { + editUrl?: string; + indexPatterns: IIndexPattern[]; + editable: boolean; + title?: string; + layerList: unknown[]; +} + +export interface MapEmbeddableInput extends EmbeddableInput { + timeRange?: TimeRange; + filters: Filter[]; + query?: Query; + refreshConfig: RefreshInterval; + isLayerTOCOpen: boolean; + openTOCDetails?: string[]; + disableTooltipControl?: boolean; + disableInteractive?: boolean; + hideToolbarOverlay?: boolean; + hideLayerControl?: boolean; + hideViewControl?: boolean; + mapCenter?: MapCenterAndZoom; + hiddenLayers?: string[]; + hideFilterActions?: boolean; +} -export class MapEmbeddable extends Embeddable { +export interface MapEmbeddableOutput extends EmbeddableOutput { + indexPatterns: IIndexPattern[]; +} + +export class MapEmbeddable extends Embeddable { type = MAP_SAVED_OBJECT_TYPE; - constructor(config, initialInput, parent, renderTooltipContent, eventHandlers) { + private _renderTooltipContent?: RenderToolTipContent; + private _eventHandlers?: EventHandlers; + private _layerList: unknown[]; + private _store: MapStore; + private _subscription: Subscription; + private _prevTimeRange?: TimeRange; + private _prevQuery?: Query; + private _prevRefreshConfig?: RefreshInterval; + private _prevFilters?: Filter[]; + private _domNode?: HTMLElement; + private _unsubscribeFromStore?: Unsubscribe; + + constructor( + config: MapEmbeddableConfig, + initialInput: MapEmbeddableInput, + parent?: IContainer, + renderTooltipContent?: RenderToolTipContent, + eventHandlers?: EventHandlers + ) { super( initialInput, { @@ -70,7 +133,7 @@ export class MapEmbeddable extends Embeddable { return getInspectorAdapters(this._store.getState()); } - onContainerStateChanged(containerState) { + onContainerStateChanged(containerState: MapEmbeddableInput) { if ( !_.isEqual(containerState.timeRange, this._prevTimeRange) || !_.isEqual(containerState.query, this._prevQuery) || @@ -84,7 +147,17 @@ export class MapEmbeddable extends Embeddable { } } - _dispatchSetQuery({ query, timeRange, filters, refresh }) { + _dispatchSetQuery({ + query, + timeRange, + filters, + refresh, + }: { + query?: Query; + timeRange?: TimeRange; + filters: Filter[]; + refresh?: boolean; + }) { this._prevTimeRange = timeRange; this._prevQuery = query; this._prevFilters = filters; @@ -98,7 +171,7 @@ export class MapEmbeddable extends Embeddable { ); } - _dispatchSetRefreshConfig({ refreshConfig }) { + _dispatchSetRefreshConfig({ refreshConfig }: Pick) { this._prevRefreshConfig = refreshConfig; this._store.dispatch( setRefreshConfig({ @@ -113,7 +186,7 @@ export class MapEmbeddable extends Embeddable { * @param {HTMLElement} domNode * @param {ContainerState} containerState */ - render(domNode) { + render(domNode: HTMLElement) { this._store.dispatch(setEventHandlers(this._eventHandlers)); this._store.dispatch(setReadOnly(true)); this._store.dispatch(disableScrollZoom()); @@ -127,23 +200,22 @@ export class MapEmbeddable extends Embeddable { } if (_.has(this.input, 'disableInteractive') && this.input.disableInteractive) { - this._store.dispatch(disableInteractive(this.input.disableInteractive)); + this._store.dispatch(disableInteractive()); } if (_.has(this.input, 'disableTooltipControl') && this.input.disableTooltipControl) { - this._store.dispatch(disableTooltipControl(this.input.disableTooltipControl)); + this._store.dispatch(disableTooltipControl()); } - if (_.has(this.input, 'hideToolbarOverlay') && this.input.hideToolbarOverlay) { - this._store.dispatch(hideToolbarOverlay(this.input.hideToolbarOverlay)); + this._store.dispatch(hideToolbarOverlay()); } if (_.has(this.input, 'hideLayerControl') && this.input.hideLayerControl) { - this._store.dispatch(hideLayerControl(this.input.hideLayerControl)); + this._store.dispatch(hideLayerControl()); } if (_.has(this.input, 'hideViewControl') && this.input.hideViewControl) { - this._store.dispatch(hideViewControl(this.input.hideViewControl)); + this._store.dispatch(hideViewControl()); } if (this.input.mapCenter) { @@ -182,12 +254,12 @@ export class MapEmbeddable extends Embeddable { }); } - async setLayerList(layerList) { + async setLayerList(layerList: unknown[]) { this._layerList = layerList; return await this._store.dispatch(replaceLayerList(this._layerList)); } - addFilters = filters => { + addFilters = (filters: Filter[]) => { npStart.plugins.uiActions.executeTriggerActions(APPLY_FILTER_TRIGGER, { embeddable: this, filters, @@ -213,7 +285,7 @@ export class MapEmbeddable extends Embeddable { this._dispatchSetQuery({ query: this._prevQuery, timeRange: this._prevTimeRange, - filters: this._prevFilters, + filters: this._prevFilters ?? [], refresh: true, }); } @@ -222,7 +294,7 @@ export class MapEmbeddable extends Embeddable { const center = getMapCenter(this._store.getState()); const zoom = getMapZoom(this._store.getState()); - const mapCenter = this.input.mapCenter || {}; + const mapCenter = this.input.mapCenter || undefined; if ( !mapCenter || mapCenter.lat !== center.lat || @@ -233,7 +305,7 @@ export class MapEmbeddable extends Embeddable { mapCenter: { lat: center.lat, lon: center.lon, - zoom: zoom, + zoom, }, }); } diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts similarity index 75% rename from x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js rename to x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts index fcbae894dffa4..b9cb66f831281 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -8,13 +8,16 @@ import _ from 'lodash'; import chrome from 'ui/chrome'; import { capabilities } from 'ui/capabilities'; import { i18n } from '@kbn/i18n'; +import { npSetup, npStart } from 'ui/new_platform'; +import { SavedObjectLoader } from 'src/plugins/saved_objects/public'; +import { IIndexPattern } from 'src/plugins/data/public'; +import { MapEmbeddable, MapEmbeddableInput } from './map_embeddable'; +import { getIndexPatternService } from '../kibana_services'; import { EmbeddableFactory, ErrorEmbeddable, -} from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { setup } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; -import { MapEmbeddable } from './map_embeddable'; -import { getIndexPatternService } from '../kibana_services'; + IContainer, +} from '../../../../../../src/plugins/embeddable/public'; import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths @@ -25,7 +28,11 @@ import { getInitialLayers } from '../angular/get_initial_layers'; import { mergeInputWithSavedMap } from './merge_input_with_saved_map'; import '../angular/services/gis_map_saved_object_loader'; import { bindSetupCoreAndPlugins, bindStartCoreAndPlugins } from '../plugin'; -import { npSetup, npStart } from 'ui/new_platform'; +import { RenderToolTipContent } from '../layers/tooltips/tooltip_property'; +import { + EventHandlers, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; export class MapEmbeddableFactory extends EmbeddableFactory { type = MAP_SAVED_OBJECT_TYPE; @@ -44,8 +51,9 @@ export class MapEmbeddableFactory extends EmbeddableFactory { bindSetupCoreAndPlugins(npSetup.core, npSetup.plugins); bindStartCoreAndPlugins(npStart.core, npStart.plugins); } - isEditable() { - return capabilities.get().maps.save; + + async isEditable() { + return capabilities.get().maps.save as boolean; } // Not supported yet for maps types. @@ -59,12 +67,12 @@ export class MapEmbeddableFactory extends EmbeddableFactory { }); } - async _getIndexPatterns(layerList) { + async _getIndexPatterns(layerList: unknown[]): Promise { // Need to extract layerList from store to get queryable index pattern ids const store = createMapStore(); let queryableIndexPatternIds; try { - layerList.forEach(layerDescriptor => { + layerList.forEach((layerDescriptor: unknown) => { store.dispatch(addLayerWithoutDataSync(layerDescriptor)); }); queryableIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState()); @@ -86,16 +94,20 @@ export class MapEmbeddableFactory extends EmbeddableFactory { } }); const indexPatterns = await Promise.all(promises); - return _.compact(indexPatterns); + return _.compact(indexPatterns) as IIndexPattern[]; } - async _fetchSavedMap(savedObjectId) { + async _fetchSavedMap(savedObjectId: string) { const $injector = await chrome.dangerouslyGetActiveInjector(); - const savedObjectLoader = $injector.get('gisMapSavedObjectLoader'); + const savedObjectLoader = $injector.get('gisMapSavedObjectLoader'); return await savedObjectLoader.get(savedObjectId); } - async createFromSavedObject(savedObjectId, input, parent) { + async createFromSavedObject( + savedObjectId: string, + input: MapEmbeddableInput, + parent?: IContainer + ) { const savedMap = await this._fetchSavedMap(savedObjectId); const layerList = getInitialLayers(savedMap.layerListJSON); const indexPatterns = await this._getIndexPatterns(layerList); @@ -106,7 +118,7 @@ export class MapEmbeddableFactory extends EmbeddableFactory { title: savedMap.title, editUrl: chrome.addBasePath(createMapPath(savedObjectId)), indexPatterns, - editable: this.isEditable(), + editable: await this.isEditable(), }, input, parent @@ -125,7 +137,13 @@ export class MapEmbeddableFactory extends EmbeddableFactory { return embeddable; } - async createFromState(state, input, parent, renderTooltipContent, eventHandlers) { + async createFromState( + state: { title?: string; layerList?: unknown[] }, + input: MapEmbeddableInput, + parent: IContainer, + renderTooltipContent: RenderToolTipContent, + eventHandlers: EventHandlers + ) { const layerList = state && state.layerList ? state.layerList : getInitialLayers(); const indexPatterns = await this._getIndexPatterns(layerList); @@ -133,7 +151,6 @@ export class MapEmbeddableFactory extends EmbeddableFactory { { layerList, title: state && state.title ? state.title : '', - editUrl: null, indexPatterns, editable: false, }, @@ -144,7 +161,7 @@ export class MapEmbeddableFactory extends EmbeddableFactory { ); } - async create(input) { + async create(input: MapEmbeddableInput) { window.location.href = chrome.addBasePath(createMapPath('')); return new ErrorEmbeddable( 'Maps can only be created with createFromSavedObject or createFromState', @@ -153,4 +170,7 @@ export class MapEmbeddableFactory extends EmbeddableFactory { } } -setup.registerEmbeddableFactory(MAP_SAVED_OBJECT_TYPE, new MapEmbeddableFactory()); +npSetup.plugins.embeddable.registerEmbeddableFactory( + MAP_SAVED_OBJECT_TYPE, + new MapEmbeddableFactory() +); diff --git a/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts b/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts new file mode 100644 index 0000000000000..4ce4df02f6a39 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MapEmbeddableInput } from './map_embeddable'; + +export function mergeInputWithSavedMap( + input: MapEmbeddableInput, + savedmap: unknown +): Partial; diff --git a/x-pack/legacy/plugins/maps/public/index.ts b/x-pack/legacy/plugins/maps/public/index.ts index 27cd64103eec9..2d13f005f1a70 100644 --- a/x-pack/legacy/plugins/maps/public/index.ts +++ b/x-pack/legacy/plugins/maps/public/index.ts @@ -25,3 +25,6 @@ import { MapsPlugin } from './plugin'; export const plugin = (initializerContext: PluginInitializerContext) => { return new MapsPlugin(); }; + +export { RenderTooltipContentParams, ITooltipProperty } from './layers/tooltips/tooltip_property'; +export { MapEmbeddable, MapEmbeddableInput } from './embeddable'; diff --git a/x-pack/legacy/plugins/maps/public/kibana_services.d.ts b/x-pack/legacy/plugins/maps/public/kibana_services.d.ts new file mode 100644 index 0000000000000..89b1fee1aa842 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/kibana_services.d.ts @@ -0,0 +1,23 @@ +/* + * 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 { IIndexPattern } from 'src/plugins/data/public'; + +export function getIndexPatternService(): { + get: (id: string) => IIndexPattern | undefined; +}; + +export function setLicenseId(args: unknown): void; +export function setInspector(args: unknown): void; +export function setFileUpload(args: unknown): void; +export function setIndexPatternSelect(args: unknown): void; +export function setHttp(args: unknown): void; +export function setTimeFilter(args: unknown): void; +export function setUiSettings(args: unknown): void; +export function setInjectedVarFunc(args: unknown): void; +export function setToasts(args: unknown): void; +export function setIndexPatternService(args: unknown): void; +export function setAutocompleteService(args: unknown): void; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js index 0d4cf322d2a40..bf57306df5697 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js @@ -123,6 +123,7 @@ export class ColorMapSelect extends Component { { value: CUSTOM_COLOR_MAP, inputDisplay: this.props.customOptionLabel, + 'data-test-subj': `colorMapSelectOption_${CUSTOM_COLOR_MAP}`, }, ...this.props.colorMapOptions, ]; @@ -150,6 +151,7 @@ export class ColorMapSelect extends Component { onChange={this._onColorMapSelect} valueOfSelected={valueOfSelected} hasDividers={true} + data-test-subj={`colorMapSelect_${this.props.styleProperty.getStyleName()}`} /> diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js index 124c2bf0cff55..edf230b0a945c 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js @@ -63,6 +63,7 @@ export const ColorStopsCategorical = ({ getValueSuggestions={getValueSuggestions} value={stopValue} onChange={onStopChange} + dataTestSubj={`colorStopInput${index}`} /> ); }; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js index c6b68b7e94409..5e8f720fcc5e3 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js @@ -28,12 +28,20 @@ export function DynamicColorForm({ }; if (type === COLOR_MAP_TYPE.ORDINAL) { newColorOptions.useCustomColorRamp = useCustomColorMap; - newColorOptions.customColorRamp = customColorMap; - newColorOptions.color = color; + if (customColorMap) { + newColorOptions.customColorRamp = customColorMap; + } + if (color) { + newColorOptions.color = color; + } } else { newColorOptions.useCustomColorPalette = useCustomColorMap; - newColorOptions.customColorPalette = customColorMap; - newColorOptions.colorCategory = color; + if (customColorMap) { + newColorOptions.customColorPalette = customColorMap; + } + if (color) { + newColorOptions.colorCategory = color; + } } onDynamicStyleChange(styleProperty.getStyleName(), newColorOptions); @@ -123,6 +131,7 @@ export function DynamicColorForm({ { onChange({ field: selectedFields.length > 0 ? selectedFields[0].value : null, @@ -98,6 +98,7 @@ export function FieldSelect({ fields, selectedFieldName, onChange, ...rest }) { defaultMessage: 'Select a field', })} renderOption={renderOption} + data-test-subj={`styleFieldSelect_${styleName}`} {...rest} /> ); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js index 0c9011b811f38..bbba6705f7de7 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js @@ -27,6 +27,7 @@ export function DynamicLabelForm({ ); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js index 752e0e4213217..005bc11aa1bd8 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js @@ -77,6 +77,7 @@ export class StylePropEditor extends Component { defaultMessage: 'Select to style by fixed value or by data value', })} compressed + data-test-subj={`staticDynamicSelect_${this.props.styleProperty.getStyleName()}`} /> ); } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js index 9065102dc8bd7..6b79ac17f2e22 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js @@ -59,6 +59,7 @@ export function DynamicIconForm({ ; } +export interface LoadFeatureProps { + layerId: string; + featureId: number; +} + +export interface FeatureGeometry { + coordinates: [number]; + type: string; +} + +export interface RenderTooltipContentParams { + addFilters(filter: object): void; + closeTooltip(): void; + features: TooltipFeature[]; + isLocked: boolean; + getLayerName(layerId: string): Promise; + loadFeatureProperties({ layerId, featureId }: LoadFeatureProps): Promise; + loadFeatureGeometry({ layerId, featureId }: LoadFeatureProps): FeatureGeometry; +} + +export type RenderToolTipContent = (params: RenderTooltipContentParams) => JSX.Element; + export class TooltipProperty implements ITooltipProperty { private readonly _propertyKey: string; - private readonly _propertyName: string; private readonly _rawValue: string | undefined; + private readonly _propertyName: string; constructor(propertyKey: string, propertyName: string, rawValue: string | undefined) { this._propertyKey = propertyKey; diff --git a/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.ts b/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.ts index a56da4b23aa1e..3599f18671ced 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.ts +++ b/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Query } from '../../../common/descriptor_types'; +import { MapQuery } from '../../../common/descriptor_types'; // Refresh only query is query where timestamps are different but query is the same. // Triggered by clicking "Refresh" button in QueryBar export function isRefreshOnlyQuery( - prevQuery: Query | undefined, - newQuery: Query | undefined + prevQuery: MapQuery | undefined, + newQuery: MapQuery | undefined ): boolean { if (!prevQuery || !newQuery) { return false; diff --git a/x-pack/legacy/plugins/maps/public/plugin.ts b/x-pack/legacy/plugins/maps/public/plugin.ts index 1f5817aa33dcc..53c951ac787e1 100644 --- a/x-pack/legacy/plugins/maps/public/plugin.ts +++ b/x-pack/legacy/plugins/maps/public/plugin.ts @@ -24,7 +24,6 @@ import { setToasts, setIndexPatternService, setAutocompleteService, - // @ts-ignore } from './kibana_services'; // @ts-ignore import { setInjectedVarFunc as npSetInjectedVarFunc } from '../../../../plugins/maps/public/kibana_services'; // eslint-disable-line @kbn/eslint/no-restricted-paths diff --git a/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js b/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js index 4d87b6a055802..64a42173098ee 100644 --- a/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js +++ b/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js @@ -5,7 +5,7 @@ */ import chrome from 'ui/chrome'; -import { setup as visualizationsSetup } from '../../../../../src/legacy/core_plugins/visualizations/public/np_ready/public/legacy'; +import { npSetup } from '../../../../../src/legacy/ui/public/new_platform'; import { i18n } from '@kbn/i18n'; import { APP_ID, APP_ICON, MAP_BASE_URL } from '../common/constants'; @@ -23,7 +23,7 @@ The Maps app offers more functionality and is easier to use.`, } ); -visualizationsSetup.registerAlias({ +npSetup.plugins.visualizations.registerAlias({ aliasUrl: MAP_BASE_URL, name: APP_ID, title: i18n.translate('xpack.maps.visTypeAlias.title', { @@ -37,5 +37,5 @@ visualizationsSetup.registerAlias({ }); if (!showMapVisualizationTypes) { - visualizationsSetup.hideTypes(['region_map', 'tile_map']); + npSetup.plugins.visualizations.hideTypes(['region_map', 'tile_map']); } diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts new file mode 100644 index 0000000000000..8c99e0adcc14f --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AnyAction } from 'redux'; +import { MapCenter } from '../../common/descriptor_types'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { MapStoreState } from '../../../../../plugins/maps/public/reducers/store'; + +export function getHiddenLayerIds(state: MapStoreState): string[]; + +export function getMapZoom(state: MapStoreState): number; + +export function getMapCenter(state: MapStoreState): MapCenter; + +export function getQueryableUniqueIndexPatternIds(state: MapStoreState): string[]; diff --git a/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.js b/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.js deleted file mode 100644 index 912ee08396212..0000000000000 --- a/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export const getFlyoutDisplay = ({ ui }) => ui.flyoutDisplay; -export const getIsSetViewOpen = ({ ui }) => ui.isSetViewOpen; -export const getIsLayerTOCOpen = ({ ui }) => ui.isLayerTOCOpen; -export const getOpenTOCDetails = ({ ui }) => ui.openTOCDetails; -export const getIsFullScreen = ({ ui }) => ui.isFullScreen; -export const getIsReadOnly = ({ ui }) => ui.isReadOnly; -export const getIndexingStage = ({ ui }) => ui.importIndexingStage; diff --git a/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.ts b/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.ts new file mode 100644 index 0000000000000..fdf2a8ea0e4f3 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { MapStoreState } from '../../../../../plugins/maps/public/reducers/store'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { FLYOUT_STATE, INDEXING_STAGE } from '../../../../../plugins/maps/public/reducers/ui'; + +export const getFlyoutDisplay = ({ ui }: MapStoreState): FLYOUT_STATE => ui.flyoutDisplay; +export const getIsSetViewOpen = ({ ui }: MapStoreState): boolean => ui.isSetViewOpen; +export const getIsLayerTOCOpen = ({ ui }: MapStoreState): boolean => ui.isLayerTOCOpen; +export const getOpenTOCDetails = ({ ui }: MapStoreState): string[] => ui.openTOCDetails; +export const getIsFullScreen = ({ ui }: MapStoreState): boolean => ui.isFullScreen; +export const getIsReadOnly = ({ ui }: MapStoreState): boolean => ui.isReadOnly; +export const getIndexingStage = ({ ui }: MapStoreState): INDEXING_STAGE | null => + ui.importIndexingStage; diff --git a/x-pack/legacy/plugins/maps/server/routes.js b/x-pack/legacy/plugins/maps/server/routes.js index 6aacfdc41aeea..20e022001577a 100644 --- a/x-pack/legacy/plugins/maps/server/routes.js +++ b/x-pack/legacy/plugins/maps/server/routes.js @@ -44,6 +44,7 @@ export function initRoutes(server, licenseUid) { fileApiUrl: mapConfig.emsFileApiUrl, tileApiUrl: mapConfig.emsTileApiUrl, landingPageUrl: mapConfig.emsLandingPageUrl, + fetchFunction: fetch, }); emsClient.addQueryParams({ license: licenseUid }); } else { diff --git a/x-pack/legacy/plugins/reporting/export_types/common/layouts/preserve_layout.css b/x-pack/legacy/plugins/reporting/export_types/common/layouts/preserve_layout.css index 2c203e507260f..88e38d55e5784 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/layouts/preserve_layout.css +++ b/x-pack/legacy/plugins/reporting/export_types/common/layouts/preserve_layout.css @@ -34,7 +34,7 @@ filter-bar, /* hide unusable controls */ discover-app .dscTimechart, -discover-app .sidebar-container, +discover-app .dscSidebar__container, discover-app .kbnCollapsibleSidebar__collapseButton, discover-app navbar[name=discover-search], discover-app .discover-table-footer { diff --git a/x-pack/legacy/plugins/reporting/export_types/common/layouts/print.css b/x-pack/legacy/plugins/reporting/export_types/common/layouts/print.css index b5c9861208b7b..ddd280c7b3f23 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/layouts/print.css +++ b/x-pack/legacy/plugins/reporting/export_types/common/layouts/print.css @@ -33,7 +33,7 @@ filter-bar, /* hide unusable controls */ discover-app .dscTimechart, -discover-app .sidebar-container, +discover-app .dscSidebar__container, discover-app .kbnCollapsibleSidebar__collapseButton, discover-app navbar[name="discover-search"], discover-app .discover-table-footer { diff --git a/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts b/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts index 60799e3e918b8..4b80e129c04da 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts +++ b/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts @@ -161,6 +161,12 @@ export class HeadlessChromiumDriver { const interceptedUrl = interceptedResponse.url(); const allowed = !interceptedUrl.startsWith('file://'); + if (!interceptedResponse.ok()) { + logger.warn( + `Chromium received a non-OK response (${interceptedResponse.status()}) for request ${interceptedUrl}` + ); + } + if (!allowed || !this.allowRequest(interceptedUrl)) { logger.error(`Got disallowed URL "${interceptedUrl}", closing browser.`); this.page.browser().close(); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/helpers/index.test.ts b/x-pack/legacy/plugins/siem/common/detection_engine/ml_helpers.test.ts similarity index 96% rename from x-pack/legacy/plugins/siem/public/components/ml/helpers/index.test.ts rename to x-pack/legacy/plugins/siem/common/detection_engine/ml_helpers.test.ts index 693f0bd0dd0fd..ba93b2e4b8a0d 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/helpers/index.test.ts +++ b/x-pack/legacy/plugins/siem/common/detection_engine/ml_helpers.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isJobStarted, isJobLoading, isJobFailed } from './'; +import { isJobStarted, isJobLoading, isJobFailed } from './ml_helpers'; describe('isJobStarted', () => { test('returns false if only jobState is enabled', () => { diff --git a/x-pack/legacy/plugins/siem/public/components/ml/helpers/index.ts b/x-pack/legacy/plugins/siem/common/detection_engine/ml_helpers.ts similarity index 89% rename from x-pack/legacy/plugins/siem/public/components/ml/helpers/index.ts rename to x-pack/legacy/plugins/siem/common/detection_engine/ml_helpers.ts index c06596b49317d..e4158d08d448d 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/helpers/index.ts +++ b/x-pack/legacy/plugins/siem/common/detection_engine/ml_helpers.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { RuleType } from './types'; + // Based on ML Job/Datafeed States from x-pack/legacy/plugins/ml/common/constants/states.js const enabledStates = ['started', 'opened']; const loadingStates = ['starting', 'stopping', 'opening', 'closing']; @@ -20,3 +22,5 @@ export const isJobLoading = (jobState: string, datafeedState: string): boolean = export const isJobFailed = (jobState: string, datafeedState: string): boolean => { return failureStates.includes(jobState) || failureStates.includes(datafeedState); }; + +export const isMlRule = (ruleType: RuleType) => ruleType === 'machine_learning'; diff --git a/x-pack/legacy/plugins/siem/common/detection_engine/types.ts b/x-pack/legacy/plugins/siem/common/detection_engine/types.ts index 0de370b11cdaf..39012d0b4b683 100644 --- a/x-pack/legacy/plugins/siem/common/detection_engine/types.ts +++ b/x-pack/legacy/plugins/siem/common/detection_engine/types.ts @@ -3,9 +3,17 @@ * 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 t from 'io-ts'; import { AlertAction } from '../../../../../plugins/alerting/common'; export type RuleAlertAction = Omit & { action_type_id: string; }; + +export const RuleTypeSchema = t.keyof({ + query: null, + saved_query: null, + machine_learning: null, +}); +export type RuleType = t.TypeOf; diff --git a/x-pack/legacy/plugins/siem/cypress.json b/x-pack/legacy/plugins/siem/cypress.json index cc41b72714ed9..a0333a1068146 100644 --- a/x-pack/legacy/plugins/siem/cypress.json +++ b/x-pack/legacy/plugins/siem/cypress.json @@ -1,6 +1,6 @@ { "baseUrl": "http://localhost:5601", - "defaultCommandTimeout": 60000, + "defaultCommandTimeout": 120000, "screenshotsFolder": "../../../../target/kibana-siem/cypress/screenshots", "trashAssetsBeforeRuns": false, "video": false, diff --git a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules_custom.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules_custom.spec.ts index 3f509c35b64ea..b7e42f7e46a70 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules_custom.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules_custom.spec.ts @@ -8,8 +8,10 @@ import { newRule, totalNumberOfPrebuiltRules } from '../objects/rule'; import { ABOUT_FALSE_POSITIVES, + ABOUT_INVESTIGATION_NOTES, ABOUT_MITRE, ABOUT_RISK, + ABOUT_RULE_DESCRIPTION, ABOUT_SEVERITY, ABOUT_STEP, ABOUT_TAGS, @@ -18,11 +20,13 @@ import { DEFINITION_INDEX_PATTERNS, DEFINITION_TIMELINE, DEFINITION_STEP, + INVESTIGATION_NOTES_MARKDOWN, + INVESTIGATION_NOTES_TOGGLE, + RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, SCHEDULE_LOOPBACK, SCHEDULE_RUNS, SCHEDULE_STEP, - ABOUT_RULE_DESCRIPTION, } from '../screens/rule_details'; import { CUSTOM_RULES_BTN, @@ -31,6 +35,7 @@ import { RULES_ROW, RULES_TABLE, SEVERITY, + SHOWING_RULES_TEXT, } from '../screens/signal_detection_rules'; import { @@ -45,9 +50,12 @@ import { } from '../tasks/detections'; import { changeToThreeHundredRowsPerPage, + deleteFirstRule, + deleteSelectedRules, filterByCustomRules, goToCreateNewRule, goToRuleDetails, + selectNumberOfRules, waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/signal_detection_rules'; @@ -166,6 +174,13 @@ describe('Signal detection rules, custom', () => { .invoke('text') .should('eql', expectedTags); + cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE) + .eq(INVESTIGATION_NOTES_TOGGLE) + .click({ force: true }); + cy.get(ABOUT_INVESTIGATION_NOTES) + .invoke('text') + .should('eql', INVESTIGATION_NOTES_MARKDOWN); + cy.get(DEFINITION_INDEX_PATTERNS).then(patterns => { cy.wrap(patterns).each((pattern, index) => { cy.wrap(pattern) @@ -192,3 +207,67 @@ describe('Signal detection rules, custom', () => { .should('eql', '1m'); }); }); + +describe('Deletes custom rules', () => { + beforeEach(() => { + esArchiverLoad('custom_rules'); + loginAndWaitForPageWithoutDateRange(DETECTIONS); + waitForSignalsPanelToBeLoaded(); + waitForSignalsIndexToBeCreated(); + goToManageSignalDetectionRules(); + }); + + after(() => { + esArchiverUnload('custom_rules'); + }); + + it('Deletes one rule', () => { + cy.get(RULES_TABLE) + .find(RULES_ROW) + .then(rules => { + const initialNumberOfRules = rules.length; + const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1; + + cy.get(SHOWING_RULES_TEXT) + .invoke('text') + .should('eql', `Showing ${initialNumberOfRules} rules`); + + deleteFirstRule(); + waitForRulesToBeLoaded(); + + cy.get(RULES_TABLE).then($table => { + cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterDeletion); + }); + cy.get(SHOWING_RULES_TEXT) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfRulesAfterDeletion} rules`); + cy.get(CUSTOM_RULES_BTN) + .invoke('text') + .should('eql', `Custom rules (${expectedNumberOfRulesAfterDeletion})`); + }); + }); + + it('Deletes more than one rule', () => { + cy.get(RULES_TABLE) + .find(RULES_ROW) + .then(rules => { + const initialNumberOfRules = rules.length; + const numberOfRulesToBeDeleted = 3; + const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - numberOfRulesToBeDeleted; + + selectNumberOfRules(numberOfRulesToBeDeleted); + deleteSelectedRules(); + waitForRulesToBeLoaded(); + + cy.get(RULES_TABLE).then($table => { + cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterDeletion); + }); + cy.get(SHOWING_RULES_TEXT) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfRulesAfterDeletion} rule`); + cy.get(CUSTOM_RULES_BTN) + .invoke('text') + .should('eql', `Custom rules (${expectedNumberOfRulesAfterDeletion})`); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/cypress/integration/timeline_data_providers.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/timeline_data_providers.spec.ts index aca988e195161..4889d40ae7d39 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/timeline_data_providers.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/timeline_data_providers.spec.ts @@ -49,7 +49,7 @@ describe('timeline data providers', () => { .first() .invoke('text') .should(hostname => { - expect(dataProviderText).to.eq(hostname); + expect(dataProviderText).to.eq(`host.name: "${hostname}"`); }); }); }); diff --git a/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts index 25e50194f543d..cd60745b19040 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts @@ -249,6 +249,7 @@ describe('url state', () => { const timelineName = 'SIEM'; addNameToTimeline(timelineName); addDescriptionToTimeline('This is the best timeline of the world'); + cy.wait(5000); cy.url({ timeout: 30000 }).should('match', /\w*-\w*-\w*-\w*-\w*/); cy.url().then(url => { diff --git a/x-pack/legacy/plugins/siem/cypress/objects/rule.ts b/x-pack/legacy/plugins/siem/cypress/objects/rule.ts index a3c648c9cc934..ce920aeb957af 100644 --- a/x-pack/legacy/plugins/siem/cypress/objects/rule.ts +++ b/x-pack/legacy/plugins/siem/cypress/objects/rule.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const totalNumberOfPrebuiltRules = 92; +export const totalNumberOfPrebuiltRules = 130; interface Mitre { tactic: string; @@ -22,6 +22,7 @@ export interface CustomRule { referenceUrls: string[]; falsePositivesExamples: string[]; mitre: Mitre[]; + note: string; } export interface MachineLearningRule { @@ -36,6 +37,7 @@ export interface MachineLearningRule { referenceUrls: string[]; falsePositivesExamples: string[]; mitre: Mitre[]; + note: string; } const mitre1: Mitre = { @@ -58,6 +60,7 @@ export const newRule: CustomRule = { referenceUrls: ['https://www.google.com/', 'https://elastic.co/'], falsePositivesExamples: ['False1', 'False2'], mitre: [mitre1, mitre2], + note: '# test markdown', }; export const machineLearningRule: MachineLearningRule = { @@ -71,4 +74,5 @@ export const machineLearningRule: MachineLearningRule = { referenceUrls: ['https://elastic.co/'], falsePositivesExamples: ['False1'], mitre: [mitre1], + note: '# test markdown', }; diff --git a/x-pack/legacy/plugins/siem/cypress/screens/create_new_rule.ts b/x-pack/legacy/plugins/siem/cypress/screens/create_new_rule.ts index e603e2ee5158e..db9866cdf7f63 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/create_new_rule.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/create_new_rule.ts @@ -24,7 +24,8 @@ export const CUSTOM_QUERY_INPUT = '[data-test-subj="queryInput"]'; export const DEFINE_CONTINUE_BUTTON = '[data-test-subj="define-continue"]'; -export const SCHEDULE_CONTINUE_BUTTON = '[data-test-subj="schedule-continue"]'; +export const INVESTIGATION_NOTES_TEXTAREA = + '[data-test-subj="detectionEngineStepAboutRuleNote"] textarea'; export const FALSE_POSITIVES_INPUT = '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] input'; @@ -53,6 +54,8 @@ export const RULE_DESCRIPTION_INPUT = export const RULE_NAME_INPUT = '[data-test-subj="detectionEngineStepAboutRuleName"] [data-test-subj="input"]'; +export const SCHEDULE_CONTINUE_BUTTON = '[data-test-subj="schedule-continue"]'; + export const SEVERITY_DROPDOWN = '[data-test-subj="detectionEngineStepAboutRuleSeverity"] [data-test-subj="select"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts b/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts index fc9e4c56dd824..ec57e142125da 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts @@ -6,6 +6,8 @@ export const ABOUT_FALSE_POSITIVES = 3; +export const ABOUT_INVESTIGATION_NOTES = '[data-test-subj="stepAboutDetailsNoteContent"]'; + export const ABOUT_MITRE = 4; export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggleDescriptionText]'; @@ -32,10 +34,16 @@ export const DEFINITION_INDEX_PATTERNS = export const DEFINITION_STEP = '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description'; +export const INVESTIGATION_NOTES_MARKDOWN = 'test markdown'; + +export const INVESTIGATION_NOTES_TOGGLE = 1; + export const MACHINE_LEARNING_JOB_ID = '[data-test-subj="machineLearningJobId"]'; export const MACHINE_LEARNING_JOB_STATUS = '[data-test-subj="machineLearningJobStatus" ]'; +export const RULE_ABOUT_DETAILS_HEADER_TOGGLE = '[data-test-subj="stepAboutDetailsToggle"]'; + export const RULE_NAME_HEADER = '[data-test-subj="header-page-title"]'; export const RULE_TYPE = 0; diff --git a/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts b/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts index 65f24e3a765ea..09fbc2132302c 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts @@ -43,4 +43,6 @@ export const RULES_ROW = '.euiTableRow'; export const SEVERITY = '[data-test-subj="severity"]'; +export const SHOWING_RULES_TEXT = '[data-test-subj="showingRules"]'; + export const THREE_HUNDRED_ROWS = '[data-test-subj="tablePagination-300-rows"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/common.ts b/x-pack/legacy/plugins/siem/cypress/tasks/common.ts index 03a1fe4496030..b0c64214459f0 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/common.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/common.ts @@ -23,14 +23,14 @@ export const drag = (subject: JQuery) => { clientY: subjectLocation.top, force: true, }) - .wait(100) + .wait(1000) .trigger('mousemove', { button: primaryButton, clientX: subjectLocation.left + dndSloppyClickDetectionThreshold, clientY: subjectLocation.top, force: true, }) - .wait(100); + .wait(1000); }; /** Drags the subject being dragged on the specified drop target, but does not drop it */ @@ -44,7 +44,7 @@ export const dragWithoutDrop = (dropTarget: JQuery) => { export const drop = (dropTarget: JQuery) => { cy.wrap(dropTarget) .trigger('mousemove', { button: primaryButton, force: true }) - .wait(100) + .wait(1000) .trigger('mouseup', { force: true }) - .wait(100); + .wait(1000); }; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/create_new_rule.ts b/x-pack/legacy/plugins/siem/cypress/tasks/create_new_rule.ts index 59ed156bf56b1..a20ad372a689c 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/create_new_rule.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/create_new_rule.ts @@ -14,6 +14,7 @@ import { CUSTOM_QUERY_INPUT, DEFINE_CONTINUE_BUTTON, FALSE_POSITIVES_INPUT, + INVESTIGATION_NOTES_TEXTAREA, MACHINE_LEARNING_DROPDOWN, MACHINE_LEARNING_LIST, MACHINE_LEARNING_TYPE, @@ -82,6 +83,8 @@ export const fillAboutRuleAndContinue = (rule: CustomRule | MachineLearningRule) cy.get(MITRE_BTN).click({ force: true }); }); + cy.get(INVESTIGATION_NOTES_TEXTAREA).type(rule.note, { force: true }); + cy.get(ABOUT_CONTINUE_BTN) .should('exist') .click({ force: true }); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx index d0b1d8ffcb5ae..c7e368da1338f 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx @@ -8,9 +8,9 @@ import { EuiLink, EuiText } from '@elastic/eui'; import React, { useEffect, useState } from 'react'; import { createPortalNode, InPortal } from 'react-reverse-portal'; import styled, { css } from 'styled-components'; +import { npStart } from 'ui/new_platform'; -import { EmbeddablePanel } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { start } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; +import { EmbeddablePanel } from '../../../../../../../src/plugins/embeddable/public'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; import { getIndexPatternTitleIdMapping } from '../../hooks/api/helpers'; import { useIndexPatterns } from '../../hooks/use_index_patterns'; @@ -22,7 +22,8 @@ import { createEmbeddable, findMatchingIndexPatterns } from './embedded_map_help import { IndexPatternsMissingPrompt } from './index_patterns_missing_prompt'; import { MapToolTip } from './map_tool_tip/map_tool_tip'; import * as i18n from './translations'; -import { MapEmbeddable, SetQuery } from './types'; +import { SetQuery } from './types'; +import { MapEmbeddable } from '../../../../../plugins/maps/public'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; import { useKibana, useUiSetting$ } from '../../lib/kibana'; import { getSavedObjectFinder } from '../../../../../../../src/plugins/saved_objects/public'; @@ -197,8 +198,8 @@ export const EmbeddedMapComponent = ({ data-test-subj="embeddable-panel" embeddable={embeddable} getActions={services.uiActions.getTriggerCompatibleActions} - getEmbeddableFactory={start.getEmbeddableFactory} - getAllEmbeddableFactories={start.getEmbeddableFactories} + getEmbeddableFactory={npStart.plugins.embeddable.getEmbeddableFactory} + getAllEmbeddableFactories={npStart.plugins.embeddable.getEmbeddableFactories} notifications={services.notifications} overlays={services.overlays} inspector={services.inspector} diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index 888df8447a728..56211c9ff8935 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -8,14 +8,13 @@ import uuid from 'uuid'; import React from 'react'; import { OutPortal, PortalNode } from 'react-reverse-portal'; import minimatch from 'minimatch'; -import { ViewMode } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { IndexPatternMapping, MapEmbeddable, RenderTooltipContentParams, SetQuery } from './types'; +import { IndexPatternMapping, SetQuery } from './types'; import { getLayerList } from './map_config'; -// @ts-ignore Missing type defs as maps moves to Typescript -import { MAP_SAVED_OBJECT_TYPE } from '../../../../maps/common/constants'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../plugins/maps/public'; +import { MapEmbeddable, RenderTooltipContentParams } from '../../../../maps/public'; import * as i18n from './translations'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; -import { EmbeddableStart } from '../../../../../../../src/plugins/embeddable/public'; +import { EmbeddableStart, ViewMode } from '../../../../../../../src/plugins/embeddable/public'; import { IndexPatternSavedObject } from '../../hooks/types'; /** diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx index 15c423a3b3dc1..fc55e3437dc21 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx @@ -64,7 +64,7 @@ export const MapToolTipComponent = ({ getLayerName(layerId), ]); - setFeatureProps(featureProperties); + setFeatureProps((featureProperties as unknown) as FeatureProperty[]); setFeatureGeometry(featureGeo); setLayerName(layerNameString); } catch (e) { diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts index cc253beb08eae..216fe9105327c 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts @@ -4,26 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimeRange } from 'src/plugins/data/public'; -import { - EmbeddableInput, - EmbeddableOutput, - IEmbeddable, -} from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +import { RenderTooltipContentParams } from '../../../../maps/public'; import { inputsModel } from '../../store/inputs'; -import { Query, Filter } from '../../../../../../../src/plugins/data/public'; - -export interface MapEmbeddableInput extends EmbeddableInput { - filters: Filter[]; - query: Query; - refreshConfig: { - isPaused: boolean; - interval: number; - }; - timeRange?: TimeRange; -} - -export type MapEmbeddable = IEmbeddable; export interface IndexPatternMapping { title: string; @@ -73,14 +55,4 @@ export interface FeatureGeometry { type: string; } -export interface RenderTooltipContentParams { - addFilters(filter: object): void; - closeTooltip(): void; - features: MapFeature[]; - isLocked: boolean; - getLayerName(layerId: string): Promise; - loadFeatureProperties({ layerId, featureId }: LoadFeatureProps): Promise; - loadFeatureGeometry({ layerId, featureId }: LoadFeatureProps): FeatureGeometry; -} - export type MapToolTipProps = Partial; diff --git a/x-pack/legacy/plugins/siem/public/components/filter_popover/index.tsx b/x-pack/legacy/plugins/siem/public/components/filter_popover/index.tsx index 0c4497f7630c9..3c01ec18a879f 100644 --- a/x-pack/legacy/plugins/siem/public/components/filter_popover/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/filter_popover/index.tsx @@ -76,6 +76,7 @@ export const FilterPopoverComponent = ({ numFilters={options.length} hasActiveFilters={selectedOptions.length > 0} numActiveFilters={selectedOptions.length} + aria-label={buttonLabel} > {buttonLabel} diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/editable_title.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/editable_title.test.tsx.snap index 24b1756aade2e..c8d4b6ec3b4c8 100644 --- a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/editable_title.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/editable_title.test.tsx.snap @@ -19,6 +19,7 @@ exports[`EditableTitle it renders 1`] = ` aria-label="You can edit Test title by clicking" data-test-subj="editable-title-edit-icon" iconType="pencil" + isDisabled={false} onClick={[Function]} /> diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/editable_title.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/editable_title.tsx index 29cc1579f9bcc..165be00384779 100644 --- a/x-pack/legacy/plugins/siem/public/components/header_page/editable_title.tsx +++ b/x-pack/legacy/plugins/siem/public/components/header_page/editable_title.tsx @@ -34,12 +34,18 @@ const MySpinner = styled(EuiLoadingSpinner)` `; interface Props { + disabled?: boolean; isLoading: boolean; title: string | React.ReactNode; onSubmit: (title: string) => void; } -const EditableTitleComponent: React.FC = ({ onSubmit, isLoading, title }) => { +const EditableTitleComponent: React.FC = ({ + disabled = false, + onSubmit, + isLoading, + title, +}) => { const [editMode, setEditMode] = useState(false); const [changedTitle, onTitleChange] = useState(typeof title === 'string' ? title : ''); @@ -104,6 +110,7 @@ const EditableTitleComponent: React.FC = ({ onSubmit, isLoading, title }) {isLoading && } {!isLoading && ( string; importComplete: () => void; - importData: (arg: ImportRulesProps) => Promise; + importData: (arg: ImportDataProps) => Promise; showCheckBox: boolean; showModal: boolean; submitBtnText: string; @@ -75,7 +75,7 @@ export const ImportDataModalComponent = ({ closeModal(); }, [setIsImporting, setSelectedFiles, closeModal]); - const importRulesCallback = useCallback(async () => { + const importDataCallback = useCallback(async () => { if (selectedFiles != null) { setIsImporting(true); const abortCtrl = new AbortController(); @@ -152,7 +152,7 @@ export const ImportDataModalComponent = ({ {i18n.CANCEL_BUTTON} diff --git a/x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap index c5086c8cde285..986e9161c519d 100644 --- a/x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap @@ -2,6 +2,7 @@ exports[`LinkIcon it renders 1`] = ` {children})< @@ -52,7 +53,17 @@ export interface LinkIconProps extends LinkProps { } export const LinkIcon = React.memo( - ({ children, color, disabled, href, iconSide = 'left', iconSize = 's', iconType, onClick }) => ( + ({ + children, + color, + disabled, + href, + iconSide = 'left', + iconSize = 's', + iconType, + onClick, + ariaLabel, + }) => ( ( href={href} iconSide={iconSide} onClick={onClick} + aria-label={ariaLabel ?? children} > {children} diff --git a/x-pack/legacy/plugins/siem/public/components/links/index.tsx b/x-pack/legacy/plugins/siem/public/components/links/index.tsx index 14dc5e7999a65..62a67af6e08b1 100644 --- a/x-pack/legacy/plugins/siem/public/components/links/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/links/index.tsx @@ -23,12 +23,13 @@ import { import { FlowTarget, FlowTargetSourceDest } from '../../graphql/types'; import { useUiSetting$ } from '../../lib/kibana'; import { IP_REPUTATION_LINKS_SETTING } from '../../../common/constants'; -import * as i18n from '../page/network/ip_overview/translations'; import { isUrlInvalid } from '../../pages/detection_engine/rules/components/step_about_rule/helpers'; import { ExternalLinkIcon } from '../external_link_icon'; import { navTabs } from '../../pages/home/home_navigations'; import { useGetUrlSearch } from '../navigation/use_get_url_search'; +import * as i18n from './translations'; + export const DEFAULT_NUMBER_OF_LINK = 5; // Internal Links @@ -88,16 +89,18 @@ const IPDetailsLinkComponent: React.FC<{ export const IPDetailsLink = React.memo(IPDetailsLinkComponent); -const CaseDetailsLinkComponent: React.FC<{ children?: React.ReactNode; detailName: string }> = ({ - children, - detailName, -}) => { +const CaseDetailsLinkComponent: React.FC<{ + children?: React.ReactNode; + detailName: string; + title?: string; +}> = ({ children, detailName, title }) => { const search = useGetUrlSearch(navTabs.case); return ( {children ? children : detailName} diff --git a/x-pack/legacy/plugins/siem/public/components/links/translations.ts b/x-pack/legacy/plugins/siem/public/components/links/translations.ts new file mode 100644 index 0000000000000..bed867cd5bf50 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/links/translations.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export * from '../page/network/ip_overview/translations'; + +export const CASE_DETAILS_LINK_ARIA = (detailName: string) => + i18n.translate('xpack.siem.case.caseTable.caseDetailsLinkAria', { + values: { detailName }, + defaultMessage: 'click to visit case with title {detailName}', + }); 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 e5066eef18c8b..a0e0c70d2f204 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 @@ -8,7 +8,11 @@ import styled from 'styled-components'; import React, { useState, useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSwitch } from '@elastic/eui'; import { SiemJob } from '../types'; -import { isJobLoading, isJobStarted, isJobFailed } from '../../ml/helpers'; +import { + isJobLoading, + isJobFailed, + isJobStarted, +} from '../../../../common/detection_engine/ml_helpers'; const StaticSwitch = styled(EuiSwitch)` .euiSwitch__thumb, diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.test.ts b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.test.ts index 60ebd2578b7c0..a779d579bf4d1 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.test.ts @@ -4,16 +4,48 @@ * you may not use this file except in compliance with the Elastic License. */ import { cloneDeep, omit } from 'lodash/fp'; +import { Dispatch } from 'redux'; -import { mockTimelineResults } from '../../mock/timeline_results'; +import { + mockTimelineResults, + mockTimelineResult, + mockTimelineModel, +} from '../../mock/timeline_results'; import { timelineDefaults } from '../../store/timeline/defaults'; +import { setTimelineRangeDatePicker as dispatchSetTimelineRangeDatePicker } from '../../store/inputs/actions'; +import { + setKqlFilterQueryDraft as dispatchSetKqlFilterQueryDraft, + applyKqlFilterQuery as dispatchApplyKqlFilterQuery, + addTimeline as dispatchAddTimeline, + addNote as dispatchAddGlobalTimelineNote, +} from '../../store/timeline/actions'; +import { + addNotes as dispatchAddNotes, + updateNote as dispatchUpdateNote, +} from '../../store/app/actions'; import { defaultTimelineToTimelineModel, getNotesCount, getPinnedEventCount, isUntitled, + omitTypenameInTimeline, + dispatchUpdateTimeline, } from './helpers'; -import { OpenTimelineResult } from './types'; +import { OpenTimelineResult, DispatchUpdateTimeline } from './types'; +import { KueryFilterQueryKind } from '../../store/model'; +import { Note } from '../../lib/note'; +import moment from 'moment'; +import sinon from 'sinon'; + +jest.mock('../../store/inputs/actions'); +jest.mock('../../store/timeline/actions'); +jest.mock('../../store/app/actions'); +jest.mock('uuid', () => { + return { + v1: jest.fn(() => 'uuid.v1()'), + v4: jest.fn(() => 'uuid.v4()'), + }; +}); describe('helpers', () => { let mockResults: OpenTimelineResult[]; @@ -620,4 +652,229 @@ describe('helpers', () => { }); }); }); + + describe('omitTypenameInTimeline', () => { + test('it does not modify the passed in timeline if no __typename exists', () => { + const result = omitTypenameInTimeline(mockTimelineResult); + + expect(result).toEqual(mockTimelineResult); + }); + + test('it returns timeline with __typename removed when it exists', () => { + const mockTimeline = { + ...mockTimelineResult, + __typename: 'something, something', + }; + const result = omitTypenameInTimeline(mockTimeline); + const expectedTimeline = { + ...mockTimeline, + __typename: undefined, + }; + + expect(result).toEqual(expectedTimeline); + }); + }); + + describe('dispatchUpdateTimeline', () => { + const dispatch = jest.fn() as Dispatch; + const anchor = '2020-03-27T20:34:51.337Z'; + const unix = moment(anchor).valueOf(); + let clock: sinon.SinonFakeTimers; + let timelineDispatch: DispatchUpdateTimeline; + + beforeEach(() => { + jest.clearAllMocks(); + + clock = sinon.useFakeTimers(unix); + timelineDispatch = dispatchUpdateTimeline(dispatch); + }); + + afterEach(function() { + clock.restore(); + }); + + test('it invokes date range picker dispatch', () => { + timelineDispatch({ + duplicate: true, + id: 'timeline-1', + from: 1585233356356, + to: 1585233716356, + notes: [], + timeline: mockTimelineModel, + })(); + + expect(dispatchSetTimelineRangeDatePicker).toHaveBeenCalledWith({ + from: 1585233356356, + to: 1585233716356, + }); + }); + + test('it invokes add timeline dispatch', () => { + timelineDispatch({ + duplicate: true, + id: 'timeline-1', + from: 1585233356356, + to: 1585233716356, + notes: [], + timeline: mockTimelineModel, + })(); + + expect(dispatchAddTimeline).toHaveBeenCalledWith({ + id: 'timeline-1', + timeline: mockTimelineModel, + }); + }); + + test('it does not invoke kql filter query dispatches if timeline.kqlQuery.filterQuery is null', () => { + timelineDispatch({ + duplicate: true, + id: 'timeline-1', + from: 1585233356356, + to: 1585233716356, + notes: [], + timeline: mockTimelineModel, + })(); + + expect(dispatchSetKqlFilterQueryDraft).not.toHaveBeenCalled(); + expect(dispatchApplyKqlFilterQuery).not.toHaveBeenCalled(); + }); + + test('it does not invoke notes dispatch if duplicate is true', () => { + timelineDispatch({ + duplicate: true, + id: 'timeline-1', + from: 1585233356356, + to: 1585233716356, + notes: [], + timeline: mockTimelineModel, + })(); + + expect(dispatchAddNotes).not.toHaveBeenCalled(); + }); + + test('it does not invoke kql filter query dispatches if timeline.kqlQuery.kuery is null', () => { + const mockTimeline = { + ...mockTimelineModel, + kqlQuery: { + filterQuery: { + kuery: null, + serializedQuery: 'some-serialized-query', + }, + filterQueryDraft: null, + }, + }; + timelineDispatch({ + duplicate: true, + id: 'timeline-1', + from: 1585233356356, + to: 1585233716356, + notes: [], + timeline: mockTimeline, + })(); + + expect(dispatchSetKqlFilterQueryDraft).not.toHaveBeenCalled(); + expect(dispatchApplyKqlFilterQuery).not.toHaveBeenCalled(); + }); + + test('it invokes kql filter query dispatches if timeline.kqlQuery.filterQuery.kuery is not null', () => { + const mockTimeline = { + ...mockTimelineModel, + kqlQuery: { + filterQuery: { + kuery: { expression: 'expression', kind: 'kuery' as KueryFilterQueryKind }, + serializedQuery: 'some-serialized-query', + }, + filterQueryDraft: null, + }, + }; + timelineDispatch({ + duplicate: true, + id: 'timeline-1', + from: 1585233356356, + to: 1585233716356, + notes: [], + timeline: mockTimeline, + })(); + + expect(dispatchSetKqlFilterQueryDraft).toHaveBeenCalledWith({ + id: 'timeline-1', + filterQueryDraft: { + kind: 'kuery', + expression: 'expression', + }, + }); + expect(dispatchApplyKqlFilterQuery).toHaveBeenCalledWith({ + id: 'timeline-1', + filterQuery: { + kuery: { + kind: 'kuery', + expression: 'expression', + }, + serializedQuery: 'some-serialized-query', + }, + }); + }); + + test('it invokes dispatchAddNotes if duplicate is false', () => { + timelineDispatch({ + duplicate: false, + id: 'timeline-1', + from: 1585233356356, + to: 1585233716356, + notes: [ + { + created: 1585233356356, + updated: 1585233356356, + noteId: 'note-id', + note: 'I am a note', + }, + ], + timeline: mockTimelineModel, + })(); + + expect(dispatchAddGlobalTimelineNote).not.toHaveBeenCalled(); + expect(dispatchUpdateNote).not.toHaveBeenCalled(); + expect(dispatchAddNotes).toHaveBeenCalledWith({ + notes: [ + { + created: new Date('2020-03-26T14:35:56.356Z'), + id: 'note-id', + lastEdit: new Date('2020-03-26T14:35:56.356Z'), + note: 'I am a note', + user: 'unknown', + saveObjectId: 'note-id', + version: undefined, + }, + ], + }); + }); + + test('it invokes dispatch to create a timeline note if duplicate is true and ruleNote exists', () => { + timelineDispatch({ + duplicate: true, + id: 'timeline-1', + from: 1585233356356, + to: 1585233716356, + notes: [], + timeline: mockTimelineModel, + ruleNote: '# this would be some markdown', + })(); + const expectedNote: Note = { + created: new Date(anchor), + id: 'uuid.v4()', + lastEdit: null, + note: '# this would be some markdown', + saveObjectId: null, + user: 'elastic', + version: null, + }; + + expect(dispatchAddNotes).not.toHaveBeenCalled(); + expect(dispatchUpdateNote).toHaveBeenCalledWith({ note: expectedNote }); + expect(dispatchAddGlobalTimelineNote).toHaveBeenLastCalledWith({ + id: 'timeline-1', + noteId: 'uuid.v4()', + }); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts index 4f7d6cd64f1d9..16ba2de872bd1 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts @@ -5,18 +5,23 @@ */ import ApolloClient from 'apollo-client'; -import { getOr, set } from 'lodash/fp'; +import { getOr, set, isEmpty } from 'lodash/fp'; import { Action } from 'typescript-fsa'; +import uuid from 'uuid'; import { Dispatch } from 'redux'; import { oneTimelineQuery } from '../../containers/timeline/one/index.gql_query'; import { TimelineResult, GetOneTimeline, NoteResult } from '../../graphql/types'; -import { addNotes as dispatchAddNotes } from '../../store/app/actions'; +import { + addNotes as dispatchAddNotes, + updateNote as dispatchUpdateNote, +} from '../../store/app/actions'; import { setTimelineRangeDatePicker as dispatchSetTimelineRangeDatePicker } from '../../store/inputs/actions'; import { setKqlFilterQueryDraft as dispatchSetKqlFilterQueryDraft, applyKqlFilterQuery as dispatchApplyKqlFilterQuery, addTimeline as dispatchAddTimeline, + addNote as dispatchAddGlobalTimelineNote, } from '../../store/timeline/actions'; import { ColumnHeaderOptions, TimelineModel } from '../../store/timeline/model'; @@ -32,6 +37,7 @@ import { import { OpenTimelineResult, UpdateTimeline, DispatchUpdateTimeline } from './types'; import { getTimeRangeSettings } from '../../utils/default_date_settings'; +import { createNote } from '../notes/helpers'; export const OPEN_TIMELINE_CLASS_NAME = 'open-timeline'; @@ -250,6 +256,7 @@ export const dispatchUpdateTimeline = (dispatch: Dispatch): DispatchUpdateTimeli notes, timeline, to, + ruleNote, }: UpdateTimeline): (() => void) => () => { dispatch(dispatchSetTimelineRangeDatePicker({ from, to })); dispatch(dispatchAddTimeline({ id, timeline })); @@ -281,6 +288,14 @@ export const dispatchUpdateTimeline = (dispatch: Dispatch): DispatchUpdateTimeli }) ); } + + if (duplicate && ruleNote != null && !isEmpty(ruleNote)) { + const getNewNoteId = (): string => uuid.v4(); + const newNote = createNote({ newNote: ruleNote, getNewNoteId }); + dispatch(dispatchUpdateNote({ note: newNote })); + dispatch(dispatchAddGlobalTimelineNote({ noteId: newNote.id, id })); + } + if (!duplicate) { dispatch( dispatchAddNotes({ diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx index 6c2cd21d808b7..c27a6039da29d 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx @@ -54,7 +54,7 @@ interface OwnProps { export type OpenTimelineOwnProps = OwnProps & Pick< OpenTimelineProps, - 'defaultPageSize' | 'title' | 'importCompleteToggle' | 'setImportCompleteToggle' + 'defaultPageSize' | 'title' | 'importDataModalToggle' | 'setImportDataModalToggle' > & PropsFromRedux; @@ -77,9 +77,9 @@ export const StatefulOpenTimelineComponent = React.memo( defaultPageSize, hideActions = [], isModal = false, - importCompleteToggle, + importDataModalToggle, onOpenTimeline, - setImportCompleteToggle, + setImportDataModalToggle, timeline, title, updateTimeline, @@ -269,7 +269,7 @@ export const StatefulOpenTimelineComponent = React.memo( defaultPageSize={defaultPageSize} isLoading={loading} itemIdToExpandedNotesRowMap={itemIdToExpandedNotesRowMap} - importCompleteToggle={importCompleteToggle} + importDataModalToggle={importDataModalToggle} onAddTimelinesToFavorites={undefined} onDeleteSelected={onDeleteSelected} onlyFavorites={onlyFavorites} @@ -284,7 +284,7 @@ export const StatefulOpenTimelineComponent = React.memo( query={search} refetch={refetch} searchResults={timelines} - setImportCompleteToggle={setImportCompleteToggle} + setImportDataModalToggle={setImportDataModalToggle} selectedItems={selectedItems} sortDirection={sortDirection} sortField={sortField} diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/open_timeline.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/open_timeline.tsx index 8b3da4427a362..6b2f953b82de4 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/open_timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/open_timeline.tsx @@ -33,7 +33,7 @@ export const OpenTimeline = React.memo( defaultPageSize, isLoading, itemIdToExpandedNotesRowMap, - importCompleteToggle, + importDataModalToggle, onAddTimelinesToFavorites, onDeleteSelected, onlyFavorites, @@ -50,7 +50,7 @@ export const OpenTimeline = React.memo( searchResults, selectedItems, sortDirection, - setImportCompleteToggle, + setImportDataModalToggle, sortField, title, totalSearchResultsCount, @@ -103,18 +103,18 @@ export const OpenTimeline = React.memo( }, [refetch]); const handleCloseModal = useCallback(() => { - if (setImportCompleteToggle != null) { - setImportCompleteToggle(false); + if (setImportDataModalToggle != null) { + setImportDataModalToggle(false); } - }, [setImportCompleteToggle]); + }, [setImportDataModalToggle]); const handleComplete = useCallback(() => { - if (setImportCompleteToggle != null) { - setImportCompleteToggle(false); + if (setImportDataModalToggle != null) { + setImportDataModalToggle(false); } if (refetch != null) { refetch(); } - }, [setImportCompleteToggle, refetch]); + }, [setImportDataModalToggle, refetch]); return ( <> @@ -136,7 +136,7 @@ export const OpenTimeline = React.memo( importData={importTimelines} successMessage={i18n.SUCCESSFULLY_IMPORTED_TIMELINES} showCheckBox={false} - showModal={importCompleteToggle ?? false} + showModal={importDataModalToggle ?? false} submitBtnText={i18n.IMPORT_TIMELINE_BTN_TITLE} subtitle={i18n.INITIAL_PROMPT_TEXT} title={i18n.IMPORT_TIMELINE} diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.test.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.test.tsx index ca82e30798d82..b0f8963dd501e 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.test.tsx @@ -70,6 +70,25 @@ describe('#getActionsColumns', () => { expect(wrapper.find('[data-test-subj="open-duplicate"]').exists()).toBe(true); }); + test('it renders only duplicate icon (without heading)', () => { + const testProps: TimelinesTableProps = { + ...getMockTimelinesTableProps(mockResults), + actionTimelineToShow: ['duplicate'], + }; + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper + .find('[data-test-subj="open-duplicate"]') + .first() + .text() + ).toEqual(''); + }); + test('it does NOT render the duplicate timeline when actionTimelineToShow is NOT including the action duplicate)', () => { const testProps: TimelinesTableProps = { ...getMockTimelinesTableProps(mockResults), @@ -156,4 +175,72 @@ describe('#getActionsColumns', () => { expect(onOpenTimeline).toBeCalledWith({ duplicate: true, timelineId: 'saved-timeline-11' }); }); + + test('it renders the export icon when enableExportTimelineDownloader is including the action export', () => { + const testProps: TimelinesTableProps = { + ...getMockTimelinesTableProps(mockResults), + actionTimelineToShow: ['export'], + }; + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.find('[data-test-subj="export-timeline"]').exists()).toBe(true); + }); + + test('it renders No export icon when export is not included in the action ', () => { + const testProps: TimelinesTableProps = { + ...getMockTimelinesTableProps(mockResults), + }; + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.find('[data-test-subj="export-timeline"]').exists()).toBe(false); + }); + + test('it renders a disabled the export button if the timeline does not have a saved object id', () => { + const missingSavedObjectId: OpenTimelineResult[] = [ + omit('savedObjectId', { ...mockResults[0] }), + ]; + + const testProps: TimelinesTableProps = { + ...getMockTimelinesTableProps(missingSavedObjectId), + actionTimelineToShow: ['export'], + }; + const wrapper = mountWithIntl( + + + + ); + + const props = wrapper + .find('[data-test-subj="export-timeline"]') + .first() + .props() as EuiButtonIconProps; + expect(props.isDisabled).toBe(true); + }); + + test('it invokes enableExportTimelineDownloader with the expected params when the button is clicked', () => { + const enableExportTimelineDownloader = jest.fn(); + const testProps: TimelinesTableProps = { + ...getMockTimelinesTableProps(mockResults), + actionTimelineToShow: ['export'], + enableExportTimelineDownloader, + }; + const wrapper = mountWithIntl( + + + + ); + + wrapper + .find('[data-test-subj="export-timeline"]') + .first() + .simulate('click'); + + expect(enableExportTimelineDownloader).toBeCalledWith(mockResults[0]); + }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.tsx index 4bbf98dafe38d..746503308c833 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.tsx +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.tsx @@ -42,6 +42,7 @@ export const getActionsColumns = ({ timelineId: savedObjectId ?? '', }); }, + type: 'icon', enabled: ({ savedObjectId }: OpenTimelineResult) => savedObjectId != null, description: i18n.OPEN_AS_DUPLICATE, 'data-test-subj': 'open-duplicate', @@ -55,6 +56,7 @@ export const getActionsColumns = ({ }, enabled: ({ savedObjectId }: OpenTimelineResult) => savedObjectId != null, description: i18n.EXPORT_SELECTED, + 'data-test-subj': 'export-timeline', }; const deleteTimelineColumn = { diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts b/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts index 1265c056ec506..b7cc92ebd183f 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts @@ -121,7 +121,7 @@ export interface OpenTimelineProps { /** Required by EuiTable for expandable rows: a map of `TimelineResult.savedObjectId` to rendered notes */ itemIdToExpandedNotesRowMap: Record; /** Display import timelines modal*/ - importCompleteToggle?: boolean; + importDataModalToggle?: boolean; /** If this callback is specified, a "Favorite Selected" button will be displayed, and this callback will be invoked when the button is clicked */ onAddTimelinesToFavorites?: OnAddTimelinesToFavorites; /** If this callback is specified, a "Delete Selected" button will be displayed, and this callback will be invoked when the button is clicked */ @@ -153,7 +153,7 @@ export interface OpenTimelineProps { /** the currently-selected timelines in the table */ selectedItems: OpenTimelineResult[]; /** Toggle export timelines modal*/ - setImportCompleteToggle?: React.Dispatch>; + setImportDataModalToggle?: React.Dispatch>; /** the requested sort direction of the query results */ sortDirection: 'asc' | 'desc'; /** the requested field to sort on */ @@ -173,6 +173,7 @@ export interface UpdateTimeline { notes: NoteResult[] | null | undefined; timeline: TimelineModel; to: number; + ruleNote?: string; } export type DispatchUpdateTimeline = ({ @@ -182,4 +183,5 @@ export type DispatchUpdateTimeline = ({ notes, timeline, to, + ruleNote, }: UpdateTimeline) => () => void; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/index.tsx index fa474c4d601ad..cf1a4ebec9bb6 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButtonIcon, EuiPopover, EuiSelectableOption } from '@elastic/eui'; +import { EuiButtonIcon, EuiPopover, EuiSelectableOption, EuiToolTip } from '@elastic/eui'; import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { useDispatch } from 'react-redux'; @@ -62,13 +62,15 @@ export const InsertTimelinePopoverComponent: React.FC = ({ const insertTimelineButton = useMemo( () => ( - + {i18n.INSERT_TIMELINE}

}> + + ), [handleOpenPopover, isDisabled] ); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/use_insert_timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/use_insert_timeline.tsx index 0cfb07cccfd6c..e4d828b68f3dc 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/use_insert_timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/use_insert_timeline.tsx @@ -17,7 +17,7 @@ export const useInsertTimeline = (form: FormHook, fieldNa }); const handleOnTimelineChange = useCallback( (title: string, id: string | null) => { - const builtLink = `${basePath}/app/siem#/timelines?timeline=(id:${id},isOpen:!t)`; + const builtLink = `${basePath}/app/siem#/timelines?timeline=(id:'${id}',isOpen:!t)`; const currentValue = form.getFormData()[fieldName]; const newValue: string = [ currentValue.slice(0, cursorPosition.start), diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/translations.ts b/x-pack/legacy/plugins/siem/public/components/timeline/translations.ts index de3e3c8e792fe..101837168350f 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/translations.ts +++ b/x-pack/legacy/plugins/siem/public/components/timeline/translations.ts @@ -25,5 +25,5 @@ export const SEARCH_BOX_TIMELINE_PLACEHOLDER = i18n.translate( ); export const INSERT_TIMELINE = i18n.translate('xpack.siem.insert.timeline.insertTimelineButton', { - defaultMessage: 'Insert Timeline…', + defaultMessage: 'Insert timeline link', }); diff --git a/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx b/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx index 19e884e326390..330e7f83b5b28 100644 --- a/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx +++ b/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx @@ -18,6 +18,7 @@ const Popover = React.memo( return ( => { +export const getCase = async ( + caseId: string, + includeComments: boolean = true, + signal: AbortSignal +): Promise => { const response = await KibanaServices.get().http.fetch(`${CASES_URL}/${caseId}`, { method: 'GET', query: { includeComments, }, + signal, }); return convertToCamelCase(decodeCaseResponse(response)); }; @@ -66,9 +73,10 @@ export const getCasesStatus = async (signal: AbortSignal): Promise return convertToCamelCase(decodeCasesStatusResponse(response)); }; -export const getTags = async (): Promise => { +export const getTags = async (signal: AbortSignal): Promise => { const response = await KibanaServices.get().http.fetch(`${CASES_URL}/tags`, { method: 'GET', + signal, }); return response ?? []; }; @@ -108,9 +116,10 @@ export const getCases = async ({ sortField: SortFieldCase.createdAt, sortOrder: 'desc', }, + signal, }: FetchCasesProps): Promise => { const query = { - reporters: filterOptions.reporters.map(r => r.username), + reporters: filterOptions.reporters.map(r => r.username ?? '').filter(r => r !== ''), tags: filterOptions.tags, ...(filterOptions.status !== '' ? { status: filterOptions.status } : {}), ...(filterOptions.search.length > 0 ? { search: filterOptions.search } : {}), @@ -119,69 +128,85 @@ export const getCases = async ({ const response = await KibanaServices.get().http.fetch(`${CASES_URL}/_find`, { method: 'GET', query, + signal, }); return convertAllCasesToCamel(decodeCasesFindResponse(response)); }; -export const postCase = async (newCase: CaseRequest): Promise => { +export const postCase = async (newCase: CasePostRequest, signal: AbortSignal): Promise => { const response = await KibanaServices.get().http.fetch(CASES_URL, { method: 'POST', body: JSON.stringify(newCase), + signal, }); return convertToCamelCase(decodeCaseResponse(response)); }; export const patchCase = async ( caseId: string, - updatedCase: Partial, - version: string + updatedCase: Pick, + version: string, + signal: AbortSignal ): Promise => { const response = await KibanaServices.get().http.fetch(CASES_URL, { method: 'PATCH', body: JSON.stringify({ cases: [{ ...updatedCase, id: caseId, version }] }), + signal, }); return convertToCamelCase(decodeCasesResponse(response)); }; -export const patchCasesStatus = async (cases: BulkUpdateStatus[]): Promise => { +export const patchCasesStatus = async ( + cases: BulkUpdateStatus[], + signal: AbortSignal +): Promise => { const response = await KibanaServices.get().http.fetch(CASES_URL, { method: 'PATCH', body: JSON.stringify({ cases }), + signal, }); return convertToCamelCase(decodeCasesResponse(response)); }; -export const postComment = async (newComment: CommentRequest, caseId: string): Promise => { - const response = await KibanaServices.get().http.fetch( +export const postComment = async ( + newComment: CommentRequest, + caseId: string, + signal: AbortSignal +): Promise => { + const response = await KibanaServices.get().http.fetch( `${CASES_URL}/${caseId}/comments`, { method: 'POST', body: JSON.stringify(newComment), + signal, } ); - return convertToCamelCase(decodeCommentResponse(response)); + return convertToCamelCase(decodeCaseResponse(response)); }; export const patchComment = async ( caseId: string, commentId: string, commentUpdate: string, - version: string -): Promise> => { - const response = await KibanaServices.get().http.fetch( + version: string, + signal: AbortSignal +): Promise => { + const response = await KibanaServices.get().http.fetch( `${CASES_URL}/${caseId}/comments`, { method: 'PATCH', body: JSON.stringify({ comment: commentUpdate, id: commentId, version }), + signal, } ); - return convertToCamelCase(decodeCommentResponse(response)); + return convertToCamelCase(decodeCaseResponse(response)); }; -export const deleteCases = async (caseIds: string[]): Promise => { +export const deleteCases = async (caseIds: string[], signal: AbortSignal): Promise => { const response = await KibanaServices.get().http.fetch(CASES_URL, { method: 'DELETE', query: { ids: JSON.stringify(caseIds) }, + signal, }); return response === 'true' ? true : false; }; diff --git a/x-pack/legacy/plugins/siem/public/containers/case/configure/translations.ts b/x-pack/legacy/plugins/siem/public/containers/case/configure/translations.ts new file mode 100644 index 0000000000000..dbd618f40155d --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/containers/case/configure/translations.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. + */ + +import { i18n } from '@kbn/i18n'; + +export * from '../translations'; + +export const SUCCESS_CONFIGURE = i18n.translate('xpack.siem.case.configure.successSaveToast', { + defaultMessage: 'Saved external connection settings', +}); diff --git a/x-pack/legacy/plugins/siem/public/containers/case/configure/use_configure.tsx b/x-pack/legacy/plugins/siem/public/containers/case/configure/use_configure.tsx index b25667f070fdf..6524c40a8e6e4 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/configure/use_configure.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/configure/use_configure.tsx @@ -7,8 +7,8 @@ import { useState, useEffect, useCallback } from 'react'; import { getCaseConfigure, patchCaseConfigure, postCaseConfigure } from './api'; -import { useStateToaster, errorToToaster } from '../../../components/toasters'; -import * as i18n from '../translations'; +import { useStateToaster, errorToToaster, displaySuccessToast } from '../../../components/toasters'; +import * as i18n from './translations'; import { ClosureType } from './types'; import { CurrentConfiguration } from '../../../pages/case/components/configure_cases/reducer'; @@ -124,6 +124,8 @@ export const useCaseConfigure = ({ closureType: res.closureType, }); } + + displaySuccessToast(i18n.SUCCESS_CONFIGURE, dispatchToaster); } } catch (error) { if (!didCancel) { diff --git a/x-pack/legacy/plugins/siem/public/containers/case/translations.ts b/x-pack/legacy/plugins/siem/public/containers/case/translations.ts index 601db373f041e..a453be32480e2 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/translations.ts +++ b/x-pack/legacy/plugins/siem/public/containers/case/translations.ts @@ -10,6 +10,46 @@ export const ERROR_TITLE = i18n.translate('xpack.siem.containers.case.errorTitle defaultMessage: 'Error fetching data', }); +export const ERROR_DELETING = i18n.translate('xpack.siem.containers.case.errorDeletingTitle', { + defaultMessage: 'Error deleting data', +}); + +export const UPDATED_CASE = (caseTitle: string) => + i18n.translate('xpack.siem.containers.case.updatedCase', { + values: { caseTitle }, + defaultMessage: 'Updated "{caseTitle}"', + }); + +export const DELETED_CASES = (totalCases: number, caseTitle?: string) => + i18n.translate('xpack.siem.containers.case.deletedCases', { + values: { caseTitle, totalCases }, + defaultMessage: 'Deleted {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', + }); + +export const CLOSED_CASES = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + i18n.translate('xpack.siem.containers.case.closedCases', { + values: { caseTitle, totalCases }, + defaultMessage: 'Closed {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', + }); + +export const REOPENED_CASES = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + i18n.translate('xpack.siem.containers.case.reopenedCases', { + values: { caseTitle, totalCases }, + defaultMessage: 'Reopened {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', + }); + export const TAG_FETCH_FAILURE = i18n.translate( 'xpack.siem.containers.case.tagFetchFailDescription', { diff --git a/x-pack/legacy/plugins/siem/public/containers/case/types.ts b/x-pack/legacy/plugins/siem/public/containers/case/types.ts index bbbb13788d53a..d2a58e9eeeff4 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/types.ts +++ b/x-pack/legacy/plugins/siem/public/containers/case/types.ts @@ -90,10 +90,10 @@ export enum SortFieldCase { export interface ElasticUser { readonly email?: string | null; readonly fullName?: string | null; - readonly username: string; + readonly username?: string | null; } -export interface FetchCasesProps { +export interface FetchCasesProps extends ApiProps { queryParams?: QueryParams; filterOptions?: FilterOptions; } @@ -114,3 +114,8 @@ export interface ActionLicense { enabledInConfig: boolean; enabledInLicense: boolean; } + +export interface DeleteCase { + id: string; + title?: string; +} diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_bulk_update_case.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_bulk_update_case.tsx index 77d779ab906cf..7d040c49f1971 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_bulk_update_case.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_bulk_update_case.tsx @@ -5,7 +5,7 @@ */ import { useCallback, useReducer } from 'react'; -import { errorToToaster, useStateToaster } from '../../components/toasters'; +import { displaySuccessToast, errorToToaster, useStateToaster } from '../../components/toasters'; import * as i18n from './translations'; import { patchCasesStatus } from './api'; import { BulkUpdateStatus, Case } from './types'; @@ -66,12 +66,27 @@ export const useUpdateCases = (): UseUpdateCase => { const dispatchUpdateCases = useCallback((cases: BulkUpdateStatus[]) => { let cancel = false; + const abortCtrl = new AbortController(); + const patchData = async () => { try { dispatch({ type: 'FETCH_INIT' }); - await patchCasesStatus(cases); + const patchResponse = await patchCasesStatus(cases, abortCtrl.signal); if (!cancel) { + const resultCount = Object.keys(patchResponse).length; + const firstTitle = patchResponse[0].title; + dispatch({ type: 'FETCH_SUCCESS', payload: true }); + const messageArgs = { + totalCases: resultCount, + caseTitle: resultCount === 1 ? firstTitle : '', + }; + const message = + resultCount && patchResponse[0].status === 'open' + ? i18n.REOPENED_CASES(messageArgs) + : i18n.CLOSED_CASES(messageArgs); + + displaySuccessToast(message, dispatchToaster); } } catch (error) { if (!cancel) { @@ -87,6 +102,7 @@ export const useUpdateCases = (): UseUpdateCase => { patchData(); return () => { cancel = true; + abortCtrl.abort(); }; }, []); diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_delete_cases.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_delete_cases.tsx index d5a3b3cf9314c..07e3786758aeb 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_delete_cases.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_delete_cases.tsx @@ -5,9 +5,10 @@ */ import { useCallback, useReducer } from 'react'; -import { errorToToaster, useStateToaster } from '../../components/toasters'; +import { displaySuccessToast, errorToToaster, useStateToaster } from '../../components/toasters'; import * as i18n from './translations'; import { deleteCases } from './api'; +import { DeleteCase } from './types'; interface DeleteState { isDisplayConfirmDeleteModal: boolean; @@ -57,9 +58,10 @@ const dataFetchReducer = (state: DeleteState, action: Action): DeleteState => { return state; } }; + interface UseDeleteCase extends DeleteState { dispatchResetIsDeleted: () => void; - handleOnDeleteConfirm: (caseIds: string[]) => void; + handleOnDeleteConfirm: (caseIds: DeleteCase[]) => void; handleToggleModal: () => void; } @@ -72,19 +74,26 @@ export const useDeleteCases = (): UseDeleteCase => { }); const [, dispatchToaster] = useStateToaster(); - const dispatchDeleteCases = useCallback((caseIds: string[]) => { + const dispatchDeleteCases = useCallback((cases: DeleteCase[]) => { let cancel = false; + const abortCtrl = new AbortController(); + const deleteData = async () => { try { dispatch({ type: 'FETCH_INIT' }); - await deleteCases(caseIds); + const caseIds = cases.map(theCase => theCase.id); + await deleteCases(caseIds, abortCtrl.signal); if (!cancel) { dispatch({ type: 'FETCH_SUCCESS', payload: true }); + displaySuccessToast( + i18n.DELETED_CASES(cases.length, cases.length === 1 ? cases[0].title : ''), + dispatchToaster + ); } } catch (error) { if (!cancel) { errorToToaster({ - title: i18n.ERROR_TITLE, + title: i18n.ERROR_DELETING, error: error.body && error.body.message ? new Error(error.body.message) : error, dispatchToaster, }); @@ -94,6 +103,7 @@ export const useDeleteCases = (): UseDeleteCase => { }; deleteData(); return () => { + abortCtrl.abort(); cancel = true; }; }, []); diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx index 02b41c9fc720f..835fb7153dc95 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useEffect, useReducer } from 'react'; +import { useEffect, useReducer, useCallback } from 'react'; import { Case } from './types'; import * as i18n from './translations'; @@ -20,7 +20,8 @@ interface CaseState { type Action = | { type: 'FETCH_INIT' } | { type: 'FETCH_SUCCESS'; payload: Case } - | { type: 'FETCH_FAILURE' }; + | { type: 'FETCH_FAILURE' } + | { type: 'UPDATE_CASE'; payload: Case }; const dataFetchReducer = (state: CaseState, action: Action): CaseState => { switch (action.type) { @@ -43,6 +44,11 @@ const dataFetchReducer = (state: CaseState, action: Action): CaseState => { isLoading: false, isError: true, }; + case 'UPDATE_CASE': + return { + ...state, + data: action.payload, + }; default: return state; } @@ -67,7 +73,12 @@ const initialData: Case = { version: '', }; -export const useGetCase = (caseId: string): CaseState => { +interface UseGetCase extends CaseState { + fetchCase: () => void; + updateCase: (newCase: Case) => void; +} + +export const useGetCase = (caseId: string): UseGetCase => { const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: true, isError: false, @@ -75,12 +86,18 @@ export const useGetCase = (caseId: string): CaseState => { }); const [, dispatchToaster] = useStateToaster(); - const callFetch = () => { + const updateCase = useCallback((newCase: Case) => { + dispatch({ type: 'UPDATE_CASE', payload: newCase }); + }, []); + + const callFetch = useCallback(async () => { let didCancel = false; + const abortCtrl = new AbortController(); + const fetchData = async () => { dispatch({ type: 'FETCH_INIT' }); try { - const response = await getCase(caseId); + const response = await getCase(caseId, true, abortCtrl.signal); if (!didCancel) { dispatch({ type: 'FETCH_SUCCESS', payload: response }); } @@ -98,11 +115,12 @@ export const useGetCase = (caseId: string): CaseState => { fetchData(); return () => { didCancel = true; + abortCtrl.abort(); }; - }; + }, [caseId]); useEffect(() => { callFetch(); }, [caseId]); - return state; + return { ...state, fetchCase: callFetch, updateCase }; }; diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx index ae7b8f3c043fa..323dc23e1b24e 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx @@ -149,12 +149,15 @@ export const useGetCases = (initialQueryParams?: QueryParams): UseGetCases => { const fetchCases = useCallback((filterOptions: FilterOptions, queryParams: QueryParams) => { let didCancel = false; + const abortCtrl = new AbortController(); + const fetchData = async () => { dispatch({ type: 'FETCH_INIT', payload: 'cases' }); try { const response = await getCases({ filterOptions, queryParams, + signal: abortCtrl.signal, }); if (!didCancel) { dispatch({ @@ -175,6 +178,7 @@ export const useGetCases = (initialQueryParams?: QueryParams): UseGetCases => { }; fetchData(); return () => { + abortCtrl.abort(); didCancel = true; }; }, []); @@ -187,13 +191,17 @@ export const useGetCases = (initialQueryParams?: QueryParams): UseGetCases => { const dispatchUpdateCaseProperty = useCallback( ({ updateKey, updateValue, caseId, refetchCasesStatus, version }: UpdateCase) => { let didCancel = false; + const abortCtrl = new AbortController(); + const fetchData = async () => { dispatch({ type: 'FETCH_INIT', payload: 'caseUpdate' }); try { await patchCase( caseId, { [updateKey]: updateValue }, - version ?? '' // saved object versions are typed as string | undefined, hope that's not true + // saved object versions are typed as string | undefined, hope that's not true + version ?? '', + abortCtrl.signal ); if (!didCancel) { dispatch({ type: 'FETCH_UPDATE_CASE_SUCCESS' }); @@ -209,6 +217,7 @@ export const useGetCases = (initialQueryParams?: QueryParams): UseGetCases => { }; fetchData(); return () => { + abortCtrl.abort(); didCancel = true; }; }, diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_get_reporters.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_get_reporters.tsx index 6974000414a06..2478172a3394b 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_get_reporters.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_get_reporters.tsx @@ -6,6 +6,7 @@ import { useCallback, useEffect, useState } from 'react'; +import { isEmpty } from 'lodash/fp'; import { User } from '../../../../../../plugins/case/common/api'; import { errorToToaster, useStateToaster } from '../../components/toasters'; import { getReporters } from './api'; @@ -44,9 +45,12 @@ export const useGetReporters = (): UseGetReporters => { }); try { const response = await getReporters(abortCtrl.signal); + const myReporters = response + .map(r => (r.full_name == null || isEmpty(r.full_name) ? r.username ?? '' : r.full_name)) + .filter(u => !isEmpty(u)); if (!didCancel) { setReporterState({ - reporters: response.map(r => r.full_name ?? r.username ?? 'N/A'), + reporters: myReporters, respReporters: response, isLoading: false, isError: false, diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_get_tags.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_get_tags.tsx index e3657f5b09da9..b41d5aab5c07a 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_get_tags.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_get_tags.tsx @@ -57,10 +57,12 @@ export const useGetTags = (): TagsState => { useEffect(() => { let didCancel = false; + const abortCtrl = new AbortController(); + const fetchData = async () => { dispatch({ type: 'FETCH_INIT' }); try { - const response = await getTags(); + const response = await getTags(abortCtrl.signal); if (!didCancel) { dispatch({ type: 'FETCH_SUCCESS', payload: response }); } @@ -77,6 +79,7 @@ export const useGetTags = (): TagsState => { }; fetchData(); return () => { + abortCtrl.abort(); didCancel = true; }; }, []); diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_post_case.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_post_case.tsx index 817101cf5e663..0e01364721dc5 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_post_case.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_post_case.tsx @@ -6,7 +6,7 @@ import { useReducer, useCallback } from 'react'; -import { CaseRequest } from '../../../../../../plugins/case/common/api'; +import { CasePostRequest } from '../../../../../../plugins/case/common/api'; import { errorToToaster, useStateToaster } from '../../components/toasters'; import { postCase } from './api'; import * as i18n from './translations'; @@ -49,7 +49,7 @@ const dataFetchReducer = (state: NewCaseState, action: Action): NewCaseState => }; interface UsePostCase extends NewCaseState { - postCase: (data: CaseRequest) => void; + postCase: (data: CasePostRequest) => void; } export const usePostCase = (): UsePostCase => { const [state, dispatch] = useReducer(dataFetchReducer, { @@ -59,11 +59,13 @@ export const usePostCase = (): UsePostCase => { }); const [, dispatchToaster] = useStateToaster(); - const postMyCase = useCallback(async (data: CaseRequest) => { + const postMyCase = useCallback(async (data: CasePostRequest) => { let cancel = false; + const abortCtrl = new AbortController(); + try { dispatch({ type: 'FETCH_INIT' }); - const response = await postCase({ ...data, status: 'open' }); + const response = await postCase(data, abortCtrl.signal); if (!cancel) { dispatch({ type: 'FETCH_SUCCESS', @@ -81,6 +83,7 @@ export const usePostCase = (): UsePostCase => { } } return () => { + abortCtrl.abort(); cancel = true; }; }, []); diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_post_comment.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_post_comment.tsx index a96cb97d7cc7b..207b05814717f 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_post_comment.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_post_comment.tsx @@ -11,43 +11,28 @@ import { errorToToaster, useStateToaster } from '../../components/toasters'; import { postComment } from './api'; import * as i18n from './translations'; -import { Comment } from './types'; +import { Case } from './types'; interface NewCommentState { - commentData: Comment | null; isLoading: boolean; isError: boolean; - caseId: string; } -type Action = - | { type: 'RESET_COMMENT_DATA' } - | { type: 'FETCH_INIT' } - | { type: 'FETCH_SUCCESS'; payload: Comment } - | { type: 'FETCH_FAILURE' }; +type Action = { type: 'FETCH_INIT' } | { type: 'FETCH_SUCCESS' } | { type: 'FETCH_FAILURE' }; const dataFetchReducer = (state: NewCommentState, action: Action): NewCommentState => { switch (action.type) { - case 'RESET_COMMENT_DATA': - return { - ...state, - commentData: null, - }; case 'FETCH_INIT': return { - ...state, isLoading: true, isError: false, }; case 'FETCH_SUCCESS': return { - ...state, isLoading: false, isError: false, - commentData: action.payload ?? null, }; case 'FETCH_FAILURE': return { - ...state, isLoading: false, isError: true, }; @@ -57,43 +42,45 @@ const dataFetchReducer = (state: NewCommentState, action: Action): NewCommentSta }; interface UsePostComment extends NewCommentState { - postComment: (data: CommentRequest) => void; - resetCommentData: () => void; + postComment: (data: CommentRequest, updateCase: (newCase: Case) => void) => void; } export const usePostComment = (caseId: string): UsePostComment => { const [state, dispatch] = useReducer(dataFetchReducer, { - commentData: null, isLoading: false, isError: false, - caseId, }); const [, dispatchToaster] = useStateToaster(); - const postMyComment = useCallback(async (data: CommentRequest) => { - let cancel = false; - try { - dispatch({ type: 'FETCH_INIT' }); - const response = await postComment(data, state.caseId); - if (!cancel) { - dispatch({ type: 'FETCH_SUCCESS', payload: response }); - } - } catch (error) { - if (!cancel) { - errorToToaster({ - title: i18n.ERROR_TITLE, - error: error.body && error.body.message ? new Error(error.body.message) : error, - dispatchToaster, - }); - dispatch({ type: 'FETCH_FAILURE' }); - } - } - return () => { - cancel = true; - }; - }, []); + const postMyComment = useCallback( + async (data: CommentRequest, updateCase: (newCase: Case) => void) => { + let cancel = false; + const abortCtrl = new AbortController(); - const resetCommentData = useCallback(() => dispatch({ type: 'RESET_COMMENT_DATA' }), []); + try { + dispatch({ type: 'FETCH_INIT' }); + const response = await postComment(data, caseId, abortCtrl.signal); + if (!cancel) { + dispatch({ type: 'FETCH_SUCCESS' }); + updateCase(response); + } + } catch (error) { + if (!cancel) { + errorToToaster({ + title: i18n.ERROR_TITLE, + error: error.body && error.body.message ? new Error(error.body.message) : error, + dispatchToaster, + }); + dispatch({ type: 'FETCH_FAILURE' }); + } + } + return () => { + abortCtrl.abort(); + cancel = true; + }; + }, + [caseId] + ); - return { ...state, postComment: postMyComment, resetCommentData }; + return { ...state, postComment: postMyComment }; }; diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_post_push_to_service.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_post_push_to_service.tsx index b6fb15f4fa083..d9a32f26f7fe7 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_post_push_to_service.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_post_push_to_service.tsx @@ -87,7 +87,7 @@ export const usePostPushToService = (): UsePostPushToService => { const abortCtrl = new AbortController(); try { dispatch({ type: 'FETCH_INIT' }); - const casePushData = await getCase(caseId); + const casePushData = await getCase(caseId, true, abortCtrl.signal); const responseService = await pushToService( connectorId, formatServiceRequestData(casePushData), @@ -143,31 +143,42 @@ const formatServiceRequestData = (myCase: Case): ServiceConnectorCaseParams => { updatedAt, updatedBy, } = myCase; - return { caseId, createdAt, createdBy: { fullName: createdBy.fullName ?? null, - username: createdBy?.username, + username: createdBy?.username ?? '', }, - comments: comments.map(c => ({ - commentId: c.id, - comment: c.comment, - createdAt: c.createdAt, - createdBy: { - fullName: c.createdBy.fullName ?? null, - username: c.createdBy.username, - }, - updatedAt: c.updatedAt, - updatedBy: - c.updatedBy != null - ? { - fullName: c.updatedBy.fullName ?? null, - username: c.updatedBy.username, - } - : null, - })), + comments: comments + .filter(c => { + const lastPush = c.pushedAt != null ? new Date(c.pushedAt) : null; + const lastUpdate = c.updatedAt != null ? new Date(c.updatedAt) : null; + if ( + lastPush === null || + (lastPush != null && lastUpdate != null && lastPush.getTime() < lastUpdate?.getTime()) + ) { + return true; + } + return false; + }) + .map(c => ({ + commentId: c.id, + comment: c.comment, + createdAt: c.createdAt, + createdBy: { + fullName: c.createdBy.fullName ?? null, + username: c.createdBy.username ?? '', + }, + updatedAt: c.updatedAt, + updatedBy: + c.updatedBy != null + ? { + fullName: c.updatedBy.fullName ?? null, + username: c.updatedBy.username ?? '', + } + : null, + })), description, incidentId: externalService?.externalId ?? null, title, @@ -176,7 +187,7 @@ const formatServiceRequestData = (myCase: Case): ServiceConnectorCaseParams => { updatedBy != null ? { fullName: updatedBy.fullName ?? null, - username: updatedBy.username, + username: updatedBy.username ?? '', } : null, }; diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_update_case.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_update_case.tsx index f8af088f7e03b..4973deef4d91a 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_update_case.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_update_case.tsx @@ -5,18 +5,16 @@ */ import { useReducer, useCallback } from 'react'; -import { cloneDeep } from 'lodash/fp'; -import { CaseRequest } from '../../../../../../plugins/case/common/api'; -import { errorToToaster, useStateToaster } from '../../components/toasters'; +import { displaySuccessToast, errorToToaster, useStateToaster } from '../../components/toasters'; +import { CasePatchRequest } from '../../../../../../plugins/case/common/api'; import { patchCase } from './api'; import * as i18n from './translations'; import { Case } from './types'; -type UpdateKey = keyof CaseRequest; +type UpdateKey = keyof Pick; interface NewCaseState { - caseData: Case; isLoading: boolean; isError: boolean; updateKey: UpdateKey | null; @@ -24,13 +22,15 @@ interface NewCaseState { export interface UpdateByKey { updateKey: UpdateKey; - updateValue: CaseRequest[UpdateKey]; + updateValue: CasePatchRequest[UpdateKey]; fetchCaseUserActions?: (caseId: string) => void; + updateCase?: (newCase: Case) => void; + version: string; } type Action = | { type: 'FETCH_INIT'; payload: UpdateKey } - | { type: 'FETCH_SUCCESS'; payload: Case } + | { type: 'FETCH_SUCCESS' } | { type: 'FETCH_FAILURE' }; const dataFetchReducer = (state: NewCaseState, action: Action): NewCaseState => { @@ -48,7 +48,6 @@ const dataFetchReducer = (state: NewCaseState, action: Action): NewCaseState => ...state, isLoading: false, isError: false, - caseData: cloneDeep(action.payload), updateKey: null, }; case 'FETCH_FAILURE': @@ -65,36 +64,37 @@ const dataFetchReducer = (state: NewCaseState, action: Action): NewCaseState => interface UseUpdateCase extends NewCaseState { updateCaseProperty: (updates: UpdateByKey) => void; - updateCase: (newCase: Case) => void; } -export const useUpdateCase = (caseId: string, initialData: Case): UseUpdateCase => { +export const useUpdateCase = ({ caseId }: { caseId: string }): UseUpdateCase => { const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: false, isError: false, - caseData: initialData, updateKey: null, }); const [, dispatchToaster] = useStateToaster(); - const updateCase = useCallback((newCase: Case) => { - dispatch({ type: 'FETCH_SUCCESS', payload: newCase }); - }, []); - const dispatchUpdateCaseProperty = useCallback( - async ({ fetchCaseUserActions, updateKey, updateValue }: UpdateByKey) => { + async ({ fetchCaseUserActions, updateKey, updateValue, updateCase, version }: UpdateByKey) => { let cancel = false; + const abortCtrl = new AbortController(); + try { dispatch({ type: 'FETCH_INIT', payload: updateKey }); const response = await patchCase( caseId, { [updateKey]: updateValue }, - state.caseData.version + version, + abortCtrl.signal ); if (!cancel) { if (fetchCaseUserActions != null) { fetchCaseUserActions(caseId); } - dispatch({ type: 'FETCH_SUCCESS', payload: response[0] }); + if (updateCase != null) { + updateCase(response[0]); + } + dispatch({ type: 'FETCH_SUCCESS' }); + displaySuccessToast(i18n.UPDATED_CASE(response[0].title), dispatchToaster); } } catch (error) { if (!cancel) { @@ -108,10 +108,11 @@ export const useUpdateCase = (caseId: string, initialData: Case): UseUpdateCase } return () => { cancel = true; + abortCtrl.abort(); }; }, - [state] + [] ); - return { ...state, updateCase, updateCaseProperty: dispatchUpdateCaseProperty }; + return { ...state, updateCaseProperty: dispatchUpdateCaseProperty }; }; diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_update_comment.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_update_comment.tsx index c1b2bfde30126..faf9649a705c5 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_update_comment.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_update_comment.tsx @@ -4,38 +4,29 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useReducer, useCallback, Dispatch } from 'react'; +import { useReducer, useCallback } from 'react'; import { errorToToaster, useStateToaster } from '../../components/toasters'; import { patchComment } from './api'; import * as i18n from './translations'; -import { Comment } from './types'; +import { Case } from './types'; interface CommentUpdateState { - comments: Comment[]; isLoadingIds: string[]; isError: boolean; } - interface CommentUpdate { - update: Partial; commentId: string; } type Action = - | { type: 'APPEND_COMMENT'; payload: Comment } | { type: 'FETCH_INIT'; payload: string } | { type: 'FETCH_SUCCESS'; payload: CommentUpdate } | { type: 'FETCH_FAILURE'; payload: string }; const dataFetchReducer = (state: CommentUpdateState, action: Action): CommentUpdateState => { switch (action.type) { - case 'APPEND_COMMENT': - return { - ...state, - comments: [...state.comments, action.payload], - }; case 'FETCH_INIT': return { ...state, @@ -44,20 +35,10 @@ const dataFetchReducer = (state: CommentUpdateState, action: Action): CommentUpd }; case 'FETCH_SUCCESS': - const updatePayload = action.payload; - const foundIndex = state.comments.findIndex( - comment => comment.id === updatePayload.commentId - ); - const newComments = state.comments; - if (foundIndex !== -1) { - newComments[foundIndex] = { ...state.comments[foundIndex], ...updatePayload.update }; - } - return { ...state, - isLoadingIds: state.isLoadingIds.filter(id => updatePayload.commentId !== id), + isLoadingIds: state.isLoadingIds.filter(id => action.payload.commentId !== id), isError: false, - comments: newComments, }; case 'FETCH_FAILURE': return { @@ -75,38 +56,45 @@ interface UpdateComment { commentId: string; commentUpdate: string; fetchUserActions: () => void; + updateCase: (newCase: Case) => void; + version: string; } interface UseUpdateComment extends CommentUpdateState { - updateComment: ({ caseId, commentId, commentUpdate, fetchUserActions }: UpdateComment) => void; - addPostedComment: Dispatch; + patchComment: ({ caseId, commentId, commentUpdate, fetchUserActions }: UpdateComment) => void; } -export const useUpdateComment = (comments: Comment[]): UseUpdateComment => { +export const useUpdateComment = (): UseUpdateComment => { const [state, dispatch] = useReducer(dataFetchReducer, { isLoadingIds: [], isError: false, - comments, }); const [, dispatchToaster] = useStateToaster(); const dispatchUpdateComment = useCallback( - async ({ caseId, commentId, commentUpdate, fetchUserActions }: UpdateComment) => { + async ({ + caseId, + commentId, + commentUpdate, + fetchUserActions, + updateCase, + version, + }: UpdateComment) => { let cancel = false; + const abortCtrl = new AbortController(); try { dispatch({ type: 'FETCH_INIT', payload: commentId }); - const currentComment = state.comments.find(comment => comment.id === commentId) ?? { - version: '', - }; const response = await patchComment( caseId, commentId, commentUpdate, - currentComment.version + version, + abortCtrl.signal ); if (!cancel) { + updateCase(response); fetchUserActions(); - dispatch({ type: 'FETCH_SUCCESS', payload: { update: response, commentId } }); + dispatch({ type: 'FETCH_SUCCESS', payload: { commentId } }); } } catch (error) { if (!cancel) { @@ -120,14 +108,11 @@ export const useUpdateComment = (comments: Comment[]): UseUpdateComment => { } return () => { cancel = true; + abortCtrl.abort(); }; }, - [state] - ); - const addPostedComment = useCallback( - (comment: Comment) => dispatch({ type: 'APPEND_COMMENT', payload: comment }), [] ); - return { ...state, updateComment: dispatchUpdateComment, addPostedComment }; + return { ...state, patchComment: dispatchUpdateComment }; }; diff --git a/x-pack/legacy/plugins/siem/public/containers/case/utils.ts b/x-pack/legacy/plugins/siem/public/containers/case/utils.ts index ce23ac6c440b6..1ec98bf5b5f1f 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/utils.ts +++ b/x-pack/legacy/plugins/siem/public/containers/case/utils.ts @@ -19,8 +19,6 @@ import { CasesStatusResponseRt, CasesStatusResponse, throwErrors, - CommentResponse, - CommentResponseRt, CasesConfigureResponse, CaseConfigureResponseRt, CaseUserActionsResponse, @@ -82,9 +80,6 @@ export const decodeCasesResponse = (respCase?: CasesResponse) => export const decodeCasesFindResponse = (respCases?: CasesFindResponse) => pipe(CasesFindResponseRt.decode(respCases), fold(throwErrors(createToasterPlainError), identity)); -export const decodeCommentResponse = (respComment?: CommentResponse) => - pipe(CommentResponseRt.decode(respComment), fold(throwErrors(createToasterPlainError), identity)); - export const decodeCaseConfigureResponse = (respCase?: CasesConfigureResponse) => pipe( CaseConfigureResponseRt.decode(respCase), diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts index 4b0e0030be53d..2dd6955581eff 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts @@ -15,10 +15,10 @@ import { Rule, FetchRuleProps, BasicFetchProps, - ImportRulesProps, + ImportDataProps, ExportDocumentsProps, RuleStatusResponse, - ImportRulesResponse, + ImportDataResponse, PrePackagedRulesStatusResponse, BulkRuleResponse, } from './types'; @@ -204,11 +204,11 @@ export const importRules = async ({ fileToImport, overwrite = false, signal, -}: ImportRulesProps): Promise => { +}: ImportDataProps): Promise => { const formData = new FormData(); formData.append('file', fileToImport); - return KibanaServices.get().http.fetch( + return KibanaServices.get().http.fetch( `${DETECTION_ENGINE_RULES_URL}/_import`, { method: 'POST', diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts index 53a1c0770028c..bc559c5ac4972 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts @@ -6,12 +6,7 @@ import * as t from 'io-ts'; -export const RuleTypeSchema = t.keyof({ - query: null, - saved_query: null, - machine_learning: null, -}); -export type RuleType = t.TypeOf; +import { RuleTypeSchema } from '../../../../common/detection_engine/types'; /** * Params is an "record", since it is a type of AlertActionParams which is action templates. @@ -194,7 +189,7 @@ export interface BasicFetchProps { signal: AbortSignal; } -export interface ImportRulesProps { +export interface ImportDataProps { fileToImport: File; overwrite?: boolean; signal: AbortSignal; @@ -208,7 +203,7 @@ export interface ImportRulesResponseError { }; } -export interface ImportRulesResponse { +export interface ImportDataResponse { success: boolean; success_count: number; errors: ImportRulesResponseError[]; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx index 7269bf1baa5e5..0a30329baf68d 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx @@ -12,7 +12,7 @@ import { ReturnRulesStatuses, } from './use_rule_status'; import * as api from './api'; -import { RuleType, Rule } from '../rules/types'; +import { Rule } from '../rules/types'; jest.mock('./api'); @@ -57,7 +57,7 @@ const testRule: Rule = { threat: [], throttle: null, to: 'now', - type: 'query' as RuleType, + type: 'query', updated_at: 'mm/dd/yyyyTHH:MM:sssz', updated_by: 'mockUser', }; diff --git a/x-pack/legacy/plugins/siem/public/containers/timeline/all/api.ts b/x-pack/legacy/plugins/siem/public/containers/timeline/all/api.ts index 0479851fc5b55..4c8e2384de585 100644 --- a/x-pack/legacy/plugins/siem/public/containers/timeline/all/api.ts +++ b/x-pack/legacy/plugins/siem/public/containers/timeline/all/api.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ImportRulesProps, ImportRulesResponse } from '../../detection_engine/rules'; +import { ImportDataProps, ImportDataResponse } from '../../detection_engine/rules'; import { KibanaServices } from '../../../lib/kibana'; import { TIMELINE_IMPORT_URL, TIMELINE_EXPORT_URL } from '../../../../common/constants'; import { ExportSelectedData } from '../../../components/generic_downloader'; @@ -13,11 +13,11 @@ export const importTimelines = async ({ fileToImport, overwrite = false, signal, -}: ImportRulesProps): Promise => { +}: ImportDataProps): Promise => { const formData = new FormData(); formData.append('file', fileToImport); - return KibanaServices.get().http.fetch(`${TIMELINE_IMPORT_URL}`, { + return KibanaServices.get().http.fetch(`${TIMELINE_IMPORT_URL}`, { method: 'POST', headers: { 'Content-Type': undefined }, query: { overwrite }, diff --git a/x-pack/legacy/plugins/siem/public/containers/timeline/index.gql_query.ts b/x-pack/legacy/plugins/siem/public/containers/timeline/index.gql_query.ts index c54238c5d8687..53d0b98570bcb 100644 --- a/x-pack/legacy/plugins/siem/public/containers/timeline/index.gql_query.ts +++ b/x-pack/legacy/plugins/siem/public/containers/timeline/index.gql_query.ts @@ -206,6 +206,7 @@ export const timelineQuery = gql` query to filters + note } } suricata { diff --git a/x-pack/legacy/plugins/siem/public/graphql/introspection.json b/x-pack/legacy/plugins/siem/public/graphql/introspection.json index 5d43024625d0d..2a9dd8f2aacfe 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/introspection.json +++ b/x-pack/legacy/plugins/siem/public/graphql/introspection.json @@ -4696,6 +4696,14 @@ "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "note", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, diff --git a/x-pack/legacy/plugins/siem/public/graphql/types.ts b/x-pack/legacy/plugins/siem/public/graphql/types.ts index a5d1e3fbcba27..e15c099a007ad 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/public/graphql/types.ts @@ -1012,6 +1012,8 @@ export interface RuleField { updated_by?: Maybe; version?: Maybe; + + note?: Maybe; } export interface SuricataEcsFields { @@ -4660,6 +4662,8 @@ export namespace GetTimelineQuery { to: Maybe; filters: Maybe; + + note: Maybe; }; export type Suricata = { diff --git a/x-pack/legacy/plugins/siem/public/lib/connectors/servicenow.tsx b/x-pack/legacy/plugins/siem/public/lib/connectors/servicenow.tsx index 8e947fbc0f9bb..536798ffad41b 100644 --- a/x-pack/legacy/plugins/siem/public/lib/connectors/servicenow.tsx +++ b/x-pack/legacy/plugins/siem/public/lib/connectors/servicenow.tsx @@ -3,7 +3,7 @@ * 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, { useCallback, ChangeEvent } from 'react'; +import React, { useCallback, ChangeEvent, useEffect } from 'react'; import { EuiFieldText, EuiFlexGroup, @@ -98,6 +98,21 @@ const ServiceNowConnectorFields: React.FunctionComponent 0 && username != null; const isPasswordInvalid: boolean = errors.password.length > 0 && password != null; + /** + * We need to distinguish between the add flyout and the edit flyout. + * useEffect will run only once on component mount. + * This guarantees that the function below will run only once. + * On the first render of the component the apiUrl can be either undefined or filled. + * If it is filled then we are on the edit flyout. Otherwise we are on the add flyout. + */ + + useEffect(() => { + if (!isEmpty(apiUrl)) { + editActionSecrets('username', ''); + editActionSecrets('password', ''); + } + }, []); + if (isEmpty(mapping)) { editActionConfig('casesConfiguration', { ...action.config.casesConfiguration, @@ -166,10 +181,11 @@ const ServiceNowConnectorFields: React.FunctionComponent + + + - diff --git a/x-pack/legacy/plugins/siem/public/lib/kibana/hooks.ts b/x-pack/legacy/plugins/siem/public/lib/kibana/hooks.ts index 95ecee7b12bb1..775a7d7c0acca 100644 --- a/x-pack/legacy/plugins/siem/public/lib/kibana/hooks.ts +++ b/x-pack/legacy/plugins/siem/public/lib/kibana/hooks.ts @@ -53,9 +53,24 @@ export const useCurrentUser = (): AuthenticatedElasticUser | null => { let didCancel = false; const fetchData = async () => { try { - const response = await security.authc.getCurrentUser(); - if (!didCancel) { - setUser(convertToCamelCase(response)); + if (security != null) { + const response = await security.authc.getCurrentUser(); + if (!didCancel) { + setUser(convertToCamelCase(response)); + } + } else { + setUser({ + username: i18n.translate('xpack.siem.getCurrentUser.unknownUser', { + defaultMessage: 'Unknown', + }), + email: '', + fullName: '', + roles: [], + enabled: false, + authenticationRealm: { name: '', type: '' }, + lookupRealm: { name: '', type: '' }, + authenticationProvider: '', + }); } } catch (error) { if (!didCancel) { @@ -81,3 +96,29 @@ export const useCurrentUser = (): AuthenticatedElasticUser | null => { }, []); return user; }; + +export interface UseGetUserSavedObjectPermissions { + crud: boolean; + read: boolean; +} + +export const useGetUserSavedObjectPermissions = () => { + const [ + savedObjectsPermissions, + setSavedObjectsPermissions, + ] = useState(null); + const uiCapabilities = useKibana().services.application.capabilities; + + useEffect(() => { + const capabilitiesCanUserCRUD: boolean = + typeof uiCapabilities.siem.crud === 'boolean' ? uiCapabilities.siem.crud : false; + const capabilitiesCanUserRead: boolean = + typeof uiCapabilities.siem.show === 'boolean' ? uiCapabilities.siem.show : false; + setSavedObjectsPermissions({ + crud: capabilitiesCanUserCRUD, + read: capabilitiesCanUserRead, + }); + }, [uiCapabilities]); + + return savedObjectsPermissions; +}; diff --git a/x-pack/legacy/plugins/siem/public/mock/index.ts b/x-pack/legacy/plugins/siem/public/mock/index.ts index dbf5f2e55e713..bdad0ab1712ab 100644 --- a/x-pack/legacy/plugins/siem/public/mock/index.ts +++ b/x-pack/legacy/plugins/siem/public/mock/index.ts @@ -13,3 +13,5 @@ export * from './mock_detail_item'; export * from './netflow'; export * from './test_providers'; export * from './utils'; +export * from './mock_ecs'; +export * from './timeline_results'; diff --git a/x-pack/legacy/plugins/siem/public/mock/mock_ecs.ts b/x-pack/legacy/plugins/siem/public/mock/mock_ecs.ts index 5d32d95804e69..59e26039e6bff 100644 --- a/x-pack/legacy/plugins/siem/public/mock/mock_ecs.ts +++ b/x-pack/legacy/plugins/siem/public/mock/mock_ecs.ts @@ -1280,3 +1280,69 @@ export const mockEcsData: Ecs[] = [ zeek: null, }, ]; + +export const mockEcsDataWithSignal: Ecs = { + _id: '1', + timestamp: '2018-11-05T19:03:25.937Z', + host: { + name: ['apache'], + ip: ['192.168.0.1'], + }, + event: { + id: ['1'], + action: ['Action'], + category: ['Access'], + module: ['nginx'], + severity: [3], + }, + source: { + ip: ['192.168.0.1'], + port: [80], + }, + destination: { + ip: ['192.168.0.3'], + port: [6343], + }, + user: { + id: ['1'], + name: ['john.dee'], + }, + geo: { + region_name: ['xx'], + country_iso_code: ['xx'], + }, + signal: { + rule: { + created_at: ['2020-01-10T21:11:45.839Z'], + updated_at: ['2020-01-10T21:11:45.839Z'], + created_by: ['elastic'], + description: ['24/7'], + enabled: [true], + false_positives: ['test-1'], + filters: [], + from: ['now-300s'], + id: ['b5ba41ab-aaf3-4f43-971b-bdf9434ce0ea'], + immutable: [false], + index: ['auditbeat-*'], + interval: ['5m'], + rule_id: ['rule-id-1'], + language: ['kuery'], + output_index: ['.siem-signals-default'], + max_signals: [100], + risk_score: ['21'], + query: ['user.name: root or user.name: admin'], + references: ['www.test.co'], + saved_id: ["Garrett's IP"], + timeline_id: ['1234-2136-11ea-9864-ebc8cc1cb8c2'], + timeline_title: ['Untitled timeline'], + severity: ['low'], + updated_by: ['elastic'], + tags: [], + to: ['now'], + type: ['saved_query'], + threat: [], + note: ['# this is some markdown documentation'], + version: ['1'], + }, + }, +}; diff --git a/x-pack/legacy/plugins/siem/public/mock/timeline_results.ts b/x-pack/legacy/plugins/siem/public/mock/timeline_results.ts index d6dc0ae131391..363281e563317 100644 --- a/x-pack/legacy/plugins/siem/public/mock/timeline_results.ts +++ b/x-pack/legacy/plugins/siem/public/mock/timeline_results.ts @@ -7,7 +7,10 @@ import { OpenTimelineResult } from '../components/open_timeline/types'; import { GetAllTimeline, SortFieldTimeline, TimelineResult, Direction } from '../graphql/types'; import { allTimelinesQuery } from '../containers/timeline/all/index.gql_query'; - +import { CreateTimelineProps } from '../pages/detection_engine/components/signals/types'; +import { TimelineModel } from '../store/timeline/model'; +import { timelineDefaults } from '../store/timeline/defaults'; +import { FilterStateStore } from '../../../../../../src/plugins/data/common/es_query/filters/meta_filter'; export interface MockedProvidedQuery { request: { query: GetAllTimeline.Query; @@ -2006,3 +2009,196 @@ export const mockTimelineResults: OpenTimelineResult[] = [ updatedBy: 'karen', }, ]; + +export const mockTimelineModel: TimelineModel = { + columns: [ + { + columnHeaderType: 'not-filtered', + id: '@timestamp', + width: 190, + }, + { + columnHeaderType: 'not-filtered', + id: 'message', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'event.category', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'host.name', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'source.ip', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'destination.ip', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'user.name', + width: 180, + }, + ], + dataProviders: [], + dateRange: { + end: 1584539558929, + start: 1584539198929, + }, + deletedEventIds: [], + description: 'This is a sample rule description', + eventIdToNoteIds: {}, + eventType: 'all', + filters: [ + { + $state: { + store: FilterStateStore.APP_STATE, + }, + meta: { + alias: null, + disabled: true, + key: 'host.name', + negate: false, + params: '"{"query":"placeholder"}"', + type: 'phrase', + }, + query: '"{"match_phrase":{"host.name":"placeholder"}}"', + }, + ], + highlightedDropAndProviderId: '', + historyIds: [], + id: 'ef579e40-jibber-jabber', + isFavorite: false, + isLive: false, + isLoading: false, + isSaving: false, + isSelectAllChecked: false, + kqlMode: 'filter', + kqlQuery: { + filterQuery: null, + filterQueryDraft: null, + }, + itemsPerPage: 25, + itemsPerPageOptions: [10, 25, 50, 100], + loadingEventIds: [], + noteIds: [], + pinnedEventIds: {}, + pinnedEventsSaveObject: {}, + savedObjectId: 'ef579e40-jibber-jabber', + selectedEventIds: {}, + show: false, + showCheckboxes: false, + showRowRenderers: true, + sort: { + columnId: '@timestamp', + sortDirection: Direction.desc, + }, + title: 'Test rule', + version: '1', + width: 1100, +}; + +export const mockTimelineResult: TimelineResult = { + savedObjectId: 'ef579e40-jibber-jabber', + columns: timelineDefaults.columns.filter(column => column.id !== 'event.action'), + dateRange: { start: 1584539198929, end: 1584539558929 }, + description: 'This is a sample rule description', + eventType: 'all', + filters: [ + { + meta: { + key: 'host.name', + negate: false, + params: '"{"query":"placeholder"}"', + type: 'phrase', + }, + query: '"{"match_phrase":{"host.name":"placeholder"}}"', + }, + ], + kqlMode: 'filter', + title: 'Test rule', + savedQueryId: null, + sort: { columnId: '@timestamp', sortDirection: 'desc' }, + version: '1', +}; + +export const mockTimelineApolloResult = { + data: { + getOneTimeline: mockTimelineResult, + }, + loading: false, + networkStatus: 7, + stale: false, +}; + +export const defaultTimelineProps: CreateTimelineProps = { + from: 1541444305937, + timeline: { + columns: [ + { columnHeaderType: 'not-filtered', id: '@timestamp', width: 190 }, + { columnHeaderType: 'not-filtered', id: 'message', width: 180 }, + { columnHeaderType: 'not-filtered', id: 'event.category', width: 180 }, + { columnHeaderType: 'not-filtered', id: 'event.action', width: 180 }, + { columnHeaderType: 'not-filtered', id: 'host.name', width: 180 }, + { columnHeaderType: 'not-filtered', id: 'source.ip', width: 180 }, + { columnHeaderType: 'not-filtered', id: 'destination.ip', width: 180 }, + { columnHeaderType: 'not-filtered', id: 'user.name', width: 180 }, + ], + dataProviders: [ + { + and: [], + enabled: true, + excluded: false, + id: + 'send-signal-to-timeline-action-default-draggable-event-details-value-formatted-field-value-timeline-1-signal-id-1', + kqlQuery: '', + name: '1', + queryMatch: { field: '_id', operator: ':', value: '1' }, + }, + ], + dateRange: { end: 1541444605937, start: 1541444305937 }, + deletedEventIds: [], + description: '', + eventIdToNoteIds: {}, + eventType: 'all', + filters: [], + highlightedDropAndProviderId: '', + historyIds: [], + id: 'timeline-1', + isFavorite: false, + isLive: false, + isLoading: false, + isSaving: false, + isSelectAllChecked: false, + itemsPerPage: 25, + itemsPerPageOptions: [10, 25, 50, 100], + kqlMode: 'filter', + kqlQuery: { + filterQuery: { kuery: { expression: '', kind: 'kuery' }, serializedQuery: '' }, + filterQueryDraft: { expression: '', kind: 'kuery' }, + }, + loadingEventIds: [], + noteIds: [], + pinnedEventIds: {}, + pinnedEventsSaveObject: {}, + savedObjectId: null, + selectedEventIds: {}, + show: false, + showCheckboxes: false, + showRowRenderers: true, + sort: { columnId: '@timestamp', sortDirection: Direction.desc }, + title: '', + version: null, + width: 1100, + }, + to: 1541444605937, + ruleNote: '# this is some markdown documentation', +}; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/case.tsx b/x-pack/legacy/plugins/siem/public/pages/case/case.tsx index 9255dee461940..2ae35796387b8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/case.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/case.tsx @@ -7,16 +7,34 @@ import React from 'react'; import { WrapperPage } from '../../components/wrapper_page'; -import { AllCases } from './components/all_cases'; +import { useGetUserSavedObjectPermissions } from '../../lib/kibana'; import { SpyRoute } from '../../utils/route/spy_routes'; +import { AllCases } from './components/all_cases'; + +import { getSavedObjectReadOnly, CaseCallOut } from './components/callout'; +import { CaseSavedObjectNoPermissions } from './saved_object_no_permissions'; + +const infoReadSavedObject = getSavedObjectReadOnly(); + +export const CasesPage = React.memo(() => { + const userPermissions = useGetUserSavedObjectPermissions(); -export const CasesPage = React.memo(() => ( - <> - - - - - -)); + return userPermissions == null || userPermissions?.read ? ( + <> + + {userPermissions != null && !userPermissions?.crud && userPermissions?.read && ( + + )} + + + + + ) : ( + + ); +}); CasesPage.displayName = 'CasesPage'; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/case_details.tsx b/x-pack/legacy/plugins/siem/public/pages/case/case_details.tsx index 890df91c8560e..cbc7bbc62fbf9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/case_details.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/case_details.tsx @@ -5,22 +5,36 @@ */ import React from 'react'; -import { useParams } from 'react-router-dom'; +import { useParams, Redirect } from 'react-router-dom'; -import { CaseView } from './components/case_view'; +import { useGetUrlSearch } from '../../components/navigation/use_get_url_search'; +import { useGetUserSavedObjectPermissions } from '../../lib/kibana'; import { SpyRoute } from '../../utils/route/spy_routes'; +import { getCaseUrl } from '../../components/link_to'; +import { navTabs } from '../home/home_navigations'; +import { CaseView } from './components/case_view'; +import { getSavedObjectReadOnly, CaseCallOut } from './components/callout'; + +const infoReadSavedObject = getSavedObjectReadOnly(); export const CaseDetailsPage = React.memo(() => { + const userPermissions = useGetUserSavedObjectPermissions(); const { detailName: caseId } = useParams(); - if (!caseId) { - return null; + const search = useGetUrlSearch(navTabs.case); + + if (userPermissions != null && !userPermissions.read) { + return ; } - return ( + + return caseId != null ? ( <> - + {userPermissions != null && !userPermissions?.crud && userPermissions?.read && ( + + )} + - ); + ) : null; }); CaseDetailsPage.displayName = 'CaseDetailsPage'; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/add_comment/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/add_comment/index.tsx index 21e4724797c5d..ecc57c50e28eb 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/add_comment/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/add_comment/index.tsx @@ -10,13 +10,14 @@ import styled from 'styled-components'; import { CommentRequest } from '../../../../../../../../plugins/case/common/api'; import { usePostComment } from '../../../../containers/case/use_post_comment'; +import { Case } from '../../../../containers/case/types'; import { MarkdownEditorForm } from '../../../../components/markdown_editor/form'; +import { InsertTimelinePopover } from '../../../../components/timeline/insert_timeline_popover'; +import { useInsertTimeline } from '../../../../components/timeline/insert_timeline_popover/use_insert_timeline'; import { Form, useForm, UseField } from '../../../../shared_imports'; + import * as i18n from '../../translations'; import { schema } from './schema'; -import { InsertTimelinePopover } from '../../../../components/timeline/insert_timeline_popover'; -import { useInsertTimeline } from '../../../../components/timeline/insert_timeline_popover/use_insert_timeline'; -import { Comment } from '../../../../containers/case/types'; const MySpinner = styled(EuiLoadingSpinner)` position: absolute; @@ -30,15 +31,16 @@ const initialCommentValue: CommentRequest = { interface AddCommentProps { caseId: string; + disabled?: boolean; insertQuote: string | null; onCommentSaving?: () => void; - onCommentPosted: (commentResponse: Comment) => void; + onCommentPosted: (newCase: Case) => void; showLoading?: boolean; } export const AddComment = React.memo( - ({ caseId, insertQuote, showLoading = true, onCommentPosted, onCommentSaving }) => { - const { commentData, isLoading, postComment, resetCommentData } = usePostComment(caseId); + ({ caseId, disabled, insertQuote, showLoading = true, onCommentPosted, onCommentSaving }) => { + const { isLoading, postComment } = usePostComment(caseId); const { form } = useForm({ defaultValue: initialCommentValue, options: { stripEmptyFields: false }, @@ -59,23 +61,16 @@ export const AddComment = React.memo( } }, [insertQuote]); - useEffect(() => { - if (commentData !== null) { - onCommentPosted(commentData); - form.reset(); - resetCommentData(); - } - }, [commentData]); - const onSubmit = useCallback(async () => { const { isValid, data } = await form.submit(); if (isValid) { if (onCommentSaving != null) { onCommentSaving(); } - await postComment(data); + await postComment(data, onCommentPosted); + form.reset(); } - }, [form]); + }, [form, onCommentPosted, onCommentSaving]); return ( @@ -93,7 +88,7 @@ export const AddComment = React.memo( bottomRightContent: ( > => [ { - description: i18n.DELETE, + description: i18n.DELETE_CASE, icon: 'trash', - name: i18n.DELETE, + name: i18n.DELETE_CASE, onClick: deleteCaseOnClick, type: 'icon', 'data-test-subj': 'action-delete', diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx index 5ca54c7f429d2..f757fd33a93a8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx @@ -11,7 +11,6 @@ import { EuiTableActionsColumnType, EuiAvatar, EuiLink, - EuiLoadingSpinner, } from '@elastic/eui'; import styled from 'styled-components'; import { DefaultItemIconButtonAction } from '@elastic/eui/src/components/basic_table/action_types'; @@ -21,7 +20,6 @@ import { FormattedRelativePreferenceDate } from '../../../../components/formatte import { CaseDetailsLink } from '../../../../components/links'; import { TruncatableText } from '../../../../components/truncatable_text'; import * as i18n from './translations'; -import { useGetCaseUserActions } from '../../../../containers/case/use_get_case_user_actions'; export type CasesColumns = | EuiTableFieldDataColumnType @@ -48,7 +46,9 @@ export const getCasesColumns = ( render: (theCase: Case) => { if (theCase.id != null && theCase.title != null) { const caseDetailsLinkComponent = ( - {theCase.title} + + {theCase.title} + ); return theCase.status === 'open' ? ( caseDetailsLinkComponent @@ -73,11 +73,11 @@ export const getCasesColumns = ( <> - {createdBy.fullName ?? createdBy.username ?? 'N/A'} + {createdBy.fullName ?? createdBy.username ?? ''} ); @@ -169,25 +169,31 @@ interface Props { } const ServiceNowColumn: React.FC = ({ theCase }) => { - const { hasDataToPush, isLoading } = useGetCaseUserActions(theCase.id); - const handleRenderDataToPush = useCallback( - () => - isLoading ? ( - - ) : ( -

- - {theCase.externalService?.externalTitle} - - {hasDataToPush ? i18n.REQUIRES_UPDATE : i18n.UP_TO_DATE} -

- ), - [hasDataToPush, isLoading, theCase.externalService] - ); + const handleRenderDataToPush = useCallback(() => { + const lastCaseUpdate = theCase.updatedAt != null ? new Date(theCase.updatedAt) : null; + const lastCasePush = + theCase.externalService?.pushedAt != null + ? new Date(theCase.externalService?.pushedAt) + : null; + const hasDataToPush = + lastCasePush === null || + (lastCasePush != null && + lastCaseUpdate != null && + lastCasePush.getTime() < lastCaseUpdate?.getTime()); + return ( +

+ + {theCase.externalService?.externalTitle} + + {hasDataToPush ? i18n.REQUIRES_UPDATE : i18n.UP_TO_DATE} +

+ ); + }, [theCase]); if (theCase.externalService !== null) { return handleRenderDataToPush(); } diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.test.tsx index bdcb87b483851..a6da45a8c5bb1 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.test.tsx @@ -87,7 +87,7 @@ describe('AllCases', () => { it('should render AllCases', () => { const wrapper = mount( - + ); expect( @@ -132,7 +132,7 @@ describe('AllCases', () => { it('should tableHeaderSortButton AllCases', () => { const wrapper = mount( - + ); wrapper @@ -149,7 +149,7 @@ describe('AllCases', () => { it('closes case when row action icon clicked', () => { const wrapper = mount( - + ); wrapper @@ -182,7 +182,7 @@ describe('AllCases', () => { const wrapper = mount( - + ); wrapper @@ -202,7 +202,7 @@ describe('AllCases', () => { .last() .simulate('click'); expect(handleOnDeleteConfirm.mock.calls[0][0]).toStrictEqual( - useGetCasesMockState.data.cases.map(theCase => theCase.id) + useGetCasesMockState.data.cases.map(({ id }) => ({ id })) ); }); it('Bulk close status update', () => { @@ -213,7 +213,7 @@ describe('AllCases', () => { const wrapper = mount( - + ); wrapper @@ -238,7 +238,7 @@ describe('AllCases', () => { const wrapper = mount( - + ); wrapper @@ -259,7 +259,7 @@ describe('AllCases', () => { mount( - + ); expect(refetchCases).toBeCalled(); @@ -274,7 +274,7 @@ describe('AllCases', () => { mount( - + ); expect(refetchCases).toBeCalled(); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx index 27316ab8427cb..161910bb5498a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx @@ -17,11 +17,12 @@ import { EuiTableSortingType, } from '@elastic/eui'; import { EuiTableSelectionType } from '@elastic/eui/src/components/basic_table/table_types'; +import { isEmpty } from 'lodash/fp'; import styled, { css } from 'styled-components'; import * as i18n from './translations'; import { getCasesColumns } from './columns'; -import { Case, FilterOptions, SortFieldCase } from '../../../../containers/case/types'; +import { Case, DeleteCase, FilterOptions, SortFieldCase } from '../../../../containers/case/types'; import { useGetCases, UpdateCase } from '../../../../containers/case/use_get_cases'; import { useGetCasesStatus } from '../../../../containers/case/use_get_cases_status'; import { useDeleteCases } from '../../../../containers/case/use_delete_cases'; @@ -35,7 +36,7 @@ import { UtilityBarSection, UtilityBarText, } from '../../../../components/utility_bar'; -import { getConfigureCasesUrl, getCreateCaseUrl } from '../../../../components/link_to'; +import { getCreateCaseUrl } from '../../../../components/link_to'; import { getBulkItems } from '../bulk_actions'; import { CaseHeaderPage } from '../case_header_page'; import { ConfirmDeleteCaseModal } from '../confirm_delete_case'; @@ -45,6 +46,11 @@ import { navTabs } from '../../../home/home_navigations'; import { getActions } from './actions'; import { CasesTableFilters } from './table_filters'; import { useUpdateCases } from '../../../../containers/case/use_bulk_update_case'; +import { useGetActionLicense } from '../../../../containers/case/use_get_action_license'; +import { getActionLicenseError } from '../use_push_to_service/helpers'; +import { CaseCallOut } from '../callout'; +import { ConfigureCaseButton } from '../configure_cases/button'; +import { ERROR_PUSH_SERVICE_CALLOUT_TITLE } from '../use_push_to_service/translations'; const Div = styled.div` margin-top: ${({ theme }) => theme.eui.paddingSizes.m}; @@ -75,9 +81,13 @@ const getSortField = (field: string): SortFieldCase => { } return SortFieldCase.createdAt; }; -export const AllCases = React.memo(() => { - const urlSearch = useGetUrlSearch(navTabs.case); +interface AllCasesProps { + userCanCrud: boolean; +} +export const AllCases = React.memo(({ userCanCrud }) => { + const urlSearch = useGetUrlSearch(navTabs.case); + const { actionLicense } = useGetActionLicense(); const { countClosedCases, countOpenCases, @@ -107,11 +117,24 @@ export const AllCases = React.memo(() => { isDisplayConfirmDeleteModal, } = useDeleteCases(); - const { dispatchResetIsUpdated, isUpdated, updateBulkStatus } = useUpdateCases(); + // Update case + const { + dispatchResetIsUpdated, + isLoading: isUpdating, + isUpdated, + updateBulkStatus, + } = useUpdateCases(); + const [deleteThisCase, setDeleteThisCase] = useState({ + title: '', + id: '', + }); + const [deleteBulk, setDeleteBulk] = useState([]); const refreshCases = useCallback(() => { refetchCases(filterOptions, queryParams); fetchCasesStatus(); + setSelectedCases([]); + setDeleteBulk([]); }, [filterOptions, queryParams]); useEffect(() => { @@ -124,11 +147,6 @@ export const AllCases = React.memo(() => { dispatchResetIsUpdated(); } }, [isDeleted, isUpdated]); - const [deleteThisCase, setDeleteThisCase] = useState({ - title: '', - id: '', - }); - const [deleteBulk, setDeleteBulk] = useState([]); const confirmDeleteModal = useMemo( () => ( { onCancel={handleToggleModal} onConfirm={handleOnDeleteConfirm.bind( null, - deleteBulk.length > 0 ? deleteBulk : [deleteThisCase.id] + deleteBulk.length > 0 ? deleteBulk : [deleteThisCase] )} /> ), @@ -150,10 +168,20 @@ export const AllCases = React.memo(() => { setDeleteThisCase(deleteCase); }, []); - const toggleBulkDeleteModal = useCallback((deleteCases: string[]) => { - handleToggleModal(); - setDeleteBulk(deleteCases); - }, []); + const toggleBulkDeleteModal = useCallback( + (caseIds: string[]) => { + handleToggleModal(); + if (caseIds.length === 1) { + const singleCase = selectedCases.find(theCase => theCase.id === caseIds[0]); + if (singleCase) { + return setDeleteThisCase({ id: singleCase.id, title: singleCase.title }); + } + } + const convertToDeleteCases: DeleteCase[] = caseIds.map(id => ({ id })); + setDeleteBulk(convertToDeleteCases); + }, + [selectedCases] + ); const handleUpdateCaseStatus = useCallback( (status: string) => { @@ -199,6 +227,8 @@ export const AllCases = React.memo(() => { [filterOptions.status, toggleDeleteModal, handleDispatchUpdate] ); + const actionsErrors = useMemo(() => getActionLicenseError(actionLicense), [actionLicense]); + const tableOnChangeCallback = useCallback( ({ page, sort }: EuiBasicTableOnChange) => { let newQueryParams = queryParams; @@ -233,10 +263,10 @@ export const AllCases = React.memo(() => { [filterOptions, queryParams] ); - const memoizedGetCasesColumns = useMemo(() => getCasesColumns(actions, filterOptions.status), [ - actions, - filterOptions.status, - ]); + const memoizedGetCasesColumns = useMemo( + () => getCasesColumns(userCanCrud ? actions : [], filterOptions.status), + [actions, filterOptions.status, userCanCrud] + ); const memoizedPagination = useMemo( () => ({ pageIndex: queryParams.page - 1, @@ -259,8 +289,12 @@ export const AllCases = React.memo(() => { [loading] ); const isDataEmpty = useMemo(() => data.total === 0, [data]); + return ( <> + {!isEmpty(actionsErrors) && ( + + )} @@ -278,18 +312,28 @@ export const AllCases = React.memo(() => { /> - - {i18n.CONFIGURE_CASES_BUTTON} - + } + titleTooltip={!isEmpty(actionsErrors) ? actionsErrors[0].title : ''} + urlSearch={urlSearch} + /> - + {i18n.CREATE_TITLE} - {(isCasesLoading || isDeleting) && !isDataEmpty && ( + {(isCasesLoading || isDeleting || isUpdating) && !isDataEmpty && ( )} @@ -321,15 +365,16 @@ export const AllCases = React.memo(() => { {i18n.SHOWING_SELECTED_CASES(selectedCases.length)} - - {i18n.BULK_ACTIONS} - - + {userCanCrud && ( + + {i18n.BULK_ACTIONS} + + )} {i18n.REFRESH} @@ -339,7 +384,7 @@ export const AllCases = React.memo(() => { { body={i18n.NO_CASES_BODY} actions={ { } onChange={tableOnChangeCallback} pagination={memoizedPagination} - selection={euiBasicTableSelectionProps} + selection={userCanCrud ? euiBasicTableSelectionProps : {}} sorting={sorting} />
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.tsx index a71ad1c45a980..a344dd7891010 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.tsx @@ -43,7 +43,7 @@ const CasesTableFiltersComponent = ({ initial = defaultInitial, }: CasesTableFiltersProps) => { const [selectedReporters, setselectedReporters] = useState( - initial.reporters.map(r => r.full_name ?? r.username) + initial.reporters.map(r => r.full_name ?? r.username ?? '') ); const [search, setSearch] = useState(initial.search); const [selectedTags, setSelectedTags] = useState(initial.tags); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/translations.ts index e8459454576e3..1bee96bc23fff 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/translations.ts @@ -59,9 +59,11 @@ export const CLOSED_CASES = i18n.translate('xpack.siem.case.caseTable.closedCase export const CLOSED = i18n.translate('xpack.siem.case.caseTable.closed', { defaultMessage: 'Closed', }); + export const DELETE = i18n.translate('xpack.siem.case.caseTable.delete', { defaultMessage: 'Delete', }); + export const REQUIRES_UPDATE = i18n.translate('xpack.siem.case.caseTable.requiresUpdate', { defaultMessage: ' requires update', }); @@ -76,3 +78,7 @@ export const NOT_PUSHED = i18n.translate('xpack.siem.case.caseTable.notPushed', export const REFRESH = i18n.translate('xpack.siem.case.caseTable.refreshTitle', { defaultMessage: 'Refresh', }); + +export const SERVICENOW_LINK_ARIA = i18n.translate('xpack.siem.case.caseTable.serviceNowLinkAria', { + defaultMessage: 'click to view the incident on servicenow', +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/callout/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/callout/helpers.tsx new file mode 100644 index 0000000000000..929e8640dceb6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/callout/helpers.tsx @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as i18n from './translations'; + +export const getSavedObjectReadOnly = () => ({ + title: i18n.READ_ONLY_SAVED_OBJECT_TITLE, + description: i18n.READ_ONLY_SAVED_OBJECT_MSG, +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/errors_push_service_callout/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/callout/index.tsx similarity index 59% rename from x-pack/legacy/plugins/siem/public/pages/case/components/errors_push_service_callout/index.tsx rename to x-pack/legacy/plugins/siem/public/pages/case/components/callout/index.tsx index 15b50e4b4cd8d..30a95db2d82a5 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/errors_push_service_callout/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/callout/index.tsx @@ -5,22 +5,28 @@ */ import { EuiCallOut, EuiButton, EuiDescriptionList, EuiSpacer } from '@elastic/eui'; +import { isEmpty } from 'lodash/fp'; import React, { memo, useCallback, useState } from 'react'; import * as i18n from './translations'; -interface ErrorsPushServiceCallOut { - errors: Array<{ title: string; description: JSX.Element }>; +export * from './helpers'; + +interface CaseCallOutProps { + title: string; + message?: string; + messages?: Array<{ title: string; description: JSX.Element }>; } -const ErrorsPushServiceCallOutComponent = ({ errors }: ErrorsPushServiceCallOut) => { +const CaseCallOutComponent = ({ title, message, messages }: CaseCallOutProps) => { const [showCallOut, setShowCallOut] = useState(true); const handleCallOut = useCallback(() => setShowCallOut(false), [setShowCallOut]); return showCallOut ? ( <> - - + + {!isEmpty(messages) && } + {!isEmpty(message) &&

{message}

} {i18n.DISMISS_CALLOUT} @@ -30,4 +36,4 @@ const ErrorsPushServiceCallOutComponent = ({ errors }: ErrorsPushServiceCallOut) ) : null; }; -export const ErrorsPushServiceCallOut = memo(ErrorsPushServiceCallOutComponent); +export const CaseCallOut = memo(CaseCallOutComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/errors_push_service_callout/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/callout/translations.ts similarity index 50% rename from x-pack/legacy/plugins/siem/public/pages/case/components/errors_push_service_callout/translations.ts rename to x-pack/legacy/plugins/siem/public/pages/case/components/callout/translations.ts index 57712e720f6d0..f70225b841162 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/errors_push_service_callout/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/callout/translations.ts @@ -6,10 +6,18 @@ import { i18n } from '@kbn/i18n'; -export const ERROR_PUSH_SERVICE_CALLOUT_TITLE = i18n.translate( - 'xpack.siem.case.errorsPushServiceCallOutTitle', +export const READ_ONLY_SAVED_OBJECT_TITLE = i18n.translate( + 'xpack.siem.case.readOnlySavedObjectTitle', { - defaultMessage: 'To send cases to external systems, you need to:', + defaultMessage: 'You have read-only feature privileges', + } +); + +export const READ_ONLY_SAVED_OBJECT_MSG = i18n.translate( + 'xpack.siem.case.readOnlySavedObjectDescription', + { + defaultMessage: + 'You are only allowed to view cases. If you need to open and update cases, contact your Kibana administrator', } ); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_status/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_status/index.tsx index 0420a71fea907..2b16dfa150d61 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_status/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_status/index.tsx @@ -8,6 +8,7 @@ import React from 'react'; import styled, { css } from 'styled-components'; import { EuiBadge, + EuiButtonEmpty, EuiButtonToggle, EuiDescriptionList, EuiDescriptionListDescription, @@ -18,6 +19,7 @@ import { import * as i18n from '../case_view/translations'; import { FormattedRelativePreferenceDate } from '../../../../components/formatted_date'; import { CaseViewActions } from '../case_view/actions'; +import { Case } from '../../../../containers/case/types'; const MyDescriptionList = styled(EuiDescriptionList)` ${({ theme }) => css` @@ -32,11 +34,12 @@ interface CaseStatusProps { 'data-test-subj': string; badgeColor: string; buttonLabel: string; - caseId: string; - caseTitle: string; + caseData: Case; + disabled?: boolean; icon: string; isLoading: boolean; isSelected: boolean; + onRefresh: () => void; status: string; title: string; toggleStatusCase: (evt: unknown) => void; @@ -46,11 +49,12 @@ const CaseStatusComp: React.FC = ({ 'data-test-subj': dataTestSubj, badgeColor, buttonLabel, - caseId, - caseTitle, + caseData, + disabled = false, icon, isLoading, isSelected, + onRefresh, status, title, toggleStatusCase, @@ -79,9 +83,15 @@ const CaseStatusComp: React.FC = ({
+ + + {i18n.CASE_REFRESH} + + = ({ /> - + diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/__mock__/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/__mock__/index.tsx index 7aadea1a453a7..0e57326707e97 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/__mock__/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/__mock__/index.tsx @@ -7,9 +7,13 @@ import { CaseProps } from '../index'; import { Case } from '../../../../../containers/case/types'; +const updateCase = jest.fn(); +const fetchCase = jest.fn(); + export const caseProps: CaseProps = { caseId: '3c4ddcc0-4e99-11ea-9290-35d05cb55c15', - initialData: { + userCanCrud: true, + caseData: { closedAt: null, closedBy: null, id: '3c4ddcc0-4e99-11ea-9290-35d05cb55c15', @@ -46,12 +50,14 @@ export const caseProps: CaseProps = { }, version: 'WzQ3LDFd', }, + fetchCase, + updateCase, }; export const caseClosedProps: CaseProps = { ...caseProps, - initialData: { - ...caseProps.initialData, + caseData: { + ...caseProps.caseData, closedAt: '2020-02-20T23:06:33.798Z', closedBy: { username: 'elastic', @@ -61,11 +67,11 @@ export const caseClosedProps: CaseProps = { }; export const data: Case = { - ...caseProps.initialData, + ...caseProps.caseData, }; export const dataClosed: Case = { - ...caseClosedProps.initialData, + ...caseClosedProps.caseData, }; export const caseUserActions = [ diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/actions.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/actions.test.tsx index 4e1e5ba753c36..49f5f44cba271 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/actions.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/actions.test.tsx @@ -6,15 +6,15 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseViewActions } from './actions'; -import { TestProviders } from '../../../../mock'; + import { useDeleteCases } from '../../../../containers/case/use_delete_cases'; +import { TestProviders } from '../../../../mock'; +import { data } from './__mock__'; +import { CaseViewActions } from './actions'; jest.mock('../../../../containers/case/use_delete_cases'); const useDeleteCasesMock = useDeleteCases as jest.Mock; describe('CaseView actions', () => { - const caseTitle = 'Cool title'; - const caseId = 'cool-id'; const handleOnDeleteConfirm = jest.fn(); const handleToggleModal = jest.fn(); const dispatchResetIsDeleted = jest.fn(); @@ -34,7 +34,7 @@ describe('CaseView actions', () => { it('clicking trash toggles modal', () => { const wrapper = mount( - + ); @@ -54,12 +54,12 @@ describe('CaseView actions', () => { })); const wrapper = mount( - + ); expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeTruthy(); wrapper.find('button[data-test-subj="confirmModalConfirmButton"]').simulate('click'); - expect(handleOnDeleteConfirm.mock.calls[0][0]).toEqual([caseId]); + expect(handleOnDeleteConfirm.mock.calls[0][0]).toEqual([{ id: data.id, title: data.title }]); }); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/actions.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/actions.tsx index 88a717ac5fa6a..0b08b866df964 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/actions.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/actions.tsx @@ -4,21 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ +import { isEmpty } from 'lodash/fp'; import React, { useMemo } from 'react'; - import { Redirect } from 'react-router-dom'; import * as i18n from './translations'; import { useDeleteCases } from '../../../../containers/case/use_delete_cases'; import { ConfirmDeleteCaseModal } from '../confirm_delete_case'; import { SiemPageName } from '../../../home/types'; import { PropertyActions } from '../property_actions'; +import { Case } from '../../../../containers/case/types'; interface CaseViewActions { - caseId: string; - caseTitle: string; + caseData: Case; + disabled?: boolean; } -const CaseViewActionsComponent: React.FC = ({ caseId, caseTitle }) => { +const CaseViewActionsComponent: React.FC = ({ caseData, disabled = false }) => { // Delete case const { handleToggleModal, @@ -30,35 +31,35 @@ const CaseViewActionsComponent: React.FC = ({ caseId, caseTitle const confirmDeleteModal = useMemo( () => ( ), - [isDisplayConfirmDeleteModal] + [isDisplayConfirmDeleteModal, caseData] ); // TO DO refactor each of these const's into their own components const propertyActions = useMemo( () => [ { + disabled, iconType: 'trash', label: i18n.DELETE_CASE, onClick: handleToggleModal, }, - { - iconType: 'popout', - label: 'View ServiceNow incident', - onClick: () => null, - }, - { - iconType: 'importAction', - label: 'Update ServiceNow incident', - onClick: () => null, - }, + ...(caseData.externalService != null && !isEmpty(caseData.externalService?.externalUrl) + ? [ + { + iconType: 'popout', + label: i18n.VIEW_INCIDENT(caseData.externalService?.externalTitle ?? ''), + onClick: () => window.open(caseData.externalService?.externalUrl, '_blank'), + }, + ] + : []), ], - [handleToggleModal] + [disabled, handleToggleModal, caseData] ); if (isDeleted) { diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx index 18cc33d8a6d4d..3f5b3a3127177 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx @@ -16,10 +16,10 @@ import { TestProviders } from '../../../../mock'; import { useUpdateCase } from '../../../../containers/case/use_update_case'; import { useGetCaseUserActions } from '../../../../containers/case/use_get_case_user_actions'; import { wait } from '../../../../lib/helpers'; -import { usePushToService } from './push_to_service'; +import { usePushToService } from '../use_push_to_service'; jest.mock('../../../../containers/case/use_update_case'); jest.mock('../../../../containers/case/use_get_case_user_actions'); -jest.mock('./push_to_service'); +jest.mock('../use_push_to_service'); const useUpdateCaseMock = useUpdateCase as jest.Mock; const useGetCaseUserActionsMock = useGetCaseUserActions as jest.Mock; const usePushToServiceMock = usePushToService as jest.Mock; @@ -67,7 +67,6 @@ describe('CaseView ', () => { /* eslint-enable no-console */ const defaultUpdateCaseState = { - caseData: data, isLoading: false, isError: false, updateKey: null, @@ -186,12 +185,7 @@ describe('CaseView ', () => { wrapper .find('input[data-test-subj="toggle-case-status"]') .simulate('change', { target: { checked: true } }); - - expect(updateCaseProperty).toBeCalledWith({ - fetchCaseUserActions, - updateKey: 'status', - updateValue: 'closed', - }); + expect(updateCaseProperty).toHaveBeenCalled(); }); it('should render comments', async () => { diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx index 5c20b53f5fcb9..947da51365d66 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx @@ -34,10 +34,11 @@ import { CaseStatus } from '../case_status'; import { navTabs } from '../../../home/home_navigations'; import { SpyRoute } from '../../../../utils/route/spy_routes'; import { useGetCaseUserActions } from '../../../../containers/case/use_get_case_user_actions'; -import { usePushToService } from './push_to_service'; +import { usePushToService } from '../use_push_to_service'; interface Props { caseId: string; + userCanCrud: boolean; } const MyWrapper = styled(WrapperPage)` @@ -55,232 +56,259 @@ const MyEuiHorizontalRule = styled(EuiHorizontalRule)` } `; -export interface CaseProps { - caseId: string; - initialData: Case; +export interface CaseProps extends Props { + fetchCase: () => void; + caseData: Case; + updateCase: (newCase: Case) => void; } -export const CaseComponent = React.memo(({ caseId, initialData }) => { - const basePath = window.location.origin + useBasePath(); - const caseLink = `${basePath}/app/siem#/case/${caseId}`; - const search = useGetUrlSearch(navTabs.case); +export const CaseComponent = React.memo( + ({ caseId, caseData, fetchCase, updateCase, userCanCrud }) => { + const basePath = window.location.origin + useBasePath(); + const caseLink = `${basePath}/app/siem#/case/${caseId}`; + const search = useGetUrlSearch(navTabs.case); - const [initLoadingData, setInitLoadingData] = useState(true); - const { - caseUserActions, - fetchCaseUserActions, - firstIndexPushToService, - hasDataToPush, - isLoading: isLoadingUserActions, - lastIndexPushToService, - participants, - } = useGetCaseUserActions(caseId); - const { caseData, isLoading, updateKey, updateCase, updateCaseProperty } = useUpdateCase( - caseId, - initialData - ); + const [initLoadingData, setInitLoadingData] = useState(true); + const { + caseUserActions, + fetchCaseUserActions, + firstIndexPushToService, + hasDataToPush, + isLoading: isLoadingUserActions, + lastIndexPushToService, + participants, + } = useGetCaseUserActions(caseId); + const { isLoading, updateKey, updateCaseProperty } = useUpdateCase({ + caseId, + }); - // Update Fields - const onUpdateField = useCallback( - (newUpdateKey: keyof Case, updateValue: Case[keyof Case]) => { - switch (newUpdateKey) { - case 'title': - const titleUpdate = getTypedPayload(updateValue); - if (titleUpdate.length > 0) { - updateCaseProperty({ - fetchCaseUserActions, - updateKey: 'title', - updateValue: titleUpdate, - }); - } - break; - case 'description': - const descriptionUpdate = getTypedPayload(updateValue); - if (descriptionUpdate.length > 0) { + // Update Fields + const onUpdateField = useCallback( + (newUpdateKey: keyof Case, updateValue: Case[keyof Case]) => { + const handleUpdateNewCase = (newCase: Case) => + updateCase({ ...newCase, comments: caseData.comments }); + switch (newUpdateKey) { + case 'title': + const titleUpdate = getTypedPayload(updateValue); + if (titleUpdate.length > 0) { + updateCaseProperty({ + fetchCaseUserActions, + updateKey: 'title', + updateValue: titleUpdate, + updateCase: handleUpdateNewCase, + version: caseData.version, + }); + } + break; + case 'description': + const descriptionUpdate = getTypedPayload(updateValue); + if (descriptionUpdate.length > 0) { + updateCaseProperty({ + fetchCaseUserActions, + updateKey: 'description', + updateValue: descriptionUpdate, + updateCase: handleUpdateNewCase, + version: caseData.version, + }); + } + break; + case 'tags': + const tagsUpdate = getTypedPayload(updateValue); updateCaseProperty({ fetchCaseUserActions, - updateKey: 'description', - updateValue: descriptionUpdate, + updateKey: 'tags', + updateValue: tagsUpdate, + updateCase: handleUpdateNewCase, + version: caseData.version, }); - } - break; - case 'tags': - const tagsUpdate = getTypedPayload(updateValue); - updateCaseProperty({ - fetchCaseUserActions, - updateKey: 'tags', - updateValue: tagsUpdate, - }); - break; - case 'status': - const statusUpdate = getTypedPayload(updateValue); - if (caseData.status !== updateValue) { - updateCaseProperty({ - fetchCaseUserActions, - updateKey: 'status', - updateValue: statusUpdate, - }); - } - default: - return null; - } - }, - [fetchCaseUserActions, updateCaseProperty, caseData.status] - ); - const handleUpdateCase = useCallback( - (newCase: Case) => { - updateCase(newCase); - fetchCaseUserActions(newCase.id); - }, - [updateCase, fetchCaseUserActions] - ); + break; + case 'status': + const statusUpdate = getTypedPayload(updateValue); + if (caseData.status !== updateValue) { + updateCaseProperty({ + fetchCaseUserActions, + updateKey: 'status', + updateValue: statusUpdate, + updateCase: handleUpdateNewCase, + version: caseData.version, + }); + } + default: + return null; + } + }, + [fetchCaseUserActions, updateCaseProperty, updateCase, caseData] + ); + const handleUpdateCase = useCallback( + (newCase: Case) => { + updateCase(newCase); + fetchCaseUserActions(newCase.id); + }, + [updateCase, fetchCaseUserActions] + ); - const { pushButton, pushCallouts } = usePushToService({ - caseId: caseData.id, - caseStatus: caseData.status, - isNew: caseUserActions.filter(cua => cua.action === 'push-to-service').length === 0, - updateCase: handleUpdateCase, - }); + const { pushButton, pushCallouts } = usePushToService({ + caseId: caseData.id, + caseStatus: caseData.status, + isNew: caseUserActions.filter(cua => cua.action === 'push-to-service').length === 0, + updateCase: handleUpdateCase, + userCanCrud, + }); - const onSubmitTags = useCallback(newTags => onUpdateField('tags', newTags), [onUpdateField]); - const onSubmitTitle = useCallback(newTitle => onUpdateField('title', newTitle), [onUpdateField]); - const toggleStatusCase = useCallback( - e => onUpdateField('status', e.target.checked ? 'closed' : 'open'), - [onUpdateField] - ); - const spyState = useMemo(() => ({ caseTitle: caseData.title }), [caseData.title]); + const onSubmitTags = useCallback(newTags => onUpdateField('tags', newTags), [onUpdateField]); + const onSubmitTitle = useCallback(newTitle => onUpdateField('title', newTitle), [ + onUpdateField, + ]); + const toggleStatusCase = useCallback( + e => onUpdateField('status', e.target.checked ? 'closed' : 'open'), + [onUpdateField] + ); + const handleRefresh = useCallback(() => { + fetchCaseUserActions(caseData.id); + fetchCase(); + }, [caseData.id, fetchCase, fetchCaseUserActions]); - const caseStatusData = useMemo( - () => - caseData.status === 'open' - ? { - 'data-test-subj': 'case-view-createdAt', - value: caseData.createdAt, - title: i18n.CASE_OPENED, - buttonLabel: i18n.CLOSE_CASE, - status: caseData.status, - icon: 'folderCheck', - badgeColor: 'secondary', - isSelected: false, - } - : { - 'data-test-subj': 'case-view-closedAt', - value: caseData.closedAt ?? '', - title: i18n.CASE_CLOSED, - buttonLabel: i18n.REOPEN_CASE, - status: caseData.status, - icon: 'folderExclamation', - badgeColor: 'danger', - isSelected: true, - }, - [caseData.closedAt, caseData.createdAt, caseData.status] - ); - const emailContent = useMemo( - () => ({ - subject: i18n.EMAIL_SUBJECT(caseData.title), - body: i18n.EMAIL_BODY(caseLink), - }), - [caseLink, caseData.title] - ); + const spyState = useMemo(() => ({ caseTitle: caseData.title }), [caseData.title]); - useEffect(() => { - if (initLoadingData && !isLoadingUserActions) { - setInitLoadingData(false); - } - }, [initLoadingData, isLoadingUserActions]); + const caseStatusData = useMemo( + () => + caseData.status === 'open' + ? { + 'data-test-subj': 'case-view-createdAt', + value: caseData.createdAt, + title: i18n.CASE_OPENED, + buttonLabel: i18n.CLOSE_CASE, + status: caseData.status, + icon: 'folderCheck', + badgeColor: 'secondary', + isSelected: false, + } + : { + 'data-test-subj': 'case-view-closedAt', + value: caseData.closedAt ?? '', + title: i18n.CASE_CLOSED, + buttonLabel: i18n.REOPEN_CASE, + status: caseData.status, + icon: 'folderExclamation', + badgeColor: 'danger', + isSelected: true, + }, + [caseData.closedAt, caseData.createdAt, caseData.status] + ); + const emailContent = useMemo( + () => ({ + subject: i18n.EMAIL_SUBJECT(caseData.title), + body: i18n.EMAIL_BODY(caseLink), + }), + [caseLink, caseData.title] + ); - return ( - <> - - - } - title={caseData.title} - > - - - - + useEffect(() => { + if (initLoadingData && !isLoadingUserActions) { + setInitLoadingData(false); + } + }, [initLoadingData, isLoadingUserActions]); + + return ( + <> - {pushCallouts != null && pushCallouts} - - - {initLoadingData && } - {!initLoadingData && ( - <> - - - - - - - {hasDataToPush && {pushButton}} - - - )} - - - - - - - + } + title={caseData.title} + > + + - - - - ); -}); + + + {pushCallouts != null && pushCallouts} + + + {initLoadingData && } + {!initLoadingData && ( + <> + + + + + + + {hasDataToPush && {pushButton}} + + + )} + + + + + + + + + + + + ); + } +); -export const CaseView = React.memo(({ caseId }: Props) => { - const { data, isLoading, isError } = useGetCase(caseId); +export const CaseView = React.memo(({ caseId, userCanCrud }: Props) => { + const { data, isLoading, isError, fetchCase, updateCase } = useGetCase(caseId); if (isError) { return null; } @@ -294,7 +322,15 @@ export const CaseView = React.memo(({ caseId }: Props) => { ); } - return ; + return ( + + ); }); CaseComponent.displayName = 'CaseComponent'; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/translations.ts index c081567e3be72..17132b9610754 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/translations.ts @@ -34,6 +34,14 @@ export const REMOVED_FIELD = i18n.translate('xpack.siem.case.caseView.actionLabe defaultMessage: 'removed', }); +export const VIEW_INCIDENT = (incidentNumber: string) => + i18n.translate('xpack.siem.case.caseView.actionLabel.viewIncident', { + defaultMessage: 'View {incidentNumber}', + values: { + incidentNumber, + }, + }); + export const PUSHED_NEW_INCIDENT = i18n.translate( 'xpack.siem.case.caseView.actionLabel.pushedNewIncident', { @@ -95,6 +103,10 @@ export const CASE_CLOSED = i18n.translate('xpack.siem.case.caseView.caseClosed', defaultMessage: 'Case closed', }); +export const CASE_REFRESH = i18n.translate('xpack.siem.case.caseView.caseRefresh', { + defaultMessage: 'Refresh case', +}); + export const EMAIL_SUBJECT = (caseTitle: string) => i18n.translate('xpack.siem.case.caseView.emailSubject', { values: { caseTitle }, @@ -106,56 +118,3 @@ export const EMAIL_BODY = (caseUrl: string) => values: { caseUrl }, defaultMessage: 'Case reference: {caseUrl}', }); - -export const PUSH_SERVICENOW = i18n.translate('xpack.siem.case.caseView.pushAsServicenowIncident', { - defaultMessage: 'Push as ServiceNow incident', -}); - -export const UPDATE_PUSH_SERVICENOW = i18n.translate( - 'xpack.siem.case.caseView.updatePushAsServicenowIncident', - { - defaultMessage: 'Update ServiceNow incident', - } -); - -export const PUSH_DISABLE_BY_NO_CASE_CONFIG_TITLE = i18n.translate( - 'xpack.siem.case.caseView.pushToServiceDisableByNoCaseConfigTitle', - { - defaultMessage: 'Configure external connector', - } -); - -export const PUSH_DISABLE_BECAUSE_CASE_CLOSED_TITLE = i18n.translate( - 'xpack.siem.case.caseView.pushToServiceDisableBecauseCaseClosedTitle', - { - defaultMessage: 'Reopen the case', - } -); - -export const PUSH_DISABLE_BY_KIBANA_CONFIG_TITLE = i18n.translate( - 'xpack.siem.case.caseView.pushToServiceDisableByConfigTitle', - { - defaultMessage: 'Enable ServiceNow in Kibana configuration file', - } -); - -export const PUSH_DISABLE_BY_LICENSE_TITLE = i18n.translate( - 'xpack.siem.case.caseView.pushToServiceDisableByLicenseTitle', - { - defaultMessage: 'Upgrade to Elastic Platinum', - } -); - -export const LINK_CLOUD_DEPLOYMENT = i18n.translate( - 'xpack.siem.case.caseView.cloudDeploymentLink', - { - defaultMessage: 'cloud deployment', - } -); - -export const LINK_CONNECTOR_CONFIGURE = i18n.translate( - 'xpack.siem.case.caseView.connectorConfigureLink', - { - defaultMessage: 'connector', - } -); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/button.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/button.tsx new file mode 100644 index 0000000000000..b0bea83148bda --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/button.tsx @@ -0,0 +1,50 @@ +/* + * 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 { EuiButton, EuiToolTip } from '@elastic/eui'; +import React, { memo, useMemo } from 'react'; +import { getConfigureCasesUrl } from '../../../../components/link_to'; + +interface ConfigureCaseButtonProps { + label: string; + isDisabled: boolean; + msgTooltip: JSX.Element; + showToolTip: boolean; + titleTooltip: string; + urlSearch: string; +} + +const ConfigureCaseButtonComponent: React.FC = ({ + isDisabled, + label, + msgTooltip, + showToolTip, + titleTooltip, + urlSearch, +}: ConfigureCaseButtonProps) => { + const configureCaseButton = useMemo( + () => ( + + {label} + + ), + [label, isDisabled, urlSearch] + ); + return showToolTip ? ( + {msgTooltip}

}> + {configureCaseButton} +
+ ) : ( + <>{configureCaseButton} + ); +}; + +export const ConfigureCaseButton = memo(ConfigureCaseButtonComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx index bb0c50b3b193a..8fb1cfb1aa6cc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx @@ -48,7 +48,9 @@ const ConnectorsComponent: React.FC = ({ {i18n.INCIDENT_MANAGEMENT_SYSTEM_LABEL} - {i18n.ADD_NEW_CONNECTOR} + + {i18n.ADD_NEW_CONNECTOR} + ); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx index a1f24275df6cd..b8cf5a3880801 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx @@ -57,8 +57,12 @@ const FormWrapper = styled.div` margin-top 40px; } - padding-top: ${theme.eui.paddingSizes.l}; - padding-bottom: ${theme.eui.paddingSizes.l}; + & > :first-child { + margin-top: 0; + } + + padding-top: ${theme.eui.paddingSizes.xl}; + padding-bottom: ${theme.eui.paddingSizes.xl}; `} `; @@ -80,7 +84,11 @@ const actionTypes: ActionType[] = [ }, ]; -const ConfigureCasesComponent: React.FC = () => { +interface ConfigureCasesComponentProps { + userCanCrud: boolean; +} + +const ConfigureCasesComponent: React.FC = ({ userCanCrud }) => { const search = useGetUrlSearch(navTabs.case); const { http, triggers_actions_ui, notifications, application } = useKibana().services; @@ -251,7 +259,7 @@ const ConfigureCasesComponent: React.FC = () => { { { iconType="cross" isDisabled={isLoadingAny} isLoading={persistLoading} - aria-label="Cancel" + aria-label={i18n.CANCEL} href={getCaseUrl(search)} > {i18n.CANCEL} @@ -301,7 +309,7 @@ const ConfigureCasesComponent: React.FC = () => { fill color="secondary" iconType="save" - aria-label="Save" + aria-label={i18n.SAVE_CHANGES} isDisabled={isLoadingAny} isLoading={persistLoading} onClick={handleSubmit} diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/translations.ts index 33784cfe0058c..49caeae1c3a34 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/translations.ts @@ -104,7 +104,7 @@ export const FIELD_MAPPING_FIRST_COL = i18n.translate( export const FIELD_MAPPING_SECOND_COL = i18n.translate( 'xpack.siem.case.configureCases.fieldMappingSecondCol', { - defaultMessage: 'Third-party incident field', + defaultMessage: 'External incident field', } ); @@ -155,7 +155,7 @@ export const WARNING_NO_CONNECTOR_MESSAGE = i18n.translate( 'xpack.siem.case.configureCases.warningMessage', { defaultMessage: - 'Configuration seems to be invalid. The selected connector is missing. Did you delete the connector?', + 'The selected connector has been deleted. Either select a different connector or create a new one.', } ); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx index 20712c3c5a815..740909db408ec 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx @@ -15,7 +15,7 @@ import { import styled, { css } from 'styled-components'; import { Redirect } from 'react-router-dom'; -import { CaseRequest } from '../../../../../../../../plugins/case/common/api'; +import { CasePostRequest } from '../../../../../../../../plugins/case/common/api'; import { Field, Form, getUseField, useForm, UseField } from '../../../../shared_imports'; import { usePostCase } from '../../../../containers/case/use_post_case'; import { schema } from './schema'; @@ -45,9 +45,8 @@ const MySpinner = styled(EuiLoadingSpinner)` z-index: 99; `; -const initialCaseValue: CaseRequest = { +const initialCaseValue: CasePostRequest = { description: '', - status: 'open', tags: [], title: '', }; @@ -55,12 +54,12 @@ const initialCaseValue: CaseRequest = { export const Create = React.memo(() => { const { caseData, isLoading, postCase } = usePostCase(); const [isCancel, setIsCancel] = useState(false); - const { form } = useForm({ + const { form } = useForm({ defaultValue: initialCaseValue, options: { stripEmptyFields: false }, schema, }); - const { handleCursorChange, handleOnTimelineChange } = useInsertTimeline( + const { handleCursorChange, handleOnTimelineChange } = useInsertTimeline( form, 'description' ); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/create/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/create/schema.tsx index 91d3b77493b03..4653dbc67d5a1 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/create/schema.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/create/schema.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CaseRequest } from '../../../../../../../../plugins/case/common/api'; +import { CasePostRequest } from '../../../../../../../../plugins/case/common/api'; import { FIELD_TYPES, fieldValidators, FormSchema } from '../../../../shared_imports'; import * as i18n from '../../translations'; @@ -18,7 +18,7 @@ export const schemaTags = { labelAppend: OptionalFieldLabel, }; -export const schema: FormSchema = { +export const schema: FormSchema = { title: { type: FIELD_TYPES.TEXT, label: i18n.NAME, diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/index.tsx index 25332982dca1a..6b8e00921abcb 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/index.tsx @@ -7,7 +7,10 @@ import React, { useCallback, useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiPopover, EuiButtonIcon, EuiButtonEmpty } from '@elastic/eui'; +import * as i18n from './translations'; + export interface PropertyActionButtonProps { + disabled?: boolean; onClick: () => void; iconType: string; label: string; @@ -16,13 +19,14 @@ export interface PropertyActionButtonProps { const ComponentId = 'property-actions'; const PropertyActionButton = React.memo( - ({ onClick, iconType, label }) => ( + ({ disabled = false, onClick, iconType, label }) => ( {label} @@ -55,10 +59,11 @@ export const PropertyActions = React.memo(({ propertyActio @@ -76,6 +81,7 @@ export const PropertyActions = React.memo(({ propertyActio {propertyActions.map((action, key) => ( onClosePopover(action.onClick)} diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/translations.ts new file mode 100644 index 0000000000000..4d7e15a76739d --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/translations.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const ACTIONS_ARIA = i18n.translate('xpack.siem.case.caseView.editActionsLinkAria', { + defaultMessage: 'click to see all actions', +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/index.tsx index 3513d4de12aa1..f7d890ca60b16 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/index.tsx @@ -17,12 +17,13 @@ import { EuiLoadingSpinner, } from '@elastic/eui'; import styled, { css } from 'styled-components'; -import * as i18n from '../../translations'; +import * as i18n from './translations'; import { Form, useForm } from '../../../../shared_imports'; import { schema } from './schema'; import { CommonUseField } from '../create'; interface TagListProps { + disabled?: boolean; isLoading: boolean; onSubmit: (a: string[]) => void; tags: string[]; @@ -37,89 +38,98 @@ const MyFlexGroup = styled(EuiFlexGroup)` `} `; -export const TagList = React.memo(({ isLoading, onSubmit, tags }: TagListProps) => { - const { form } = useForm({ - defaultValue: { tags }, - options: { stripEmptyFields: false }, - schema, - }); - const [isEditTags, setIsEditTags] = useState(false); +export const TagList = React.memo( + ({ disabled = false, isLoading, onSubmit, tags }: TagListProps) => { + const { form } = useForm({ + defaultValue: { tags }, + options: { stripEmptyFields: false }, + schema, + }); + const [isEditTags, setIsEditTags] = useState(false); - const onSubmitTags = useCallback(async () => { - const { isValid, data: newData } = await form.submit(); - if (isValid && newData.tags) { - onSubmit(newData.tags); - setIsEditTags(false); - } - }, [form, onSubmit]); + const onSubmitTags = useCallback(async () => { + const { isValid, data: newData } = await form.submit(); + if (isValid && newData.tags) { + onSubmit(newData.tags); + setIsEditTags(false); + } + }, [form, onSubmit]); - return ( - - - -

{i18n.TAGS}

-
- {isLoading && } - {!isLoading && ( + return ( + + - +

{i18n.TAGS}

- )} -
- - - {tags.length === 0 && !isEditTags &&

{i18n.NO_TAGS}

} - {tags.length > 0 && - !isEditTags && - tags.map((tag, key) => ( - - {tag} - - ))} - {isEditTags && ( - - -
- - -
- - - - - {i18n.SAVE} - - - - - {i18n.CANCEL} - - - + {isLoading && } + {!isLoading && ( + + -
- )} -
-
- ); -}); + )} +
+ + + {tags.length === 0 && !isEditTags &&

{i18n.NO_TAGS}

} + {tags.length > 0 && + !isEditTags && + tags.map((tag, key) => ( + + {tag} + + ))} + {isEditTags && ( + + +
+ + +
+ + + + + {i18n.SAVE} + + + + + {i18n.CANCEL} + + + + +
+ )} +
+
+ ); + } +); TagList.displayName = 'TagList'; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/translations.ts new file mode 100644 index 0000000000000..f7f215248dad8 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/translations.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. + */ + +import { i18n } from '@kbn/i18n'; + +export * from '../../translations'; + +export const EDIT_TAGS_ARIA = i18n.translate('xpack.siem.case.caseView.editTagsLinkAria', { + defaultMessage: 'click to edit tags', +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/helpers.tsx new file mode 100644 index 0000000000000..1e4fd92058e8d --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/helpers.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 { EuiLink } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; + +import * as i18n from './translations'; +import { ActionLicense } from '../../../../containers/case/types'; + +export const getLicenseError = () => ({ + title: i18n.PUSH_DISABLE_BY_LICENSE_TITLE, + description: ( + + {i18n.LINK_CLOUD_DEPLOYMENT} + + ), + }} + /> + ), +}); + +export const getKibanaConfigError = () => ({ + title: i18n.PUSH_DISABLE_BY_KIBANA_CONFIG_TITLE, + description: ( + + {'coming soon...'} + + ), + }} + /> + ), +}); + +export const getActionLicenseError = ( + actionLicense: ActionLicense | null +): Array<{ title: string; description: JSX.Element }> => { + let errors: Array<{ title: string; description: JSX.Element }> = []; + if (actionLicense != null && !actionLicense.enabledInLicense) { + errors = [...errors, getLicenseError()]; + } + if (actionLicense != null && !actionLicense.enabledInConfig) { + errors = [...errors, getKibanaConfigError()]; + } + return errors; +}; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/push_to_service.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/index.tsx similarity index 71% rename from x-pack/legacy/plugins/siem/public/pages/case/components/case_view/push_to_service.tsx rename to x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/index.tsx index 944302c1940ee..aeb694e52b7fa 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/push_to_service.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/index.tsx @@ -5,8 +5,8 @@ */ import { EuiButton, EuiLink, EuiToolTip } from '@elastic/eui'; -import React, { useCallback, useState, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; +import React, { useCallback, useState, useMemo } from 'react'; import { useCaseConfigure } from '../../../../containers/case/configure/use_configure'; import { Case } from '../../../../containers/case/types'; @@ -15,7 +15,8 @@ import { usePostPushToService } from '../../../../containers/case/use_post_push_ import { getConfigureCasesUrl } from '../../../../components/link_to'; import { useGetUrlSearch } from '../../../../components/navigation/use_get_url_search'; import { navTabs } from '../../../home/home_navigations'; -import { ErrorsPushServiceCallOut } from '../errors_push_service_callout'; +import { CaseCallOut } from '../callout'; +import { getLicenseError, getKibanaConfigError } from './helpers'; import * as i18n from './translations'; interface UsePushToService { @@ -23,6 +24,7 @@ interface UsePushToService { caseStatus: string; isNew: boolean; updateCase: (newCase: Case) => void; + userCanCrud: boolean; } interface Connector { @@ -38,8 +40,9 @@ interface ReturnUsePushToService { export const usePushToService = ({ caseId, caseStatus, - updateCase, isNew, + updateCase, + userCanCrud, }: UsePushToService): ReturnUsePushToService => { const urlSearch = useGetUrlSearch(navTabs.case); const [connector, setConnector] = useState(null); @@ -69,25 +72,7 @@ export const usePushToService = ({ const errorsMsg = useMemo(() => { let errors: Array<{ title: string; description: JSX.Element }> = []; if (actionLicense != null && !actionLicense.enabledInLicense) { - errors = [ - ...errors, - { - title: i18n.PUSH_DISABLE_BY_LICENSE_TITLE, - description: ( - - {i18n.LINK_CLOUD_DEPLOYMENT} - - ), - }} - /> - ), - }, - ]; + errors = [...errors, getLicenseError()]; } if (connector == null && !loadingCaseConfigure && !loadingLicense) { errors = [ @@ -125,25 +110,7 @@ export const usePushToService = ({ ]; } if (actionLicense != null && !actionLicense.enabledInConfig) { - errors = [ - ...errors, - { - title: i18n.PUSH_DISABLE_BY_KIBANA_CONFIG_TITLE, - description: ( - - {'coming soon...'} - - ), - }} - /> - ), - }, - ]; + errors = [...errors, getKibanaConfigError()]; } return errors; }, [actionLicense, caseStatus, connector, loadingCaseConfigure, loadingLicense, urlSearch]); @@ -154,13 +121,27 @@ export const usePushToService = ({ fill iconType="importAction" onClick={handlePushToService} - disabled={isLoading || loadingLicense || loadingCaseConfigure || errorsMsg.length > 0} + disabled={ + isLoading || + loadingLicense || + loadingCaseConfigure || + errorsMsg.length > 0 || + !userCanCrud + } isLoading={isLoading} > {isNew ? i18n.PUSH_SERVICENOW : i18n.UPDATE_PUSH_SERVICENOW} ), - [isNew, handlePushToService, isLoading, loadingLicense, loadingCaseConfigure, errorsMsg] + [ + isNew, + handlePushToService, + isLoading, + loadingLicense, + loadingCaseConfigure, + errorsMsg, + userCanCrud, + ] ); const objToReturn = useMemo( @@ -177,7 +158,10 @@ export const usePushToService = ({ ) : ( <>{pushToServiceButton} ), - pushCallouts: errorsMsg.length > 0 ? : null, + pushCallouts: + errorsMsg.length > 0 ? ( + + ) : null, }), [errorsMsg, pushToServiceButton] ); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/translations.ts new file mode 100644 index 0000000000000..14bdb0c69712c --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/translations.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 { i18n } from '@kbn/i18n'; + +export const ERROR_PUSH_SERVICE_CALLOUT_TITLE = i18n.translate( + 'xpack.siem.case.caseView.errorsPushServiceCallOutTitle', + { + defaultMessage: 'To send cases to external systems, you need to:', + } +); + +export const PUSH_SERVICENOW = i18n.translate('xpack.siem.case.caseView.pushAsServicenowIncident', { + defaultMessage: 'Push as ServiceNow incident', +}); + +export const UPDATE_PUSH_SERVICENOW = i18n.translate( + 'xpack.siem.case.caseView.updatePushAsServicenowIncident', + { + defaultMessage: 'Update ServiceNow incident', + } +); + +export const PUSH_DISABLE_BY_NO_CASE_CONFIG_TITLE = i18n.translate( + 'xpack.siem.case.caseView.pushToServiceDisableByNoCaseConfigTitle', + { + defaultMessage: 'Configure external connector', + } +); + +export const PUSH_DISABLE_BECAUSE_CASE_CLOSED_TITLE = i18n.translate( + 'xpack.siem.case.caseView.pushToServiceDisableBecauseCaseClosedTitle', + { + defaultMessage: 'Reopen the case', + } +); + +export const PUSH_DISABLE_BY_KIBANA_CONFIG_TITLE = i18n.translate( + 'xpack.siem.case.caseView.pushToServiceDisableByConfigTitle', + { + defaultMessage: 'Enable ServiceNow in Kibana configuration file', + } +); + +export const PUSH_DISABLE_BY_LICENSE_TITLE = i18n.translate( + 'xpack.siem.case.caseView.pushToServiceDisableByLicenseTitle', + { + defaultMessage: 'Upgrade to Elastic Platinum', + } +); + +export const LINK_CLOUD_DEPLOYMENT = i18n.translate( + 'xpack.siem.case.caseView.cloudDeploymentLink', + { + defaultMessage: 'cloud deployment', + } +); + +export const LINK_CONNECTOR_CONFIGURE = i18n.translate( + 'xpack.siem.case.caseView.connectorConfigureLink', + { + defaultMessage: 'connector', + } +); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/index.tsx index d8b9ac115426a..0892d5dcb3ee7 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/index.tsx @@ -11,7 +11,7 @@ import styled from 'styled-components'; import * as i18n from '../case_view/translations'; -import { Case, CaseUserActions, Comment } from '../../../../containers/case/types'; +import { Case, CaseUserActions } from '../../../../containers/case/types'; import { useUpdateComment } from '../../../../containers/case/use_update_comment'; import { useCurrentUser } from '../../../../lib/kibana'; import { AddComment } from '../add_comment'; @@ -28,6 +28,8 @@ export interface UserActionTreeProps { isLoadingUserActions: boolean; lastIndexPushToService: number; onUpdateField: (updateKey: keyof Case, updateValue: string | string[]) => void; + updateCase: (newCase: Case) => void; + userCanCrud: boolean; } const MyEuiFlexGroup = styled(EuiFlexGroup)` @@ -47,14 +49,14 @@ export const UserActionTree = React.memo( isLoadingUserActions, lastIndexPushToService, onUpdateField, + updateCase, + userCanCrud, }: UserActionTreeProps) => { const { commentId } = useParams(); const handlerTimeoutId = useRef(0); const [initLoading, setInitLoading] = useState(true); const [selectedOutlineCommentId, setSelectedOutlineCommentId] = useState(''); - const { comments, isLoadingIds, updateComment, addPostedComment } = useUpdateComment( - caseData.comments - ); + const { isLoadingIds, patchComment } = useUpdateComment(); const currentUser = useCurrentUser(); const [manageMarkdownEditIds, setManangeMardownEditIds] = useState([]); const [insertQuote, setInsertQuote] = useState(null); @@ -71,16 +73,18 @@ export const UserActionTree = React.memo( ); const handleSaveComment = useCallback( - (id: string, content: string) => { + ({ id, version }: { id: string; version: string }, content: string) => { handleManageMarkdownEditId(id); - updateComment({ + patchComment({ caseId: caseData.id, commentId: id, commentUpdate: content, fetchUserActions, + version, + updateCase, }); }, - [handleManageMarkdownEditId, updateComment] + [caseData, handleManageMarkdownEditId, patchComment, updateCase] ); const handleOutlineComment = useCallback( @@ -117,11 +121,11 @@ export const UserActionTree = React.memo( ); const handleUpdate = useCallback( - (comment: Comment) => { - addPostedComment(comment); + (newCase: Case) => { + updateCase(newCase); fetchUserActions(); }, - [addPostedComment, fetchUserActions] + [fetchUserActions, updateCase] ); const MarkdownDescription = useMemo( @@ -144,13 +148,14 @@ export const UserActionTree = React.memo( () => ( ), - [caseData.id, handleUpdate, insertQuote] + [caseData.id, handleUpdate, insertQuote, userCanCrud] ); useEffect(() => { @@ -166,27 +171,29 @@ export const UserActionTree = React.memo( <> {i18n.ADDED_DESCRIPTION}} - fullName={caseData.createdBy.fullName ?? caseData.createdBy.username} + fullName={caseData.createdBy.fullName ?? caseData.createdBy.username ?? ''} markdown={MarkdownDescription} onEdit={handleManageMarkdownEditId.bind(null, DESCRIPTION_ID)} onQuote={handleManageQuote.bind(null, caseData.description)} - userName={caseData.createdBy.username} + username={caseData.createdBy.username ?? 'Unknown'} /> {caseUserActions.map((action, index) => { if (action.commentId != null && action.action === 'create') { - const comment = comments.find(c => c.id === action.commentId); + const comment = caseData.comments.find(c => c.id === action.commentId); if (comment != null) { return ( {i18n.ADDED_COMMENT}} - fullName={comment.createdBy.fullName ?? comment.createdBy.username} + fullName={comment.createdBy.fullName ?? comment.createdBy.username ?? ''} markdown={ } onEdit={handleManageMarkdownEditId.bind(null, comment.id)} onQuote={handleManageQuote.bind(null, comment.comment)} outlineComment={handleOutlineComment} - userName={comment.createdBy.username} + username={comment.createdBy.username ?? ''} updatedAt={comment.updatedAt} /> ); @@ -226,6 +236,7 @@ export const UserActionTree = React.memo( ); } @@ -258,12 +269,13 @@ export const UserActionTree = React.memo( )} ); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/translations.ts index 0ca6bcff513fc..066145f7762c9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/translations.ts @@ -22,13 +22,13 @@ export const REQUIRED_UPDATE_TO_SERVICE = i18n.translate( } ); -export const COPY_LINK_COMMENT = i18n.translate('xpack.siem.case.caseView.copyCommentLinkAria', { - defaultMessage: 'click to copy comment link', +export const COPY_REFERENCE_LINK = i18n.translate('xpack.siem.case.caseView.copyCommentLinkAria', { + defaultMessage: 'Copy reference link', }); export const MOVE_TO_ORIGINAL_COMMENT = i18n.translate( 'xpack.siem.case.caseView.moveToCommentAria', { - defaultMessage: 'click to highlight the reference comment', + defaultMessage: 'Highlight the referenced comment', } ); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_item.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_item.tsx index c1dbe3b5fdbfa..89b94d98f91db 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_item.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_item.tsx @@ -21,6 +21,7 @@ import * as i18n from './translations'; interface UserActionItemProps { createdAt: string; + disabled: boolean; id: string; isEditable: boolean; isLoading: boolean; @@ -28,11 +29,11 @@ interface UserActionItemProps { labelQuoteAction?: string; labelTitle?: JSX.Element; linkId?: string | null; - fullName: string; + fullName?: string | null; markdown?: React.ReactNode; onEdit?: (id: string) => void; onQuote?: (id: string) => void; - userName: string; + username: string; updatedAt?: string | null; outlineComment?: (id: string) => void; showBottomFooter?: boolean; @@ -110,6 +111,7 @@ const PushedInfoContainer = styled.div` export const UserActionItem = ({ createdAt, + disabled, id, idToOutline, isEditable, @@ -125,15 +127,15 @@ export const UserActionItem = ({ outlineComment, showBottomFooter, showTopFooter, - userName, + username, updatedAt, }: UserActionItemProps) => ( - {fullName.length > 0 || userName.length > 0 ? ( - + {(fullName && fullName.length > 0) || username.length > 0 ? ( + ) : ( )} @@ -148,13 +150,15 @@ export const UserActionItem = ({ > } linkId={linkId} - userName={userName} + fullName={fullName} + username={username} updatedAt={updatedAt} onEdit={onEdit} onQuote={onQuote} diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_title.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_title.tsx index 391f54da7e972..9ccf921c87602 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_title.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_title.tsx @@ -4,7 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem, EuiText, EuiButtonIcon } from '@elastic/eui'; +import { + EuiLoadingSpinner, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiButtonIcon, + EuiToolTip, +} from '@elastic/eui'; import { FormattedRelative } from '@kbn/i18n/react'; import copy from 'copy-to-clipboard'; import { isEmpty } from 'lodash/fp'; @@ -27,14 +34,16 @@ const MySpinner = styled(EuiLoadingSpinner)` interface UserActionTitleProps { createdAt: string; + disabled: boolean; id: string; isLoading: boolean; labelEditAction?: string; labelQuoteAction?: string; labelTitle: JSX.Element; linkId?: string | null; + fullName?: string | null; updatedAt?: string | null; - userName: string; + username: string; onEdit?: (id: string) => void; onQuote?: (id: string) => void; outlineComment?: (id: string) => void; @@ -42,13 +51,15 @@ interface UserActionTitleProps { export const UserActionTitle = ({ createdAt, + disabled, id, isLoading, labelEditAction, labelQuoteAction, labelTitle, linkId, - userName, + fullName, + username, updatedAt, onEdit, onQuote, @@ -61,6 +72,7 @@ export const UserActionTitle = ({ ...(labelEditAction != null && onEdit != null ? [ { + disabled, iconType: 'pencil', label: labelEditAction, onClick: () => onEdit(id), @@ -70,6 +82,7 @@ export const UserActionTitle = ({ ...(labelQuoteAction != null && onQuote != null ? [ { + disabled, iconType: 'quote', label: labelQuoteAction, onClick: () => onQuote(id), @@ -77,7 +90,7 @@ export const UserActionTitle = ({ ] : []), ]; - }, [id, labelEditAction, onEdit, labelQuoteAction, onQuote]); + }, [disabled, id, labelEditAction, onEdit, labelQuoteAction, onQuote]); const handleAnchorLink = useCallback(() => { copy( @@ -105,7 +118,9 @@ export const UserActionTitle = ({ - {userName} + {fullName ?? username}

}> + {username} +
{labelTitle} @@ -137,20 +152,24 @@ export const UserActionTitle = ({ {!isEmpty(linkId) && ( - + {i18n.MOVE_TO_ORIGINAL_COMMENT}

}> + +
)} - + {i18n.COPY_REFERENCE_LINK}

}> + +
{propertyActions.length > 0 && ( diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/index.tsx index 9ace36eea1e9e..579e8e48fa147 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/index.tsx @@ -5,6 +5,8 @@ */ import React, { useCallback } from 'react'; +import { isEmpty } from 'lodash/fp'; + import { EuiButtonIcon, EuiText, @@ -12,9 +14,14 @@ import { EuiAvatar, EuiFlexGroup, EuiFlexItem, + EuiLoadingSpinner, + EuiToolTip, } from '@elastic/eui'; + import styled, { css } from 'styled-components'; + import { ElasticUser } from '../../../../containers/case/types'; +import * as i18n from './translations'; interface UserListProps { email: { @@ -22,6 +29,7 @@ interface UserListProps { body: string; }; headline: string; + loading?: boolean; users: ElasticUser[]; } @@ -38,20 +46,22 @@ const MyFlexGroup = styled(EuiFlexGroup)` const renderUsers = ( users: ElasticUser[], handleSendEmail: (emailAddress: string | undefined | null) => void -) => { - return users.map(({ fullName, username, email }, key) => ( +) => + users.map(({ fullName, username, email }, key) => ( - + -

- - {username} - -

+ {fullName ? fullName : username ?? ''}

}> +

+ + {username} + +

+
@@ -60,14 +70,14 @@ const renderUsers = ( data-test-subj="user-list-email-button" onClick={handleSendEmail.bind(null, email)} iconType="email" - aria-label="email" + aria-label={i18n.SEND_EMAIL_ARIA(fullName ? fullName : username ?? '')} + isDisabled={isEmpty(email)} />
)); -}; -export const UserList = React.memo(({ email, headline, users }: UserListProps) => { +export const UserList = React.memo(({ email, headline, loading, users }: UserListProps) => { const handleSendEmail = useCallback( (emailAddress: string | undefined | null) => { if (emailAddress && emailAddress != null) { @@ -76,13 +86,23 @@ export const UserList = React.memo(({ email, headline, users }: UserListProps) = }, [email.subject] ); - return ( + return users.filter(({ username }) => username != null && username !== '').length > 0 ? (

{headline}

- {renderUsers(users, handleSendEmail)} + {loading && ( + + + + + + )} + {renderUsers( + users.filter(({ username }) => username != null && username !== ''), + handleSendEmail + )}
- ); + ) : null; }); UserList.displayName = 'UserList'; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/translations.ts new file mode 100644 index 0000000000000..4d50d11f39101 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/translations.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. + */ + +import { i18n } from '@kbn/i18n'; + +export const SEND_EMAIL_ARIA = (user: string) => + i18n.translate('xpack.siem.case.caseView.sendEmalLinkAria', { + values: { user }, + defaultMessage: 'click to send an email to {user}', + }); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/configure_cases.tsx b/x-pack/legacy/plugins/siem/public/pages/case/configure_cases.tsx index b7e7ced308331..7515efa0e1b7a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/configure_cases.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/configure_cases.tsx @@ -5,16 +5,18 @@ */ import React, { useMemo } from 'react'; +import { Redirect } from 'react-router-dom'; +import { getCaseUrl } from '../../components/link_to'; +import { useGetUrlSearch } from '../../components/navigation/use_get_url_search'; import { WrapperPage } from '../../components/wrapper_page'; -import { CaseHeaderPage } from './components/case_header_page'; +import { useGetUserSavedObjectPermissions } from '../../lib/kibana'; import { SpyRoute } from '../../utils/route/spy_routes'; -import { getCaseUrl } from '../../components/link_to'; +import { navTabs } from '../home/home_navigations'; +import { CaseHeaderPage } from './components/case_header_page'; +import { ConfigureCases } from './components/configure_cases'; import { WhitePageWrapper, SectionWrapper } from './components/wrappers'; import * as i18n from './translations'; -import { ConfigureCases } from './components/configure_cases'; -import { useGetUrlSearch } from '../../components/navigation/use_get_url_search'; -import { navTabs } from '../home/home_navigations'; const wrapperPageStyle: Record = { paddingLeft: '0', @@ -23,6 +25,7 @@ const wrapperPageStyle: Record = { }; const ConfigureCasesPageComponent: React.FC = () => { + const userPermissions = useGetUserSavedObjectPermissions(); const search = useGetUrlSearch(navTabs.case); const backOptions = useMemo( @@ -33,6 +36,10 @@ const ConfigureCasesPageComponent: React.FC = () => { [search] ); + if (userPermissions != null && !userPermissions.read) { + return ; + } + return ( <> @@ -40,7 +47,7 @@ const ConfigureCasesPageComponent: React.FC = () => {
- + diff --git a/x-pack/legacy/plugins/siem/public/pages/case/create_case.tsx b/x-pack/legacy/plugins/siem/public/pages/case/create_case.tsx index bd1f6da0ca28b..06cb7fadfb8d3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/create_case.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/create_case.tsx @@ -5,17 +5,20 @@ */ import React, { useMemo } from 'react'; +import { Redirect } from 'react-router-dom'; +import { getCaseUrl } from '../../components/link_to'; +import { useGetUrlSearch } from '../../components/navigation/use_get_url_search'; import { WrapperPage } from '../../components/wrapper_page'; -import { Create } from './components/create'; +import { useGetUserSavedObjectPermissions } from '../../lib/kibana'; import { SpyRoute } from '../../utils/route/spy_routes'; +import { navTabs } from '../home/home_navigations'; import { CaseHeaderPage } from './components/case_header_page'; +import { Create } from './components/create'; import * as i18n from './translations'; -import { getCaseUrl } from '../../components/link_to'; -import { useGetUrlSearch } from '../../components/navigation/use_get_url_search'; -import { navTabs } from '../home/home_navigations'; export const CreateCasePage = React.memo(() => { + const userPermissions = useGetUserSavedObjectPermissions(); const search = useGetUrlSearch(navTabs.case); const backOptions = useMemo( @@ -26,6 +29,10 @@ export const CreateCasePage = React.memo(() => { [search] ); + if (userPermissions != null && !userPermissions.crud) { + return ; + } + return ( <> diff --git a/x-pack/legacy/plugins/siem/public/pages/case/saved_object_no_permissions.tsx b/x-pack/legacy/plugins/siem/public/pages/case/saved_object_no_permissions.tsx new file mode 100644 index 0000000000000..689c290c91019 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/saved_object_no_permissions.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { EmptyPage } from '../../components/empty_page'; +import * as i18n from './translations'; +import { useKibana } from '../../lib/kibana'; + +export const CaseSavedObjectNoPermissions = React.memo(() => { + const docLinks = useKibana().services.docLinks; + + return ( + + ); +}); + +CaseSavedObjectNoPermissions.displayName = 'CaseSavedObjectNoPermissions'; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/translations.ts index 8f9d2087699f8..0d1e6d1435ca3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/case/translations.ts @@ -6,6 +6,21 @@ import { i18n } from '@kbn/i18n'; +export const SAVED_OBJECT_NO_PERMISSIONS_TITLE = i18n.translate( + 'xpack.siem.case.caseSavedObjectNoPermissionsTitle', + { + defaultMessage: 'Kibana feature privileges required', + } +); + +export const SAVED_OBJECT_NO_PERMISSIONS_MSG = i18n.translate( + 'xpack.siem.case.caseSavedObjectNoPermissionsMessage', + { + defaultMessage: + 'To view cases, you must have privileges for the Saved Object Management feature in the Kibana space. For more information, contact your Kibana administrator.', + } +); + export const BACK_TO_ALL = i18n.translate('xpack.siem.case.caseView.backLabel', { defaultMessage: 'Back to cases', }); @@ -169,3 +184,10 @@ export const ADD_COMMENT_HELP_TEXT = i18n.translate( export const SAVE = i18n.translate('xpack.siem.case.caseView.description.save', { defaultMessage: 'Save', }); + +export const GO_TO_DOCUMENTATION = i18n.translate( + 'xpack.siem.case.caseView.goToDocumentationButton', + { + defaultMessage: 'View documentation', + } +); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/actions.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/actions.test.tsx new file mode 100644 index 0000000000000..8aaed08a0a0a1 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/actions.test.tsx @@ -0,0 +1,380 @@ +/* + * 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 sinon from 'sinon'; +import moment from 'moment'; + +import { sendSignalToTimelineAction, determineToAndFrom } from './actions'; +import { + mockEcsDataWithSignal, + defaultTimelineProps, + apolloClient, + mockTimelineApolloResult, +} from '../../../../mock/'; +import { CreateTimeline, UpdateTimelineLoading } from './types'; +import { Ecs } from '../../../../graphql/types'; + +jest.mock('apollo-client'); + +describe('signals actions', () => { + const anchor = '2020-03-01T17:59:46.349Z'; + const unix = moment(anchor).valueOf(); + let createTimeline: CreateTimeline; + let updateTimelineIsLoading: UpdateTimelineLoading; + let clock: sinon.SinonFakeTimers; + + beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 + jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); + + createTimeline = jest.fn() as jest.Mocked; + updateTimelineIsLoading = jest.fn() as jest.Mocked; + + jest.spyOn(apolloClient, 'query').mockResolvedValue(mockTimelineApolloResult); + + clock = sinon.useFakeTimers(unix); + }); + + afterEach(() => { + clock.restore(); + }); + + describe('sendSignalToTimelineAction', () => { + describe('timeline id is NOT empty string and apollo client exists', () => { + test('it invokes updateTimelineIsLoading to set to true', async () => { + await sendSignalToTimelineAction({ + apolloClient, + createTimeline, + ecsData: mockEcsDataWithSignal, + updateTimelineIsLoading, + }); + + expect(updateTimelineIsLoading).toHaveBeenCalledTimes(1); + expect(updateTimelineIsLoading).toHaveBeenCalledWith({ id: 'timeline-1', isLoading: true }); + }); + + test('it invokes createTimeline with designated timeline template if "timelineTemplate" exists', async () => { + await sendSignalToTimelineAction({ + apolloClient, + createTimeline, + ecsData: mockEcsDataWithSignal, + updateTimelineIsLoading, + }); + const expected = { + from: 1541444305937, + timeline: { + columns: [ + { + aggregatable: undefined, + category: undefined, + columnHeaderType: 'not-filtered', + description: undefined, + example: undefined, + id: '@timestamp', + placeholder: undefined, + type: undefined, + width: 190, + }, + { + aggregatable: undefined, + category: undefined, + columnHeaderType: 'not-filtered', + description: undefined, + example: undefined, + id: 'message', + placeholder: undefined, + type: undefined, + width: 180, + }, + { + aggregatable: undefined, + category: undefined, + columnHeaderType: 'not-filtered', + description: undefined, + example: undefined, + id: 'event.category', + placeholder: undefined, + type: undefined, + width: 180, + }, + { + aggregatable: undefined, + category: undefined, + columnHeaderType: 'not-filtered', + description: undefined, + example: undefined, + id: 'host.name', + placeholder: undefined, + type: undefined, + width: 180, + }, + { + aggregatable: undefined, + category: undefined, + columnHeaderType: 'not-filtered', + description: undefined, + example: undefined, + id: 'source.ip', + placeholder: undefined, + type: undefined, + width: 180, + }, + { + aggregatable: undefined, + category: undefined, + columnHeaderType: 'not-filtered', + description: undefined, + example: undefined, + id: 'destination.ip', + placeholder: undefined, + type: undefined, + width: 180, + }, + { + aggregatable: undefined, + category: undefined, + columnHeaderType: 'not-filtered', + description: undefined, + example: undefined, + id: 'user.name', + placeholder: undefined, + type: undefined, + width: 180, + }, + ], + dataProviders: [], + dateRange: { + end: 1541444605937, + start: 1541444305937, + }, + deletedEventIds: [], + description: 'This is a sample rule description', + eventIdToNoteIds: {}, + eventType: 'all', + filters: [ + { + $state: { + store: 'appState', + }, + meta: { + key: 'host.name', + negate: false, + params: { + query: 'apache', + }, + type: 'phrase', + }, + query: { + match_phrase: { + 'host.name': 'apache', + }, + }, + }, + ], + highlightedDropAndProviderId: '', + historyIds: [], + id: '', + isFavorite: false, + isLive: false, + isLoading: false, + isSaving: false, + isSelectAllChecked: false, + itemsPerPage: 25, + itemsPerPageOptions: [10, 25, 50, 100], + kqlMode: 'filter', + kqlQuery: { + filterQuery: { + kuery: { + expression: '', + kind: 'kuery', + }, + serializedQuery: '', + }, + filterQueryDraft: { + expression: '', + kind: 'kuery', + }, + }, + loadingEventIds: [], + noteIds: [], + pinnedEventIds: {}, + pinnedEventsSaveObject: {}, + savedObjectId: null, + selectedEventIds: {}, + show: true, + showCheckboxes: false, + showRowRenderers: true, + sort: { + columnId: '@timestamp', + sortDirection: 'desc', + }, + title: '', + version: null, + width: 1100, + }, + to: 1541444605937, + ruleNote: '# this is some markdown documentation', + }; + + expect(createTimeline).toHaveBeenCalledWith(expected); + }); + + test('it invokes createTimeline with kqlQuery.filterQuery.kuery.kind as "kuery" if not specified in returned timeline template', async () => { + const mockTimelineApolloResultModified = { + ...mockTimelineApolloResult, + kqlQuery: { + filterQuery: { + kuery: { + expression: [''], + }, + }, + filterQueryDraft: { + expression: [''], + }, + }, + }; + jest.spyOn(apolloClient, 'query').mockResolvedValue(mockTimelineApolloResultModified); + + await sendSignalToTimelineAction({ + apolloClient, + createTimeline, + ecsData: mockEcsDataWithSignal, + updateTimelineIsLoading, + }); + // @ts-ignore + const createTimelineArg = createTimeline.mock.calls[0][0]; + + expect(createTimeline).toHaveBeenCalledTimes(1); + expect(createTimelineArg.timeline.kqlQuery.filterQuery.kuery.kind).toEqual('kuery'); + }); + + test('it invokes createTimeline with kqlQuery.filterQueryDraft.kuery.kind as "kuery" if not specified in returned timeline template', async () => { + const mockTimelineApolloResultModified = { + ...mockTimelineApolloResult, + kqlQuery: { + filterQuery: { + kuery: { + expression: [''], + }, + }, + filterQueryDraft: { + expression: [''], + }, + }, + }; + jest.spyOn(apolloClient, 'query').mockResolvedValue(mockTimelineApolloResultModified); + + await sendSignalToTimelineAction({ + apolloClient, + createTimeline, + ecsData: mockEcsDataWithSignal, + updateTimelineIsLoading, + }); + // @ts-ignore + const createTimelineArg = createTimeline.mock.calls[0][0]; + + expect(createTimeline).toHaveBeenCalledTimes(1); + expect(createTimelineArg.timeline.kqlQuery.filterQueryDraft.kind).toEqual('kuery'); + }); + + test('it invokes createTimeline with default timeline if apolloClient throws', async () => { + jest.spyOn(apolloClient, 'query').mockImplementation(() => { + throw new Error('Test error'); + }); + + await sendSignalToTimelineAction({ + apolloClient, + createTimeline, + ecsData: mockEcsDataWithSignal, + updateTimelineIsLoading, + }); + + expect(updateTimelineIsLoading).toHaveBeenCalledWith({ id: 'timeline-1', isLoading: true }); + expect(updateTimelineIsLoading).toHaveBeenCalledWith({ + id: 'timeline-1', + isLoading: false, + }); + expect(createTimeline).toHaveBeenCalledTimes(1); + expect(createTimeline).toHaveBeenCalledWith(defaultTimelineProps); + }); + }); + + describe('timelineId is empty string', () => { + test('it invokes createTimeline with timelineDefaults', async () => { + const ecsDataMock: Ecs = { + ...mockEcsDataWithSignal, + signal: { + rule: { + ...mockEcsDataWithSignal.signal?.rule!, + timeline_id: null, + }, + }, + }; + + await sendSignalToTimelineAction({ + apolloClient, + createTimeline, + ecsData: ecsDataMock, + updateTimelineIsLoading, + }); + + expect(updateTimelineIsLoading).not.toHaveBeenCalled(); + expect(createTimeline).toHaveBeenCalledTimes(1); + expect(createTimeline).toHaveBeenCalledWith(defaultTimelineProps); + }); + }); + + describe('apolloClient is not defined', () => { + test('it invokes createTimeline with timelineDefaults', async () => { + const ecsDataMock: Ecs = { + ...mockEcsDataWithSignal, + signal: { + rule: { + ...mockEcsDataWithSignal.signal?.rule!, + timeline_id: [''], + }, + }, + }; + + await sendSignalToTimelineAction({ + createTimeline, + ecsData: ecsDataMock, + updateTimelineIsLoading, + }); + + expect(updateTimelineIsLoading).not.toHaveBeenCalled(); + expect(createTimeline).toHaveBeenCalledTimes(1); + expect(createTimeline).toHaveBeenCalledWith(defaultTimelineProps); + }); + }); + }); + + describe('determineToAndFrom', () => { + test('it uses ecs.Data.timestamp if one is provided', () => { + const ecsDataMock: Ecs = { + ...mockEcsDataWithSignal, + timestamp: '2020-03-20T17:59:46.349Z', + }; + const result = determineToAndFrom({ ecsData: ecsDataMock }); + + expect(result.from).toEqual(1584726886349); + expect(result.to).toEqual(1584727186349); + }); + + test('it uses current time timestamp if ecsData.timestamp is not provided', () => { + const { timestamp, ...ecsDataMock } = { + ...mockEcsDataWithSignal, + }; + const result = determineToAndFrom({ ecsData: ecsDataMock }); + + expect(result.from).toEqual(1583085286349); + expect(result.to).toEqual(1583085586349); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/actions.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/actions.tsx index b23b051e8b2e8..c71ede32d8403 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/actions.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/actions.tsx @@ -10,7 +10,7 @@ import moment from 'moment'; import { updateSignalStatus } from '../../../../containers/detection_engine/signals/api'; import { SendSignalToTimelineActionProps, UpdateSignalStatusActionProps } from './types'; -import { TimelineNonEcsData, GetOneTimeline, TimelineResult } from '../../../../graphql/types'; +import { TimelineNonEcsData, GetOneTimeline, TimelineResult, Ecs } from '../../../../graphql/types'; import { oneTimelineQuery } from '../../../../containers/timeline/one/index.gql_query'; import { omitTypenameInTimeline, @@ -72,16 +72,7 @@ export const updateSignalStatusAction = async ({ } }; -export const sendSignalToTimelineAction = async ({ - apolloClient, - createTimeline, - ecsData, - updateTimelineIsLoading, -}: SendSignalToTimelineActionProps) => { - let openSignalInBasicTimeline = true; - const timelineId = - ecsData.signal?.rule?.timeline_id != null ? ecsData.signal?.rule?.timeline_id[0] : ''; - +export const determineToAndFrom = ({ ecsData }: { ecsData: Ecs }) => { const ellapsedTimeRule = moment.duration( moment().diff( dateMath.parse(ecsData.signal?.rule?.from != null ? ecsData.signal?.rule?.from[0] : 'now-0s') @@ -93,6 +84,21 @@ export const sendSignalToTimelineAction = async ({ .valueOf(); const to = moment(ecsData.timestamp ?? new Date()).valueOf(); + return { to, from }; +}; + +export const sendSignalToTimelineAction = async ({ + apolloClient, + createTimeline, + ecsData, + updateTimelineIsLoading, +}: SendSignalToTimelineActionProps) => { + let openSignalInBasicTimeline = true; + const noteContent = ecsData.signal?.rule?.note != null ? ecsData.signal?.rule?.note[0] : ''; + const timelineId = + ecsData.signal?.rule?.timeline_id != null ? ecsData.signal?.rule?.timeline_id[0] : ''; + const { to, from } = determineToAndFrom({ ecsData }); + if (timelineId !== '' && apolloClient != null) { try { updateTimelineIsLoading({ id: 'timeline-1', isLoading: true }); @@ -106,10 +112,10 @@ export const sendSignalToTimelineAction = async ({ id: timelineId, }, }); - const timelineTemplate: TimelineResult = omitTypenameInTimeline( - getOr({}, 'data.getOneTimeline', responseTimeline) - ); - if (!isEmpty(timelineTemplate)) { + const resultingTimeline: TimelineResult = getOr({}, 'data.getOneTimeline', responseTimeline); + + if (!isEmpty(resultingTimeline)) { + const timelineTemplate: TimelineResult = omitTypenameInTimeline(resultingTimeline); openSignalInBasicTimeline = false; const { timeline } = formatTimelineResultToModel(timelineTemplate, true); const query = replaceTemplateFieldFromQuery( @@ -148,6 +154,7 @@ export const sendSignalToTimelineAction = async ({ show: true, }, to, + ruleNote: noteContent, }); } } catch { @@ -197,6 +204,7 @@ export const sendSignalToTimelineAction = async ({ }, }, to, + ruleNote: noteContent, }); } }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.test.tsx new file mode 100644 index 0000000000000..6212cad7e1845 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.test.tsx @@ -0,0 +1,193 @@ +/* + * 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 { mount, ReactWrapper } from 'enzyme'; +import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; + +import { Filter } from '../../../../../../../../../src/plugins/data/common/es_query'; +import { TimelineAction } from '../../../../components/timeline/body/actions'; +import { buildSignalsRuleIdFilter, getSignalsActions } from './default_config'; +import { + CreateTimeline, + SetEventsDeletedProps, + SetEventsLoadingProps, + UpdateTimelineLoading, +} from './types'; +import { mockEcsDataWithSignal } from '../../../../mock/mock_ecs'; +import { sendSignalToTimelineAction, updateSignalStatusAction } from './actions'; +import * as i18n from './translations'; + +jest.mock('./actions'); + +describe('signals default_config', () => { + describe('buildSignalsRuleIdFilter', () => { + test('given a rule id this will return an array with a single filter', () => { + const filters: Filter[] = buildSignalsRuleIdFilter('rule-id-1'); + const expectedFilter: Filter = { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'signal.rule.id', + params: { + query: 'rule-id-1', + }, + }, + query: { + match_phrase: { + 'signal.rule.id': 'rule-id-1', + }, + }, + }; + expect(filters).toHaveLength(1); + expect(filters[0]).toEqual(expectedFilter); + }); + }); + + describe('getSignalsActions', () => { + let setEventsLoading: ({ eventIds, isLoading }: SetEventsLoadingProps) => void; + let setEventsDeleted: ({ eventIds, isDeleted }: SetEventsDeletedProps) => void; + let createTimeline: CreateTimeline; + let updateTimelineIsLoading: UpdateTimelineLoading; + + beforeEach(() => { + setEventsLoading = jest.fn(); + setEventsDeleted = jest.fn(); + createTimeline = jest.fn(); + updateTimelineIsLoading = jest.fn(); + }); + + describe('timeline tooltip', () => { + test('it invokes sendSignalToTimelineAction when button clicked', () => { + const signalsActions = getSignalsActions({ + canUserCRUD: true, + hasIndexWrite: true, + setEventsLoading, + setEventsDeleted, + createTimeline, + status: 'open', + updateTimelineIsLoading, + }); + const timelineAction = signalsActions[0].getAction({ + eventId: 'even-id', + ecsData: mockEcsDataWithSignal, + }); + const wrapper = mount(timelineAction as React.ReactElement); + wrapper.find(EuiButtonIcon).simulate('click'); + + expect(sendSignalToTimelineAction).toHaveBeenCalled(); + }); + }); + + describe('signal open action', () => { + let signalsActions: TimelineAction[]; + let signalOpenAction: JSX.Element; + let wrapper: ReactWrapper; + + beforeEach(() => { + signalsActions = getSignalsActions({ + canUserCRUD: true, + hasIndexWrite: true, + setEventsLoading, + setEventsDeleted, + createTimeline, + status: 'open', + updateTimelineIsLoading, + }); + + signalOpenAction = signalsActions[1].getAction({ + eventId: 'event-id', + ecsData: mockEcsDataWithSignal, + }); + + wrapper = mount(signalOpenAction as React.ReactElement); + }); + + afterEach(() => { + wrapper.unmount(); + }); + + test('it invokes updateSignalStatusAction when button clicked', () => { + wrapper.find(EuiButtonIcon).simulate('click'); + + expect(updateSignalStatusAction).toHaveBeenCalledWith({ + signalIds: ['event-id'], + status: 'open', + setEventsLoading, + setEventsDeleted, + }); + }); + + test('it displays expected text on hover', () => { + const openSignal = wrapper.find(EuiToolTip); + openSignal.simulate('mouseOver'); + const tooltip = wrapper.find('.euiToolTipPopover').text(); + + expect(tooltip).toEqual(i18n.ACTION_OPEN_SIGNAL); + }); + + test('it displays expected icon', () => { + const icon = wrapper.find(EuiButtonIcon).props().iconType; + + expect(icon).toEqual('securitySignalDetected'); + }); + }); + + describe('signal close action', () => { + let signalsActions: TimelineAction[]; + let signalCloseAction: JSX.Element; + let wrapper: ReactWrapper; + + beforeEach(() => { + signalsActions = getSignalsActions({ + canUserCRUD: true, + hasIndexWrite: true, + setEventsLoading, + setEventsDeleted, + createTimeline, + status: 'closed', + updateTimelineIsLoading, + }); + + signalCloseAction = signalsActions[1].getAction({ + eventId: 'event-id', + ecsData: mockEcsDataWithSignal, + }); + + wrapper = mount(signalCloseAction as React.ReactElement); + }); + + afterEach(() => { + wrapper.unmount(); + }); + + test('it invokes updateSignalStatusAction when status button clicked', () => { + wrapper.find(EuiButtonIcon).simulate('click'); + + expect(updateSignalStatusAction).toHaveBeenCalledWith({ + signalIds: ['event-id'], + status: 'closed', + setEventsLoading, + setEventsDeleted, + }); + }); + + test('it displays expected text on hover', () => { + const closeSignal = wrapper.find(EuiToolTip); + closeSignal.simulate('mouseOver'); + const tooltip = wrapper.find('.euiToolTipPopover').text(); + expect(tooltip).toEqual(i18n.ACTION_CLOSE_SIGNAL); + }); + + test('it displays expected icon', () => { + const icon = wrapper.find(EuiButtonIcon).props().iconType; + + expect(icon).toEqual('securitySignalResolved'); + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx index 44c48b1879e89..fd3b9a6f68e82 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx @@ -23,7 +23,12 @@ import { timelineDefaults } from '../../../../store/timeline/defaults'; import { FILTER_OPEN } from './signals_filter_group'; import { sendSignalToTimelineAction, updateSignalStatusAction } from './actions'; import * as i18n from './translations'; -import { CreateTimeline, SetEventsDeletedProps, SetEventsLoadingProps } from './types'; +import { + CreateTimeline, + SetEventsDeletedProps, + SetEventsLoadingProps, + UpdateTimelineLoading, +} from './types'; export const signalsOpenFilters: Filter[] = [ { @@ -198,13 +203,13 @@ export const getSignalsActions = ({ setEventsDeleted: ({ eventIds, isDeleted }: SetEventsDeletedProps) => void; createTimeline: CreateTimeline; status: 'open' | 'closed'; - updateTimelineIsLoading: ({ id, isLoading }: { id: string; isLoading: boolean }) => void; + updateTimelineIsLoading: UpdateTimelineLoading; }): TimelineAction[] => [ { getAction: ({ ecsData }: TimelineActionProps): JSX.Element => ( { let localValueToChange = valueToChange; - if (keuryNode.function === 'is' && templateFields.includes(keuryNode.arguments[0].value)) { + if (kueryNode.function === 'is' && templateFields.includes(kueryNode.arguments[0].value)) { localValueToChange = [ ...localValueToChange, { - field: keuryNode.arguments[0].value, - valueToChange: keuryNode.arguments[1].value, + field: kueryNode.arguments[0].value, + valueToChange: kueryNode.arguments[1].value, }, ]; } - return keuryNode.arguments.reduce( + return kueryNode.arguments.reduce( (addValueToChange: FindValueToChangeInQuery[], ast: KueryNode) => { if (ast.function === 'is' && templateFields.includes(ast.arguments[0].value)) { return [ diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx index afd325f539966..6cdb2f326901e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx @@ -114,7 +114,7 @@ const SignalsTableComponent: React.FC = ({ // Callback for creating a new timeline -- utilized by row/batch actions const createTimelineCallback = useCallback( - ({ from: fromTimeline, timeline, to: toTimeline }: CreateTimelineProps) => { + ({ from: fromTimeline, timeline, to: toTimeline, ruleNote }: CreateTimelineProps) => { updateTimelineIsLoading({ id: 'timeline-1', isLoading: false }); updateTimeline({ duplicate: true, @@ -126,6 +126,7 @@ const SignalsTableComponent: React.FC = ({ show: true, }, to: toTimeline, + ruleNote, })(); }, [updateTimeline, updateTimelineIsLoading] diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/translations.ts index c2807db179780..f68dcd932bc32 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/translations.ts @@ -95,9 +95,9 @@ export const ACTION_CLOSE_SIGNAL = i18n.translate( } ); -export const ACTION_VIEW_IN_TIMELINE = i18n.translate( - 'xpack.siem.detectionEngine.signals.actions.viewInTimelineTitle', +export const ACTION_INVESTIGATE_IN_TIMELINE = i18n.translate( + 'xpack.siem.detectionEngine.signals.actions.investigateInTimelineTitle', { - defaultMessage: 'View in timeline', + defaultMessage: 'Investigate in timeline', } ); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts index b3e7ed75cfb99..909b217646746 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts @@ -45,13 +45,16 @@ export interface SendSignalToTimelineActionProps { apolloClient?: ApolloClient<{}>; createTimeline: CreateTimeline; ecsData: Ecs; - updateTimelineIsLoading: ({ id, isLoading }: { id: string; isLoading: boolean }) => void; + updateTimelineIsLoading: UpdateTimelineLoading; } +export type UpdateTimelineLoading = ({ id, isLoading }: { id: string; isLoading: boolean }) => void; + export interface CreateTimelineProps { from: number; timeline: TimelineModel; to: number; + ruleNote?: string; } export type CreateTimeline = ({ from, timeline, to }: CreateTimelineProps) => void; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts index 6d76fde49634d..5e0293325289b 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts @@ -71,7 +71,7 @@ export const mockRule = (id: string): Rule => ({ to: 'now', type: 'saved_query', threat: [], - throttle: null, + throttle: 'no_actions', note: '# this is some markdown documentation', version: 1, }); @@ -145,7 +145,7 @@ export const mockRuleWithEverything = (id: string): Rule => ({ ], }, ], - throttle: null, + throttle: 'no_actions', note: '# this is some markdown documentation', version: 1, }); @@ -184,7 +184,7 @@ export const mockActionsStepRule = (isNew = false, enabled = false): ActionsStep actions: [], kibanaSiemAppUrl: 'http://localhost:5601/app/siem', enabled, - throttle: null, + throttle: 'no_actions', }); export const mockDefineStepRule = (isNew = false): DefineStepRule => ({ diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx index ebdf0fc27b6c8..a155f3eb2803c 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx @@ -48,7 +48,6 @@ export const getActions = ( icon: 'controlsHorizontal', name: i18n.EDIT_RULE_SETTINGS, onClick: (rowItem: Rule) => editRuleAction(rowItem, history), - enabled: (rowItem: Rule) => !rowItem.immutable, }, { description: i18n.DUPLICATE_RULE, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx index 8458b32368525..4003b71b95d77 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx @@ -300,7 +300,9 @@ export const AllRules = React.memo( - {i18n.SHOWING_RULES(pagination.total ?? 0)} + + {i18n.SHOWING_RULES(pagination.total ?? 0)} + diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap index 9a534297e5e29..31abea53462fa 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap @@ -145,7 +145,7 @@ exports[`description_step StepRuleDescriptionComponent renders correctly against # this is some markdown documentation
, - "title": "Investigation notes", + "title": "Investigation guide", }, ] } @@ -287,7 +287,7 @@ exports[`description_step StepRuleDescriptionComponent renders correctly against # this is some markdown documentation
, - "title": "Investigation notes", + "title": "Investigation guide", }, ] } @@ -430,7 +430,7 @@ exports[`description_step StepRuleDescriptionComponent renders correctly against # this is some markdown documentation
, - "title": "Investigation notes", + "title": "Investigation guide", }, ] } diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx index 13cebd98ce9b1..f7785319142ad 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx @@ -21,13 +21,13 @@ import styled from 'styled-components'; import { esFilters } from '../../../../../../../../../../src/plugins/data/public'; +import { RuleType } from '../../../../../../common/detection_engine/types'; import { tacticsOptions, techniquesOptions } from '../../../mitre/mitre_tactics_techniques'; import * as i18n from './translations'; import { BuildQueryBarDescription, BuildThreatDescription, ListItems } from './types'; import { SeverityBadge } from '../severity_badge'; import ListTreeIcon from './assets/list_tree_icon.svg'; -import { RuleType } from '../../../../../containers/detection_engine/rules'; import { assertUnreachable } from '../../../../../lib/helpers'; const NoteDescriptionContainer = styled(EuiFlexItem)` diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx index a01aec0ccf2cf..8e8927cb7bbd1 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx @@ -461,12 +461,12 @@ describe('description_step', () => { test('returns default "note" description', () => { const result: ListItems[] = getDescriptionItem( 'note', - 'Investigation notes', + 'Investigation guide', mockAboutStep, mockFilterManager ); - expect(result[0].title).toEqual('Investigation notes'); + expect(result[0].title).toEqual('Investigation guide'); expect(React.isValidElement(result[0].description)).toBeTruthy(); }); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx index 69c4ee1017155..05e47225c8f4b 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx @@ -15,7 +15,7 @@ import { esFilters, FilterManager, } from '../../../../../../../../../../src/plugins/data/public'; -import { RuleType } from '../../../../../containers/detection_engine/rules'; +import { RuleType } from '../../../../../../common/detection_engine/types'; import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/translations'; import { useKibana } from '../../../../../lib/kibana'; import { IMitreEnterpriseAttack } from '../../types'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx index 947bf29c07148..8276aa3578563 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx @@ -11,8 +11,8 @@ import { EuiBadge, EuiIcon, EuiLink, EuiToolTip } from '@elastic/eui'; import { useKibana } from '../../../../../lib/kibana'; import { SiemJob } from '../../../../../components/ml_popover/types'; import { ListItems } from './types'; -import { isJobStarted } from '../../../../../components/ml/helpers'; import { ML_JOB_STARTED, ML_JOB_STOPPED } from './translations'; +import { isJobStarted } from '../../../../../../common/detection_engine/ml_helpers'; enum MessageLevels { info = 'info', diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx index 4ccde78f3cda7..9d3b37f1788fa 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx @@ -16,10 +16,10 @@ import { EuiText, } from '@elastic/eui'; +import { isMlRule } from '../../../../../../common/detection_engine/ml_helpers'; +import { RuleType } from '../../../../../../common/detection_engine/types'; import { FieldHook } from '../../../../../shared_imports'; -import { RuleType } from '../../../../../containers/detection_engine/rules/types'; import * as i18n from './translations'; -import { isMlRule } from '../../helpers'; const MlCardDescription = ({ hasValidLicense = false }: { hasValidLicense?: boolean }) => ( diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx index 8cb38b9dc7393..7c088c068c9b2 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx @@ -178,12 +178,12 @@ export const schema: FormSchema = { }, note: { type: FIELD_TYPES.TEXTAREA, - label: i18n.translate('xpack.siem.detectionEngine.createRule.stepAboutRule.noteLabel', { - defaultMessage: 'Investigation notes', + label: i18n.translate('xpack.siem.detectionEngine.createRule.stepAboutRule.guideLabel', { + defaultMessage: 'Investigation guide', }), - helpText: i18n.translate('xpack.siem.detectionEngine.createRule.stepAboutRule.noteHelpText', { + helpText: i18n.translate('xpack.siem.detectionEngine.createRule.stepAboutRule.guideHelpText', { defaultMessage: - 'Provide helpful information for analysts that are performing a signal investigation. These notes will appear on both the rule details page and in timelines created from signals generated by this rule.', + 'Provide helpful information for analysts that are performing a signal investigation. This guide will appear on both the rule details page and in timelines created from signals generated by this rule.', }), labelAppend: OptionalFieldLabel, }, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts index dfa60268e903a..0b1e712c663f3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts @@ -72,6 +72,6 @@ export const URL_FORMAT_INVALID = i18n.translate( export const ADD_RULE_NOTE_HELP_TEXT = i18n.translate( 'xpack.siem.detectionEngine.createRule.stepAboutrule.noteHelpText', { - defaultMessage: 'Add rule investigation notes...', + defaultMessage: 'Add rule investigation guide...', } ); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx index bbd037af10c3f..76a3c590a62a6 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx @@ -136,7 +136,7 @@ describe('StepAboutRuleToggleDetails', () => { expect(wrapper.find('EuiButtonGroup[idSelected="notes"]').exists()).toBeFalsy(); wrapper - .find('input[title="Investigation notes"]') + .find('input[title="Investigation guide"]') .at(0) .simulate('change', { target: { value: 'notes' } }); @@ -159,7 +159,7 @@ describe('StepAboutRuleToggleDetails', () => { ); wrapper - .find('input[title="Investigation notes"]') + .find('input[title="Investigation guide"]') .at(0) .simulate('change', { target: { value: 'notes' } }); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.ts index fa725366210de..79c5eb12d4663 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.ts @@ -20,8 +20,8 @@ export const ABOUT_TEXT = i18n.translate( ); export const ABOUT_PANEL_NOTES_TAB = i18n.translate( - 'xpack.siem.detectionEngine.details.stepAboutRule.investigationNotesLabel', + 'xpack.siem.detectionEngine.details.stepAboutRule.investigationGuideLabel', { - defaultMessage: 'Investigation notes', + defaultMessage: 'Investigation guide', } ); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx index 6c46ab0b171a2..05043e5b96a30 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx @@ -12,10 +12,11 @@ import deepEqual from 'fast-deep-equal'; import { IIndexPattern } from '../../../../../../../../../../src/plugins/data/public'; import { useFetchIndexPatterns } from '../../../../../containers/detection_engine/rules'; import { DEFAULT_INDEX_KEY } from '../../../../../../common/constants'; +import { isMlRule } from '../../../../../../common/detection_engine/ml_helpers'; import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/translations'; import { useMlCapabilities } from '../../../../../components/ml_popover/hooks/use_ml_capabilities'; import { useUiSetting$ } from '../../../../../lib/kibana'; -import { setFieldValue, isMlRule } from '../../helpers'; +import { setFieldValue } from '../../helpers'; import { DefineStepRule, RuleStep, RuleStepProps } from '../../types'; import { StepRuleDescription } from '../description_step'; import { QueryBarDefineRule } from '../query_bar'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx index 271c8fabed3a5..4a132f94a9871 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx @@ -10,6 +10,7 @@ import { isEmpty } from 'lodash/fp'; import React from 'react'; import { esKuery } from '../../../../../../../../../../src/plugins/data/public'; +import { isMlRule } from '../../../../../../common/detection_engine/ml_helpers'; import { FieldValueQueryBar } from '../query_bar'; import { ERROR_CODE, @@ -19,7 +20,6 @@ import { ValidationFunc, } from '../../../../../shared_imports'; import { CUSTOM_QUERY_REQUIRED, INVALID_CUSTOM_QUERY, INDEX_HELPER_TEXT } from './translations'; -import { isMlRule } from '../../helpers'; export const schema: FormSchema = { index: { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/index.tsx index 9c16a61822662..aec315938b6ae 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/index.tsx @@ -141,6 +141,7 @@ const StepRuleActionsComponent: FC = ({ /> )} + diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/schema.tsx index 511427978db3a..bc3b0dfe720bc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/schema.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/schema.tsx @@ -10,6 +10,7 @@ import { FormSchema } from '../../../../../shared_imports'; export const schema: FormSchema = { actions: {}, + enabled: {}, kibanaSiemAppUrl: {}, throttle: { label: i18n.translate( diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts index 212147ec6d4d8..efb601b6bd207 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts @@ -515,10 +515,9 @@ describe('helpers', () => { actions: [], enabled: false, meta: { - throttle: 'no_actions', kibanaSiemAppUrl: 'http://localhost:5601/app/siem', }, - throttle: null, + throttle: 'no_actions', }; expect(result).toEqual(expected); @@ -534,10 +533,9 @@ describe('helpers', () => { actions: [], enabled: false, meta: { - throttle: mockStepData.throttle, kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl, }, - throttle: null, + throttle: 'no_actions', }; expect(result).toEqual(expected); @@ -568,10 +566,9 @@ describe('helpers', () => { ], enabled: false, meta: { - throttle: mockStepData.throttle, kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl, }, - throttle: null, + throttle: 'rule', }; expect(result).toEqual(expected); @@ -602,7 +599,6 @@ describe('helpers', () => { ], enabled: false, meta: { - throttle: mockStepData.throttle, kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl, }, throttle: mockStepData.throttle, @@ -635,10 +631,9 @@ describe('helpers', () => { ], enabled: false, meta: { - throttle: null, kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl, }, - throttle: null, + throttle: 'no_actions', }; expect(result).toEqual(expected); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts index 7abe5a576c0e5..151e3a9bdf4d6 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts @@ -8,12 +8,11 @@ import { has, isEmpty } from 'lodash/fp'; import moment from 'moment'; import deepmerge from 'deepmerge'; -import { - NOTIFICATION_THROTTLE_RULE, - NOTIFICATION_THROTTLE_NO_ACTIONS, -} from '../../../../../common/constants'; -import { NewRule, RuleType } from '../../../../containers/detection_engine/rules'; +import { NOTIFICATION_THROTTLE_NO_ACTIONS } from '../../../../../common/constants'; import { transformAlertToRuleAction } from '../../../../../common/detection_engine/transform_actions'; +import { RuleType } from '../../../../../common/detection_engine/types'; +import { isMlRule } from '../../../../../common/detection_engine/ml_helpers'; +import { NewRule } from '../../../../containers/detection_engine/rules'; import { AboutStepRule, @@ -25,7 +24,6 @@ import { AboutStepRuleJson, ActionsStepRuleJson, } from '../types'; -import { isMlRule } from '../helpers'; export const getTimeTypeValue = (time: string): { unit: string; value: number } => { const timeObj = { @@ -144,11 +142,6 @@ export const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRule }; }; -export const getAlertThrottle = (throttle: string | null) => - throttle && ![NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].includes(throttle) - ? throttle - : null; - export const formatActionsStepData = (actionsStepData: ActionsStepRule): ActionsStepRuleJson => { const { actions = [], @@ -160,9 +153,8 @@ export const formatActionsStepData = (actionsStepData: ActionsStepRule): Actions return { actions: actions.map(transformAlertToRuleAction), enabled, - throttle: actions.length ? getAlertThrottle(throttle) : null, + throttle: actions.length ? throttle : NOTIFICATION_THROTTLE_NO_ACTIONS, meta: { - throttle: actions.length ? throttle : NOTIFICATION_THROTTLE_NO_ACTIONS, kibanaSiemAppUrl, }, }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx index a35caf4acf67b..b8e2310ef0614 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx @@ -277,7 +277,7 @@ const RuleDetailsPageComponent: FC = ({ {ruleI18n.EDIT_RULE_SETTINGS} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx index f89e3206cc67d..60d6158987a1d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx @@ -119,6 +119,7 @@ const EditRulePageComponent: FC = () => { { id: RuleStep.defineRule, name: ruleI18n.DEFINITION, + disabled: rule?.immutable, content: ( <> @@ -140,6 +141,7 @@ const EditRulePageComponent: FC = () => { { id: RuleStep.aboutRule, name: ruleI18n.ABOUT, + disabled: rule?.immutable, content: ( <> @@ -161,6 +163,7 @@ const EditRulePageComponent: FC = () => { { id: RuleStep.scheduleRule, name: ruleI18n.SCHEDULE, + disabled: rule?.immutable, content: ( <> @@ -203,6 +206,7 @@ const EditRulePageComponent: FC = () => { }, ], [ + rule, loading, initLoading, isLoading, @@ -331,10 +335,11 @@ const EditRulePageComponent: FC = () => { }, [rule]); useEffect(() => { - setSelectedTab(tabs[0]); - }, []); + const tabIndex = rule?.immutable ? 3 : 0; + setSelectedTab(tabs[tabIndex]); + }, [rule]); - if (isSaved || (rule != null && rule.immutable)) { + if (isSaved) { displaySuccessToast(i18n.SUCCESSFULLY_SAVED_RULE(rule?.name ?? ''), dispatchToaster); return ; } diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx index fbdfcf4fc75d8..522464d585cca 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx @@ -107,7 +107,12 @@ describe('rule helpers', () => { ], }; const scheduleRuleStepData = { from: '0s', interval: '5m', isNew: false }; - const ruleActionsStepData = { enabled: true, throttle: undefined, isNew: false, actions: [] }; + const ruleActionsStepData = { + enabled: true, + throttle: 'no_actions', + isNew: false, + actions: [], + }; const aboutRuleDataDetailsData = { note: '# this is some markdown documentation', description: '24/7', @@ -303,7 +308,7 @@ describe('rule helpers', () => { actions: [], enabled: mockedRule.enabled, isNew: false, - throttle: undefined, + throttle: 'no_actions', }; expect(result).toEqual(expected); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx index 50b76552ddc8f..b6afba527ccdc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx @@ -10,10 +10,11 @@ import moment from 'moment'; import memoizeOne from 'memoize-one'; import { useLocation } from 'react-router-dom'; -import { RuleAlertAction } from '../../../../common/detection_engine/types'; +import { RuleAlertAction, RuleType } from '../../../../common/detection_engine/types'; +import { isMlRule } from '../../../../common/detection_engine/ml_helpers'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; import { Filter } from '../../../../../../../../src/plugins/data/public'; -import { Rule, RuleType } from '../../../containers/detection_engine/rules'; +import { Rule } from '../../../containers/detection_engine/rules'; import { FormData, FormHook, FormSchema } from '../../../shared_imports'; import { AboutStepRule, @@ -57,12 +58,12 @@ export const getStepsData = ({ export const getActionsStepsData = ( rule: Omit & { actions: RuleAlertAction[] } ): ActionsStepRule => { - const { enabled, actions = [], meta } = rule; + const { enabled, throttle, meta, actions = [] } = rule; return { actions: actions?.map(transformRuleToAlertAction), isNew: false, - throttle: meta?.throttle, + throttle, kibanaSiemAppUrl: meta?.kibanaSiemAppUrl, enabled, }; @@ -214,8 +215,6 @@ export const setFieldValue = ( } }); -export const isMlRule = (ruleType: RuleType) => ruleType === 'machine_learning'; - export const redirectToDetections = ( isSignalIndexExists: boolean | null, isAuthenticated: boolean | null, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts index c1db24991c17c..1c366e6640b29 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts @@ -5,9 +5,8 @@ */ import { AlertAction } from '../../../../../../../plugins/alerting/common'; -import { RuleAlertAction } from '../../../../common/detection_engine/types'; +import { RuleAlertAction, RuleType } from '../../../../common/detection_engine/types'; import { Filter } from '../../../../../../../../src/plugins/data/common'; -import { RuleType } from '../../../containers/detection_engine/rules/types'; import { FieldValueQueryBar } from './components/query_bar'; import { FormData, FormHook } from '../../../shared_imports'; import { FieldValueTimeline } from './components/pick_timeline'; diff --git a/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.test.tsx b/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.test.tsx new file mode 100644 index 0000000000000..62399891c9606 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.test.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 { TimelinesPageComponent } from './timelines_page'; +import { useKibana } from '../../lib/kibana'; +import { shallow, ShallowWrapper } from 'enzyme'; +import React from 'react'; +import ApolloClient from 'apollo-client'; + +jest.mock('../../lib/kibana', () => { + return { + useKibana: jest.fn(), + }; +}); +describe('TimelinesPageComponent', () => { + const mockAppollloClient = {} as ApolloClient; + let wrapper: ShallowWrapper; + + describe('If the user is authorised', () => { + beforeAll(() => { + ((useKibana as unknown) as jest.Mock).mockReturnValue({ + services: { + application: { + capabilities: { + siem: { + crud: true, + }, + }, + }, + }, + }); + wrapper = shallow(); + }); + + afterAll(() => { + ((useKibana as unknown) as jest.Mock).mockReset(); + }); + + test('should not show the import timeline modal by default', () => { + expect( + wrapper.find('[data-test-subj="stateful-open-timeline"]').prop('importDataModalToggle') + ).toEqual(false); + }); + + test('should show the import timeline button', () => { + expect(wrapper.find('[data-test-subj="open-import-data-modal-btn"]').exists()).toEqual(true); + }); + + test('should show the import timeline modal after user clicking on the button', () => { + wrapper.find('[data-test-subj="open-import-data-modal-btn"]').simulate('click'); + expect( + wrapper.find('[data-test-subj="stateful-open-timeline"]').prop('importDataModalToggle') + ).toEqual(true); + }); + }); + + describe('If the user is not authorised', () => { + beforeAll(() => { + ((useKibana as unknown) as jest.Mock).mockReturnValue({ + services: { + application: { + capabilities: { + siem: { + crud: false, + }, + }, + }, + }, + }); + wrapper = shallow(); + }); + + afterAll(() => { + ((useKibana as unknown) as jest.Mock).mockReset(); + }); + test('should not show the import timeline modal by default', () => { + expect( + wrapper.find('[data-test-subj="stateful-open-timeline"]').prop('importDataModalToggle') + ).toEqual(false); + }); + + test('should not show the import timeline button', () => { + expect(wrapper.find('[data-test-subj="open-import-data-modal-btn"]').exists()).toEqual(false); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx b/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx index 38462e6526454..73070d2b94aac 100644 --- a/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx @@ -14,6 +14,7 @@ import { StatefulOpenTimeline } from '../../components/open_timeline'; import { WrapperPage } from '../../components/wrapper_page'; import { SpyRoute } from '../../utils/route/spy_routes'; import * as i18n from './translations'; +import { useKibana } from '../../lib/kibana'; const TimelinesContainer = styled.div` width: 100%; @@ -27,18 +28,29 @@ type OwnProps = TimelinesProps; export const DEFAULT_SEARCH_RESULTS_PER_PAGE = 10; -const TimelinesPageComponent: React.FC = ({ apolloClient }) => { - const [importCompleteToggle, setImportCompleteToggle] = useState(false); +export const TimelinesPageComponent: React.FC = ({ apolloClient }) => { + const [importDataModalToggle, setImportDataModalToggle] = useState(false); const onImportTimelineBtnClick = useCallback(() => { - setImportCompleteToggle(true); - }, [setImportCompleteToggle]); + setImportDataModalToggle(true); + }, [setImportDataModalToggle]); + + const uiCapabilities = useKibana().services.application.capabilities; + const capabilitiesCanUserCRUD: boolean = + typeof uiCapabilities.siem.crud === 'boolean' ? uiCapabilities.siem.crud : false; + return ( <> - - {i18n.ALL_TIMELINES_IMPORT_TIMELINE_TITLE} - + {capabilitiesCanUserCRUD && ( + + {i18n.ALL_TIMELINES_IMPORT_TIMELINE_TITLE} + + )} @@ -46,9 +58,10 @@ const TimelinesPageComponent: React.FC = ({ apolloClient }) => { apolloClient={apolloClient} defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE} isModal={false} - importCompleteToggle={importCompleteToggle} - setImportCompleteToggle={setImportCompleteToggle} + importDataModalToggle={importDataModalToggle && capabilitiesCanUserCRUD} + setImportDataModalToggle={setImportDataModalToggle} title={i18n.ALL_TIMELINES_PANEL_TITLE} + data-test-subj="stateful-open-timeline" /> diff --git a/x-pack/legacy/plugins/siem/scripts/optimize_tsconfig/tsconfig.json b/x-pack/legacy/plugins/siem/scripts/optimize_tsconfig/tsconfig.json index bec6988bdebd9..c4705c8b8c16a 100644 --- a/x-pack/legacy/plugins/siem/scripts/optimize_tsconfig/tsconfig.json +++ b/x-pack/legacy/plugins/siem/scripts/optimize_tsconfig/tsconfig.json @@ -4,7 +4,8 @@ "plugins/siem/**/*", "legacy/plugins/siem/**/*", "plugins/apm/typings/numeral.d.ts", - "legacy/plugins/canvas/types/webpack.d.ts" + "legacy/plugins/canvas/types/webpack.d.ts", + "plugins/triggers_actions_ui/**/*" ], "exclude": [ "test/**/*", diff --git a/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts index f897236b3470e..9bf55cfe1ed2a 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts @@ -410,6 +410,7 @@ export const ecsSchema = gql` created_by: ToStringArray updated_by: ToStringArray version: ToStringArray + note: ToStringArray } type SignalField { diff --git a/x-pack/legacy/plugins/siem/server/graphql/types.ts b/x-pack/legacy/plugins/siem/server/graphql/types.ts index e2b365f8bfa5b..d272b7ff59b79 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/types.ts @@ -1014,6 +1014,8 @@ export interface RuleField { updated_by?: Maybe; version?: Maybe; + + note?: Maybe; } export interface SuricataEcsFields { @@ -4822,6 +4824,8 @@ export namespace RuleFieldResolvers { updated_by?: UpdatedByResolver, TypeParent, TContext>; version?: VersionResolver, TypeParent, TContext>; + + note?: NoteResolver, TypeParent, TContext>; } export type IdResolver< @@ -4974,6 +4978,11 @@ export namespace RuleFieldResolvers { Parent = RuleField, TContext = SiemContext > = Resolver; + export type NoteResolver< + R = Maybe, + Parent = RuleField, + TContext = SiemContext + > = Resolver; } export namespace SuricataEcsFieldsResolvers { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/types.ts index edcd821353bc8..128a7965cd7dc 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/types.ts @@ -49,7 +49,7 @@ export type UpdateNotificationParams = Omit { try { const clusterClient = context.core.elasticsearch.dataClient; - const siemClient = context.siem.getSiemClient(); + const siemClient = context.siem?.getSiemClient(); const callCluster = clusterClient.callAsCurrentUser; + if (!siemClient) { + return siemResponse.error({ statusCode: 404 }); + } + const index = siemClient.signalsIndex; const indexExists = await getIndexExists(callCluster, index); if (indexExists) { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/delete_index_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/delete_index_route.ts index aa418c11d9d16..c667e7ae9c463 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/delete_index_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/delete_index_route.ts @@ -38,7 +38,11 @@ export const deleteIndexRoute = (router: IRouter) => { try { const clusterClient = context.core.elasticsearch.dataClient; - const siemClient = context.siem.getSiemClient(); + const siemClient = context.siem?.getSiemClient(); + + if (!siemClient) { + return siemResponse.error({ statusCode: 404 }); + } const callCluster = clusterClient.callAsCurrentUser; const index = siemClient.signalsIndex; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts index 4fc5a4e1f347f..047176f155611 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts @@ -23,7 +23,11 @@ export const readIndexRoute = (router: IRouter) => { try { const clusterClient = context.core.elasticsearch.dataClient; - const siemClient = context.siem.getSiemClient(); + const siemClient = context.siem?.getSiemClient(); + + if (!siemClient) { + return siemResponse.error({ statusCode: 404 }); + } const index = siemClient.signalsIndex; const indexExists = await getIndexExists(clusterClient.callAsCurrentUser, index); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts index aa4f6150889f9..3209f5ce9f519 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts @@ -62,6 +62,13 @@ describe('read_privileges route', () => { expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', status_code: 500 }); }); + + it('returns 404 if siem client is unavailable', async () => { + const { siem, ...contextWithoutSiem } = context; + const response = await server.inject(getPrivilegeRequest(), contextWithoutSiem); + expect(response.status).toEqual(404); + expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); + }); }); describe('when security plugin is disabled', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts index 2f5ea4d1ec767..d86880de65386 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts @@ -27,9 +27,14 @@ export const readPrivilegesRoute = ( }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); + try { const clusterClient = context.core.elasticsearch.dataClient; - const siemClient = context.siem.getSiemClient(); + const siemClient = context.siem?.getSiemClient(); + + if (!siemClient) { + return siemResponse.error({ statusCode: 404 }); + } const index = siemClient.signalsIndex; const clusterPrivileges = await readPrivileges(clusterClient.callAsCurrentUser, index); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts index f53efc8a3234d..f0b975379388f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts @@ -63,7 +63,7 @@ describe('add_prepackaged_rules_route', () => { addPrepackedRulesRoute(server.router); }); - describe('status codes with actionClient and alertClient', () => { + describe('status codes', () => { test('returns 200 when creating with a valid actionClient and alertClient', async () => { const request = addPrepackagedRulesRequest(); const response = await server.inject(request, context); @@ -96,6 +96,13 @@ describe('add_prepackaged_rules_route', () => { ), }); }); + + it('returns 404 if siem client is unavailable', async () => { + const { siem, ...contextWithoutSiem } = context; + const response = await server.inject(addPrepackagedRulesRequest(), contextWithoutSiem); + expect(response.status).toEqual(404); + expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); + }); }); describe('responses', () => { 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 index 4e08188af0d12..3eba04debb21f 100644 --- 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 @@ -33,16 +33,13 @@ export const addPrepackedRulesRoute = (router: IRouter) => { const siemResponse = buildSiemResponse(response); try { - if (!context.alerting || !context.actions) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); - const actionsClient = context.actions.getActionsClient(); + const alertsClient = context.alerting?.getAlertsClient(); + const actionsClient = context.actions?.getActionsClient(); const clusterClient = context.core.elasticsearch.dataClient; const savedObjectsClient = context.core.savedObjects.client; - const siemClient = context.siem.getSiemClient(); + const siemClient = context.siem?.getSiemClient(); - if (!actionsClient || !alertsClient) { + if (!siemClient || !actionsClient || !alertsClient) { return siemResponse.error({ statusCode: 404 }); } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts index 32b8eca298229..e6facf6f3b7a8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts @@ -42,7 +42,7 @@ describe('create_rules_bulk', () => { createRulesBulkRoute(server.router); }); - describe('status codes with actionClient and alertClient', () => { + describe('status codes', () => { test('returns 200 when creating a single rule with a valid actionClient and alertClient', async () => { const response = await server.inject(getReadBulkRequest(), context); expect(response.status).toEqual(200); @@ -54,6 +54,13 @@ describe('create_rules_bulk', () => { expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); }); + + it('returns 404 if siem client is unavailable', async () => { + const { siem, ...contextWithoutSiem } = context; + const response = await server.inject(getReadBulkRequest(), contextWithoutSiem); + expect(response.status).toEqual(404); + expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); + }); }); describe('unhappy paths', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index 1ca9f7ef9075e..d0e36515946a8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -23,6 +23,7 @@ import { } from '../utils'; import { createRulesBulkSchema } from '../schemas/create_rules_bulk_schema'; import { rulesBulkSchema } from '../schemas/response/rules_bulk_schema'; +import { updateRulesNotifications } from '../../rules/update_rules_notifications'; export const createRulesBulkRoute = (router: IRouter) => { router.post( @@ -37,15 +38,13 @@ export const createRulesBulkRoute = (router: IRouter) => { }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - if (!context.alerting || !context.actions) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); - const actionsClient = context.actions.getActionsClient(); + const alertsClient = context.alerting?.getAlertsClient(); + const actionsClient = context.actions?.getActionsClient(); const clusterClient = context.core.elasticsearch.dataClient; - const siemClient = context.siem.getSiemClient(); + const savedObjectsClient = context.core.savedObjects.client; + const siemClient = context.siem?.getSiemClient(); - if (!actionsClient || !alertsClient) { + if (!siemClient || !actionsClient || !alertsClient) { return siemResponse.error({ statusCode: 404 }); } @@ -115,7 +114,6 @@ export const createRulesBulkRoute = (router: IRouter) => { const createdRule = await createRules({ alertsClient, actionsClient, - actions, anomalyThreshold, description, enabled, @@ -139,7 +137,6 @@ export const createRulesBulkRoute = (router: IRouter) => { name, severity, tags, - throttle, to, type, threat, @@ -148,7 +145,18 @@ export const createRulesBulkRoute = (router: IRouter) => { version, lists, }); - return transformValidateBulkError(ruleIdOrUuid, createdRule); + + const ruleActions = await updateRulesNotifications({ + ruleAlertId: createdRule.id, + alertsClient, + savedObjectsClient, + enabled, + actions, + throttle, + name, + }); + + return transformValidateBulkError(ruleIdOrUuid, createdRule, ruleActions); } catch (err) { return transformBulkError(ruleIdOrUuid, err); } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts index 4da879d12f809..f15f47432f838 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts @@ -15,13 +15,12 @@ import { getEmptyIndex, getFindResultWithSingleHit, createMlRuleRequest, - createRuleWithActionsRequest, } from '../__mocks__/request_responses'; import { requestContextMock, serverMock, requestMock } from '../__mocks__'; import { createRulesRoute } from './create_rules_route'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../feature_flags'; -import { createNotifications } from '../../notifications/create_notifications'; -jest.mock('../../notifications/create_notifications'); +import { updateRulesNotifications } from '../../rules/update_rules_notifications'; +jest.mock('../../rules/update_rules_notifications'); describe('create_rules', () => { let server: ReturnType; @@ -49,6 +48,12 @@ describe('create_rules', () => { describe('status codes with actionClient and alertClient', () => { test('returns 200 when creating a single rule with a valid actionClient and alertClient', async () => { + (updateRulesNotifications as jest.Mock).mockResolvedValue({ + id: 'id', + actions: [], + alertThrottle: null, + ruleThrottle: 'no_actions', + }); const response = await server.inject(getCreateRequest(), context); expect(response.status).toEqual(200); }); @@ -60,6 +65,13 @@ describe('create_rules', () => { expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); }); + it('returns 404 if siem client is unavailable', async () => { + const { siem, ...contextWithoutSiem } = context; + const response = await server.inject(getCreateRequest(), contextWithoutSiem); + expect(response.status).toEqual(404); + expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); + }); + it('returns 200 if license is not platinum', async () => { (context.licensing.license.hasAtLeast as jest.Mock).mockReturnValue(false); @@ -86,18 +98,6 @@ describe('create_rules', () => { }); }); - describe('creating a Notification if throttle and actions were provided ', () => { - it('is successful', async () => { - const response = await server.inject(createRuleWithActionsRequest(), context); - expect(response.status).toEqual(200); - expect(createNotifications).toHaveBeenCalledWith( - expect.objectContaining({ - ruleAlertId: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', - }) - ); - }); - }); - describe('unhappy paths', () => { test('it returns a 400 if the index does not exist', async () => { clients.clusterClient.callAsCurrentUser.mockResolvedValue(getEmptyIndex()); 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 edf37bcb8dbe7..6038ad2095323 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 @@ -22,7 +22,7 @@ import { buildSiemResponse, validateLicenseForRuleType, } from '../utils'; -import { createNotifications } from '../../notifications/create_notifications'; +import { updateRulesNotifications } from '../../rules/update_rules_notifications'; export const createRulesRoute = (router: IRouter): void => { router.post( @@ -72,16 +72,13 @@ export const createRulesRoute = (router: IRouter): void => { try { validateLicenseForRuleType({ license: context.licensing.license, ruleType: type }); - if (!context.alerting || !context.actions) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); - const actionsClient = context.actions.getActionsClient(); + const alertsClient = context.alerting?.getAlertsClient(); + const actionsClient = context.actions?.getActionsClient(); const clusterClient = context.core.elasticsearch.dataClient; const savedObjectsClient = context.core.savedObjects.client; - const siemClient = context.siem.getSiemClient(); + const siemClient = context.siem?.getSiemClient(); - if (!actionsClient || !alertsClient) { + if (!siemClient || !actionsClient || !alertsClient) { return siemResponse.error({ statusCode: 404 }); } @@ -105,7 +102,6 @@ export const createRulesRoute = (router: IRouter): void => { const createdRule = await createRules({ alertsClient, actionsClient, - actions, anomalyThreshold, description, enabled, @@ -129,7 +125,6 @@ export const createRulesRoute = (router: IRouter): void => { name, severity, tags, - throttle, to, type, threat, @@ -139,16 +134,15 @@ export const createRulesRoute = (router: IRouter): void => { lists, }); - if (throttle && actions.length) { - await createNotifications({ - alertsClient, - enabled, - name, - interval, - actions, - ruleAlertId: createdRule.id, - }); - } + const ruleActions = await updateRulesNotifications({ + ruleAlertId: createdRule.id, + alertsClient, + savedObjectsClient, + enabled, + actions, + throttle, + name, + }); const ruleStatuses = await savedObjectsClient.find< IRuleSavedAttributesSavedObjectAttributes @@ -160,7 +154,11 @@ export const createRulesRoute = (router: IRouter): void => { search: `${createdRule.id}`, searchFields: ['alertId'], }); - const [validated, errors] = transformValidate(createdRule, ruleStatuses.saved_objects[0]); + const [validated, errors] = transformValidate( + createdRule, + ruleActions, + ruleStatuses.saved_objects[0] + ); if (errors != null) { return siemResponse.error({ statusCode: 500, body: errors }); } else { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts index 85cfeefdceead..0c5ad2e060924 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts @@ -18,6 +18,7 @@ import { import { deleteRules } from '../../rules/delete_rules'; import { deleteNotifications } from '../../notifications/delete_notifications'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; +import { deleteRuleActionsSavedObject } from '../../rule_actions/delete_rule_actions_saved_object'; type Config = RouteConfig; type Handler = RequestHandler; @@ -35,11 +36,8 @@ export const deleteRulesBulkRoute = (router: IRouter) => { const handler: Handler = async (context, request, response) => { const siemResponse = buildSiemResponse(response); - if (!context.alerting || !context.actions) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); - const actionsClient = context.actions.getActionsClient(); + const alertsClient = context.alerting?.getAlertsClient(); + const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; if (!actionsClient || !alertsClient) { @@ -59,6 +57,10 @@ export const deleteRulesBulkRoute = (router: IRouter) => { }); if (rule != null) { await deleteNotifications({ alertsClient, ruleAlertId: rule.id }); + await deleteRuleActionsSavedObject({ + ruleAlertId: rule.id, + savedObjectsClient, + }); const ruleStatuses = await savedObjectsClient.find< IRuleSavedAttributesSavedObjectAttributes >({ @@ -70,7 +72,7 @@ export const deleteRulesBulkRoute = (router: IRouter) => { ruleStatuses.saved_objects.forEach(async obj => savedObjectsClient.delete(ruleStatusSavedObjectType, obj.id) ); - return transformValidateBulkError(idOrRuleIdOrUnknown, rule, ruleStatuses); + return transformValidateBulkError(idOrRuleIdOrUnknown, rule, undefined, ruleStatuses); } else { return getIdBulkError({ id, ruleId }); } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts index 6fd50abd9364a..71724e3ba9b58 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts @@ -17,6 +17,7 @@ import { } from '../../rules/types'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; import { deleteNotifications } from '../../notifications/delete_notifications'; +import { deleteRuleActionsSavedObject } from '../../rule_actions/delete_rule_actions_saved_object'; export const deleteRulesRoute = (router: IRouter) => { router.delete( @@ -34,12 +35,9 @@ export const deleteRulesRoute = (router: IRouter) => { try { const { id, rule_id: ruleId } = request.query; - if (!context.alerting || !context.actions) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); - const actionsClient = context.actions.getActionsClient(); + const alertsClient = context.alerting?.getAlertsClient(); + const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; if (!actionsClient || !alertsClient) { @@ -54,6 +52,10 @@ export const deleteRulesRoute = (router: IRouter) => { }); if (rule != null) { await deleteNotifications({ alertsClient, ruleAlertId: rule.id }); + await deleteRuleActionsSavedObject({ + ruleAlertId: rule.id, + savedObjectsClient, + }); const ruleStatuses = await savedObjectsClient.find< IRuleSavedAttributesSavedObjectAttributes >({ @@ -65,7 +67,11 @@ export const deleteRulesRoute = (router: IRouter) => { ruleStatuses.saved_objects.forEach(async obj => savedObjectsClient.delete(ruleStatusSavedObjectType, obj.id) ); - const [validated, errors] = transformValidate(rule, ruleStatuses.saved_objects[0]); + const [validated, errors] = transformValidate( + rule, + undefined, + ruleStatuses.saved_objects[0] + ); if (errors != null) { return siemResponse.error({ statusCode: 500, body: errors }); } else { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/export_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/export_rules_route.ts index c434f42780e47..50eafe163c265 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/export_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/export_rules_route.ts @@ -28,10 +28,7 @@ export const exportRulesRoute = (router: IRouter, config: LegacyServices['config }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - if (!context.alerting) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); + const alertsClient = context.alerting?.getAlertsClient(); if (!alertsClient) { return siemResponse.error({ statusCode: 404 }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.ts index 961859417ef1b..85555c1a57084 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.ts @@ -15,6 +15,7 @@ import { findRulesSchema } from '../schemas/find_rules_schema'; import { transformValidateFindAlerts } from './validate'; import { buildRouteValidation, transformError, buildSiemResponse } from '../utils'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; +import { getRuleActionsSavedObject } from '../../rule_actions/get_rule_actions_saved_object'; export const findRulesRoute = (router: IRouter) => { router.get( @@ -32,10 +33,7 @@ export const findRulesRoute = (router: IRouter) => { try { const { query } = request; - if (!context.alerting) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); + const alertsClient = context.alerting?.getAlertsClient(); const savedObjectsClient = context.core.savedObjects.client; if (!alertsClient) { @@ -65,7 +63,18 @@ export const findRulesRoute = (router: IRouter) => { return results; }) ); - const [validated, errors] = transformValidateFindAlerts(rules, ruleStatuses); + const ruleActions = await Promise.all( + rules.data.map(async rule => { + const results = await getRuleActionsSavedObject({ + savedObjectsClient, + ruleAlertId: rule.id, + }); + + return results; + }) + ); + + const [validated, errors] = transformValidateFindAlerts(rules, ruleActions, ruleStatuses); if (errors != null) { return siemResponse.error({ statusCode: 500, body: errors }); } else { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts index 4f4ae7c2c1fa6..6fee4d71a904e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts @@ -35,10 +35,7 @@ export const findRulesStatusesRoute = (router: IRouter) => { async (context, request, response) => { const { query } = request; const siemResponse = buildSiemResponse(response); - if (!context.alerting) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); + const alertsClient = context.alerting?.getAlertsClient(); const savedObjectsClient = context.core.savedObjects.client; if (!alertsClient) { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts index 7e16b4495593e..7f0bf4bf81179 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts @@ -29,10 +29,7 @@ export const getPrepackagedRulesStatusRoute = (router: IRouter) => { }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - if (!context.alerting) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); + const alertsClient = context.alerting?.getAlertsClient(); if (!alertsClient) { return siemResponse.error({ statusCode: 404 }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts index aacf83b9ec58a..61f5e6faf1bdb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts @@ -101,6 +101,13 @@ describe('import_rules_route', () => { expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); }); + + it('returns 404 if siem client is unavailable', async () => { + const { siem, ...contextWithoutSiem } = context; + const response = await server.inject(request, contextWithoutSiem); + expect(response.status).toEqual(404); + expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); + }); }); describe('unhappy paths', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts index 2e6c72a87ec7f..43e970702ba72 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -57,30 +57,27 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config async (context, request, response) => { const siemResponse = buildSiemResponse(response); - if (!context.alerting || !context.actions) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); - const actionsClient = context.actions.getActionsClient(); - const clusterClient = context.core.elasticsearch.dataClient; - const savedObjectsClient = context.core.savedObjects.client; - const siemClient = context.siem.getSiemClient(); + try { + const alertsClient = context.alerting?.getAlertsClient(); + const actionsClient = context.actions?.getActionsClient(); + const clusterClient = context.core.elasticsearch.dataClient; + const savedObjectsClient = context.core.savedObjects.client; + const siemClient = context.siem?.getSiemClient(); - if (!actionsClient || !alertsClient) { - return siemResponse.error({ statusCode: 404 }); - } + if (!siemClient || !actionsClient || !alertsClient) { + return siemResponse.error({ statusCode: 404 }); + } - const { filename } = request.body.file.hapi; - const fileExtension = extname(filename).toLowerCase(); - if (fileExtension !== '.ndjson') { - return siemResponse.error({ - statusCode: 400, - body: `Invalid file extension ${fileExtension}`, - }); - } + const { filename } = request.body.file.hapi; + const fileExtension = extname(filename).toLowerCase(); + if (fileExtension !== '.ndjson') { + return siemResponse.error({ + statusCode: 400, + body: `Invalid file extension ${fileExtension}`, + }); + } - const objectLimit = config().get('savedObjects.maxImportExportSize'); - try { + const objectLimit = config().get('savedObjects.maxImportExportSize'); const readStream = createRulesStreamFromNdJson(objectLimit); const parsedObjects = await createPromiseFromStreams([ request.body.file, @@ -112,7 +109,6 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config return null; } const { - actions, anomaly_threshold: anomalyThreshold, description, enabled, @@ -135,7 +131,6 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config severity, tags, threat, - throttle, to, type, references, @@ -171,7 +166,6 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config await createRules({ alertsClient, actionsClient, - actions, anomalyThreshold, description, enabled, @@ -198,7 +192,6 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config to, type, threat, - throttle, references, note, version, @@ -209,7 +202,6 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config await patchRules({ alertsClient, actionsClient, - actions, savedObjectsClient, description, enabled, @@ -236,7 +228,6 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config to, type, threat, - throttle, references, note, version, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 645dbdadf8cab..85255594ee480 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -22,6 +22,7 @@ import { patchRulesBulkSchema } from '../schemas/patch_rules_bulk_schema'; import { rulesBulkSchema } from '../schemas/response/rules_bulk_schema'; import { patchRules } from '../../rules/patch_rules'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; +import { updateRulesNotifications } from '../../rules/update_rules_notifications'; export const patchRulesBulkRoute = (router: IRouter) => { router.patch( @@ -37,11 +38,8 @@ export const patchRulesBulkRoute = (router: IRouter) => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); - if (!context.alerting || !context.actions) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); - const actionsClient = context.actions.getActionsClient(); + const alertsClient = context.alerting?.getAlertsClient(); + const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; if (!actionsClient || !alertsClient) { @@ -92,7 +90,6 @@ export const patchRulesBulkRoute = (router: IRouter) => { const rule = await patchRules({ alertsClient, actionsClient, - actions, description, enabled, falsePositives, @@ -118,7 +115,6 @@ export const patchRulesBulkRoute = (router: IRouter) => { to, type, threat, - throttle, references, note, version, @@ -126,6 +122,15 @@ export const patchRulesBulkRoute = (router: IRouter) => { machineLearningJobId, }); if (rule != null) { + const ruleActions = await updateRulesNotifications({ + ruleAlertId: rule.id, + alertsClient, + savedObjectsClient, + enabled: rule.enabled!, + actions, + throttle, + name: rule.name!, + }); const ruleStatuses = await savedObjectsClient.find< IRuleSavedAttributesSavedObjectAttributes >({ @@ -136,7 +141,12 @@ export const patchRulesBulkRoute = (router: IRouter) => { search: rule.id, searchFields: ['alertId'], }); - return transformValidateBulkError(rule.id, rule, ruleStatuses.saved_objects[0]); + return transformValidateBulkError( + rule.id, + rule, + ruleActions, + ruleStatuses.saved_objects[0] + ); } else { return getIdBulkError({ id, ruleId }); } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts index 1e344d8ea7e31..dbb0a3bb3e1da 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts @@ -35,6 +35,7 @@ describe('patch_rules', () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); + clients.alertsClient.get.mockResolvedValue(getResult()); // existing rule clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); // existing rule clients.alertsClient.update.mockResolvedValue(getResult()); // successful update clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); // successful transform diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts index 620bcd8fc17b0..f553ccd2c6f81 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -21,6 +21,7 @@ import { import { getIdError } from './utils'; import { transformValidate } from './validate'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; +import { updateRulesNotifications } from '../../rules/update_rules_notifications'; export const patchRulesRoute = (router: IRouter) => { router.patch( @@ -74,12 +75,8 @@ export const patchRulesRoute = (router: IRouter) => { validateLicenseForRuleType({ license: context.licensing.license, ruleType: type }); } - if (!context.alerting || !context.actions) { - return siemResponse.error({ statusCode: 404 }); - } - - const alertsClient = context.alerting.getAlertsClient(); - const actionsClient = context.actions.getActionsClient(); + const alertsClient = context.alerting?.getAlertsClient(); + const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; if (!actionsClient || !alertsClient) { @@ -89,7 +86,6 @@ export const patchRulesRoute = (router: IRouter) => { const rule = await patchRules({ actionsClient, alertsClient, - actions, description, enabled, falsePositives, @@ -115,7 +111,6 @@ export const patchRulesRoute = (router: IRouter) => { to, type, threat, - throttle, references, note, version, @@ -123,6 +118,15 @@ export const patchRulesRoute = (router: IRouter) => { machineLearningJobId, }); if (rule != null) { + const ruleActions = await updateRulesNotifications({ + ruleAlertId: rule.id, + alertsClient, + savedObjectsClient, + enabled: rule.enabled!, + actions, + throttle, + name: rule.name!, + }); const ruleStatuses = await savedObjectsClient.find< IRuleSavedAttributesSavedObjectAttributes >({ @@ -134,7 +138,11 @@ export const patchRulesRoute = (router: IRouter) => { searchFields: ['alertId'], }); - const [validated, errors] = transformValidate(rule, ruleStatuses.saved_objects[0]); + const [validated, errors] = transformValidate( + rule, + ruleActions, + ruleStatuses.saved_objects[0] + ); if (errors != null) { return siemResponse.error({ statusCode: 500, body: errors }); } else { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.ts index e4117166ed4fa..77747448e94fd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.ts @@ -16,6 +16,7 @@ import { IRuleSavedAttributesSavedObjectAttributes, } from '../../rules/types'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; +import { getRuleActionsSavedObject } from '../../rule_actions/get_rule_actions_saved_object'; export const readRulesRoute = (router: IRouter) => { router.get( @@ -32,10 +33,7 @@ export const readRulesRoute = (router: IRouter) => { const { id, rule_id: ruleId } = request.query; const siemResponse = buildSiemResponse(response); - if (!context.alerting) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); + const alertsClient = context.alerting?.getAlertsClient(); const savedObjectsClient = context.core.savedObjects.client; try { @@ -49,6 +47,10 @@ export const readRulesRoute = (router: IRouter) => { ruleId, }); if (rule != null) { + const ruleActions = await getRuleActionsSavedObject({ + savedObjectsClient, + ruleAlertId: rule.id, + }); const ruleStatuses = await savedObjectsClient.find< IRuleSavedAttributesSavedObjectAttributes >({ @@ -59,7 +61,11 @@ export const readRulesRoute = (router: IRouter) => { search: rule.id, searchFields: ['alertId'], }); - const [validated, errors] = transformValidate(rule, ruleStatuses.saved_objects[0]); + const [validated, errors] = transformValidate( + rule, + ruleActions, + ruleStatuses.saved_objects[0] + ); if (errors != null) { return siemResponse.error({ statusCode: 500, body: errors }); } else { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts index 611b38ccbae8b..332a47d0c0fc2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts @@ -69,6 +69,13 @@ describe('update_rules_bulk', () => { expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); }); + it('returns 404 if siem client is unavailable', async () => { + const { siem, ...contextWithoutSiem } = context; + const response = await server.inject(getUpdateBulkRequest(), contextWithoutSiem); + expect(response.status).toEqual(404); + expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); + }); + test('returns an error if update throws', async () => { clients.alertsClient.update.mockImplementation(() => { throw new Error('Test error'); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index 4abeb840c8c0a..9916972f41843 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -22,6 +22,7 @@ import { updateRulesBulkSchema } from '../schemas/update_rules_bulk_schema'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; import { updateRules } from '../../rules/update_rules'; import { rulesBulkSchema } from '../schemas/response/rules_bulk_schema'; +import { updateRulesNotifications } from '../../rules/update_rules_notifications'; export const updateRulesBulkRoute = (router: IRouter) => { router.put( @@ -37,15 +38,12 @@ export const updateRulesBulkRoute = (router: IRouter) => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); - if (!context.alerting || !context.actions) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); - const actionsClient = context.actions.getActionsClient(); + const alertsClient = context.alerting?.getAlertsClient(); + const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; - const siemClient = context.siem.getSiemClient(); + const siemClient = context.siem?.getSiemClient(); - if (!actionsClient || !alertsClient) { + if (!siemClient || !actionsClient || !alertsClient) { return siemResponse.error({ statusCode: 404 }); } @@ -93,11 +91,9 @@ export const updateRulesBulkRoute = (router: IRouter) => { const rule = await updateRules({ alertsClient, actionsClient, - actions, anomalyThreshold, description, enabled, - immutable: false, falsePositives, from, query, @@ -122,13 +118,21 @@ export const updateRulesBulkRoute = (router: IRouter) => { to, type, threat, - throttle, references, note, version, lists, }); if (rule != null) { + const ruleActions = await updateRulesNotifications({ + ruleAlertId: rule.id, + alertsClient, + savedObjectsClient, + enabled, + actions, + throttle, + name, + }); const ruleStatuses = await savedObjectsClient.find< IRuleSavedAttributesSavedObjectAttributes >({ @@ -139,7 +143,12 @@ export const updateRulesBulkRoute = (router: IRouter) => { search: rule.id, searchFields: ['alertId'], }); - return transformValidateBulkError(rule.id, rule, ruleStatuses.saved_objects[0]); + return transformValidateBulkError( + rule.id, + rule, + ruleActions, + ruleStatuses.saved_objects[0] + ); } else { return getIdBulkError({ id, ruleId }); } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts index 717f2cc4a52fe..53c52153e84e6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts @@ -18,6 +18,8 @@ import { import { requestContextMock, serverMock, requestMock } from '../__mocks__'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../feature_flags'; +import { updateRulesNotifications } from '../../rules/update_rules_notifications'; +jest.mock('../../rules/update_rules_notifications'); describe('update_rules', () => { let server: ReturnType; @@ -35,6 +37,7 @@ describe('update_rules', () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); + clients.alertsClient.get.mockResolvedValue(getResult()); // existing rule clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists clients.alertsClient.update.mockResolvedValue(getResult()); // successful update clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatusEmpty()); // successful transform @@ -44,6 +47,12 @@ describe('update_rules', () => { describe('status codes with actionClient and alertClient', () => { test('returns 200 when updating a single rule with a valid actionClient and alertClient', async () => { + (updateRulesNotifications as jest.Mock).mockResolvedValue({ + id: 'id', + actions: [], + alertThrottle: null, + ruleThrottle: 'no_actions', + }); const response = await server.inject(getUpdateRequest(), context); expect(response.status).toEqual(200); }); @@ -67,6 +76,13 @@ describe('update_rules', () => { expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); }); + it('returns 404 if siem client is unavailable', async () => { + const { siem, ...contextWithoutSiem } = context; + const response = await server.inject(getUpdateRequest(), contextWithoutSiem); + expect(response.status).toEqual(404); + expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); + }); + test('returns error when updating non-rule', async () => { clients.alertsClient.find.mockResolvedValue(nonRuleFindResult()); const response = await server.inject(getUpdateRequest(), context); 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 f0d5f08c5f636..21dd2a4429cca 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 @@ -21,7 +21,7 @@ import { getIdError } from './utils'; import { transformValidate } from './validate'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; import { updateRules } from '../../rules/update_rules'; -import { updateNotifications } from '../../notifications/update_notifications'; +import { updateRulesNotifications } from '../../rules/update_rules_notifications'; export const updateRulesRoute = (router: IRouter) => { router.put( @@ -74,15 +74,12 @@ export const updateRulesRoute = (router: IRouter) => { try { validateLicenseForRuleType({ license: context.licensing.license, ruleType: type }); - if (!context.alerting || !context.actions) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); - const actionsClient = context.actions.getActionsClient(); + const alertsClient = context.alerting?.getAlertsClient(); + const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; - const siemClient = context.siem.getSiemClient(); + const siemClient = context.siem?.getSiemClient(); - if (!actionsClient || !alertsClient) { + if (!siemClient || !actionsClient || !alertsClient) { return siemResponse.error({ statusCode: 404 }); } @@ -90,13 +87,11 @@ export const updateRulesRoute = (router: IRouter) => { const rule = await updateRules({ alertsClient, actionsClient, - actions, anomalyThreshold, description, enabled, falsePositives, from, - immutable: false, query, language, machineLearningJobId, @@ -119,7 +114,6 @@ export const updateRulesRoute = (router: IRouter) => { to, type, threat, - throttle, references, note, version, @@ -127,15 +121,15 @@ export const updateRulesRoute = (router: IRouter) => { }); if (rule != null) { - await updateNotifications({ + const ruleActions = await updateRulesNotifications({ + ruleAlertId: rule.id, alertsClient, - actions, + savedObjectsClient, enabled, - ruleAlertId: rule.id, - interval: throttle, + actions, + throttle, name, }); - const ruleStatuses = await savedObjectsClient.find< IRuleSavedAttributesSavedObjectAttributes >({ @@ -146,7 +140,11 @@ export const updateRulesRoute = (router: IRouter) => { search: rule.id, searchFields: ['alertId'], }); - const [validated, errors] = transformValidate(rule, ruleStatuses.saved_objects[0]); + const [validated, errors] = transformValidate( + rule, + ruleActions, + ruleStatuses.saved_objects[0] + ); if (errors != null) { return siemResponse.error({ statusCode: 500, body: errors }); } else { 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 3a047f91a0bcb..31a0f37fe81c9 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 @@ -215,17 +215,20 @@ describe('utils', () => { describe('transformFindAlerts', () => { test('outputs empty data set when data set is empty correct', () => { - const output = transformFindAlerts({ data: [], page: 1, perPage: 0, total: 0 }); + const output = transformFindAlerts({ data: [], page: 1, perPage: 0, total: 0 }, []); expect(output).toEqual({ data: [], page: 1, perPage: 0, total: 0 }); }); test('outputs 200 if the data is of type siem alert', () => { - const output = transformFindAlerts({ - page: 1, - perPage: 0, - total: 0, - data: [getResult()], - }); + const output = transformFindAlerts( + { + page: 1, + perPage: 0, + total: 0, + data: [getResult()], + }, + [] + ); const expected = getOutputRuleAlertForRest(); expect(output).toEqual({ page: 1, @@ -237,12 +240,15 @@ describe('utils', () => { test('returns 500 if the data is not of type siem alert', () => { const unsafeCast = ([{ name: 'something else' }] as unknown) as SanitizedAlert[]; - const output = transformFindAlerts({ - data: unsafeCast, - page: 1, - perPage: 1, - total: 1, - }); + const output = transformFindAlerts( + { + data: unsafeCast, + page: 1, + perPage: 1, + total: 1, + }, + [] + ); expect(output).toBeNull(); }); }); @@ -364,14 +370,24 @@ describe('utils', () => { describe('transformOrBulkError', () => { test('outputs 200 if the data is of type siem alert', () => { - const output = transformOrBulkError('rule-1', getResult()); + const output = transformOrBulkError('rule-1', getResult(), { + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + actions: [], + ruleThrottle: 'no_actions', + alertThrottle: null, + }); const expected = getOutputRuleAlertForRest(); expect(output).toEqual(expected); }); test('returns 500 if the data is not of type siem alert', () => { const unsafeCast = ({ name: 'something else' } as unknown) as PartialAlert; - const output = transformOrBulkError('rule-1', unsafeCast); + const output = transformOrBulkError('rule-1', unsafeCast, { + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + actions: [], + ruleThrottle: 'no_actions', + alertThrottle: null, + }); const expected: BulkError = { rule_id: 'rule-1', error: { message: 'Internal error transforming', status_code: 500 }, 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 ca0d133627210..4d13fa1b6ae50 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 @@ -19,12 +19,7 @@ import { isRuleStatusFindTypes, isRuleStatusSavedObjectType, } from '../../rules/types'; -import { - OutputRuleAlertRest, - ImportRuleAlertRest, - RuleAlertParamsRest, - RuleType, -} from '../../types'; +import { OutputRuleAlertRest, ImportRuleAlertRest, RuleAlertParamsRest } from '../../types'; import { createBulkErrorObject, BulkError, @@ -34,7 +29,8 @@ import { OutputError, } from '../utils'; import { hasListsFeature } from '../../feature_flags'; -import { transformAlertToRuleAction } from '../../../../../common/detection_engine/transform_actions'; +// import { transformAlertToRuleAction } from '../../../../../common/detection_engine/transform_actions'; +import { RuleActions } from '../../rule_actions/types'; type PromiseFromStreams = ImportRuleAlertRest | Error; @@ -105,10 +101,11 @@ export const transformTags = (tags: string[]): string[] => { // those on the export export const transformAlertToRule = ( alert: RuleAlertType, + ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): Partial => { return pickBy((value: unknown) => value != null, { - actions: alert.actions.map(transformAlertToRuleAction), + actions: ruleActions?.actions ?? [], created_at: alert.createdAt.toISOString(), updated_at: alert.updatedAt.toISOString(), created_by: alert.createdBy, @@ -141,7 +138,7 @@ export const transformAlertToRule = ( to: alert.params.to, type: alert.params.type, threat: alert.params.threat, - throttle: alert.throttle, + throttle: ruleActions?.ruleThrottle || 'no_actions', note: alert.params.note, version: alert.params.version, status: ruleStatus?.attributes.status, @@ -172,6 +169,7 @@ export const transformAlertsToRules = ( export const transformFindAlerts = ( findResults: FindResult, + ruleActions: Array, ruleStatuses?: Array> ): { page: number; @@ -184,7 +182,7 @@ export const transformFindAlerts = ( page: findResults.page, perPage: findResults.perPage, total: findResults.total, - data: findResults.data.map(alert => transformAlertToRule(alert)), + data: findResults.data.map((alert, idx) => transformAlertToRule(alert, ruleActions[idx])), }; } else if (isAlertTypes(findResults.data) && isRuleStatusFindTypes(ruleStatuses)) { return { @@ -192,7 +190,7 @@ export const transformFindAlerts = ( perPage: findResults.perPage, total: findResults.total, data: findResults.data.map((alert, idx) => - transformAlertToRule(alert, ruleStatuses[idx].saved_objects[0]) + transformAlertToRule(alert, ruleActions[idx], ruleStatuses[idx].saved_objects[0]) ), }; } else { @@ -202,28 +200,31 @@ export const transformFindAlerts = ( export const transform = ( alert: PartialAlert, + ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): Partial | null => { - if (!ruleStatus && isAlertType(alert)) { - return transformAlertToRule(alert); - } - if (isAlertType(alert) && isRuleStatusSavedObjectType(ruleStatus)) { - return transformAlertToRule(alert, ruleStatus); - } else { - return null; + if (isAlertType(alert)) { + return transformAlertToRule( + alert, + ruleActions, + isRuleStatusSavedObjectType(ruleStatus) ? ruleStatus : undefined + ); } + + return null; }; export const transformOrBulkError = ( ruleId: string, alert: PartialAlert, + ruleActions: RuleActions, ruleStatus?: unknown ): Partial | BulkError => { if (isAlertType(alert)) { if (isRuleStatusFindType(ruleStatus) && ruleStatus?.saved_objects.length > 0) { - return transformAlertToRule(alert, ruleStatus?.saved_objects[0] ?? ruleStatus); + return transformAlertToRule(alert, ruleActions, ruleStatus?.saved_objects[0] ?? ruleStatus); } else { - return transformAlertToRule(alert); + return transformAlertToRule(alert, ruleActions); } } else { return createBulkErrorObject({ @@ -300,5 +301,3 @@ export const getTupleDuplicateErrorsAndUniqueRules = ( return [Array.from(errors.values()), Array.from(rulesAcc.values())]; }; - -export const isMlRule = (ruleType: RuleType) => ruleType === 'machine_learning'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts index 3727908ac62de..77e05796fbcbe 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts @@ -43,6 +43,7 @@ export const ruleOutput: RulesSchema = { tags: [], to: 'now', type: 'query', + throttle: 'no_actions', threat: [ { framework: 'MITRE ATT&CK', @@ -154,7 +155,7 @@ describe('validate', () => { describe('transformValidateFindAlerts', () => { test('it should do a validation correctly of a find alert', () => { const findResult: FindResult = { data: [getResult()], page: 1, perPage: 0, total: 0 }; - const [validated, errors] = transformValidateFindAlerts(findResult); + const [validated, errors] = transformValidateFindAlerts(findResult, []); expect(validated).toEqual({ data: [ruleOutput], page: 1, perPage: 0, total: 0 }); expect(errors).toEqual(null); }); @@ -162,7 +163,7 @@ describe('validate', () => { test('it should do an in-validation correctly of a partial alert', () => { const findResult: FindResult = { data: [getResult()], page: 1, perPage: 0, total: 0 }; delete findResult.page; - const [validated, errors] = transformValidateFindAlerts(findResult); + const [validated, errors] = transformValidateFindAlerts(findResult, []); expect(validated).toEqual(null); expect(errors).toEqual('Invalid value "undefined" supplied to "page"'); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts index e654da99fe67b..1f3d1ec856684 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts @@ -22,9 +22,11 @@ import { rulesSchema, RulesSchema } from '../schemas/response/rules_schema'; import { exactCheck } from '../schemas/response/exact_check'; import { transformFindAlerts, transform, transformAlertToRule } from './utils'; import { findRulesSchema } from '../schemas/response/find_rules_schema'; +import { RuleActions } from '../../rule_actions/types'; export const transformValidateFindAlerts = ( findResults: FindResult, + ruleActions: Array, ruleStatuses?: Array> ): [ { @@ -35,7 +37,7 @@ export const transformValidateFindAlerts = ( } | null, string | null ] => { - const transformed = transformFindAlerts(findResults, ruleStatuses); + const transformed = transformFindAlerts(findResults, ruleActions, ruleStatuses); if (transformed == null) { return [null, 'Internal error transforming']; } else { @@ -54,9 +56,10 @@ export const transformValidateFindAlerts = ( export const transformValidate = ( alert: PartialAlert, + ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): [RulesSchema | null, string | null] => { - const transformed = transform(alert, ruleStatus); + const transformed = transform(alert, ruleActions, ruleStatus); if (transformed == null) { return [null, 'Internal error transforming']; } else { @@ -67,11 +70,16 @@ export const transformValidate = ( export const transformValidateBulkError = ( ruleId: string, alert: PartialAlert, + ruleActions?: RuleActions | null, ruleStatus?: unknown ): RulesSchema | BulkError => { if (isAlertType(alert)) { if (isRuleStatusFindType(ruleStatus) && ruleStatus?.saved_objects.length > 0) { - const transformed = transformAlertToRule(alert, ruleStatus?.saved_objects[0] ?? ruleStatus); + const transformed = transformAlertToRule( + alert, + ruleActions, + ruleStatus?.saved_objects[0] ?? ruleStatus + ); const [validated, errors] = validate(transformed, rulesSchema); if (errors != null || validated == null) { return createBulkErrorObject({ 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 index da9f9777a01a6..006fc81e3ee87 100644 --- 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 @@ -52,7 +52,7 @@ import { hasListsFeature } from '../../feature_flags'; * - immutable is forbidden but defaults to true instead of to false and it can only ever be true * - enabled defaults to false instead of true * - version is a required field that must exist - * - index is a required field that must exist + * - index is a required field that must exist if type !== machine_learning */ export const addPrepackagedRulesSchema = Joi.object({ actions: actions.default([]), @@ -71,7 +71,11 @@ export const addPrepackagedRulesSchema = Joi.object({ .forbidden() .default(true) .valid(true), - index: index.required(), + index: index.when('type', { + is: 'machine_learning', + then: Joi.forbidden(), + otherwise: Joi.required(), + }), interval: interval.default('5m'), query: query.when('type', { is: 'machine_learning', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.ts index b5a01e3e5c6df..25e76f367037a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.ts @@ -8,6 +8,7 @@ import * as t from 'io-ts'; import { Either, left, fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; +import { isMlRule } from '../../../../../../common/detection_engine/ml_helpers'; import { dependentRulesSchema, RequiredRulesSchema, @@ -47,7 +48,7 @@ export const addQueryFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixe }; export const addMlFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { - if (typeAndTimelineOnly.type === 'machine_learning') { + if (isMlRule(typeAndTimelineOnly.type)) { return [ t.exact(t.type({ anomaly_threshold: dependentRulesSchema.props.anomaly_threshold })), t.exact( diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts index 612d08c09785a..72f3c89f660c7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts @@ -49,6 +49,13 @@ describe('set signal status', () => { expect(response.status).toEqual(200); }); + it('returns 404 if siem client is unavailable', async () => { + const { siem, ...contextWithoutSiem } = context; + const response = await server.inject(getSetSignalStatusByQueryRequest(), contextWithoutSiem); + expect(response.status).toEqual(404); + expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); + }); + test('catches error if callAsCurrentUser throws error', async () => { clients.clusterClient.callAsCurrentUser.mockImplementation(async () => { throw new Error('Test error'); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts index c1cba641de3ef..2daf63c468593 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts @@ -24,9 +24,13 @@ export const setSignalsStatusRoute = (router: IRouter) => { async (context, request, response) => { const { signal_ids: signalIds, query, status } = request.body; const clusterClient = context.core.elasticsearch.dataClient; - const siemClient = context.siem.getSiemClient(); + const siemClient = context.siem?.getSiemClient(); const siemResponse = buildSiemResponse(response); + if (!siemClient) { + return siemResponse.error({ statusCode: 404 }); + } + let queryObject; if (signalIds) { queryObject = { ids: { values: signalIds } }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts index 77b62b058fa54..f05f494619b9c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts @@ -24,7 +24,7 @@ export const querySignalsRoute = (router: IRouter) => { async (context, request, response) => { const { query, aggs, _source, track_total_hits, size } = request.body; const clusterClient = context.core.elasticsearch.dataClient; - const siemClient = context.siem.getSiemClient(); + const siemClient = context.siem!.getSiemClient(); const siemResponse = buildSiemResponse(response); try { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/tags/read_tags_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/tags/read_tags_route.ts index e12bf50169c17..adabc62a9456f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/tags/read_tags_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/tags/read_tags_route.ts @@ -20,11 +20,7 @@ export const readTagsRoute = (router: IRouter) => { }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - - if (!context.alerting) { - return siemResponse.error({ statusCode: 404 }); - } - const alertsClient = context.alerting.getAlertsClient(); + const alertsClient = context.alerting?.getAlertsClient(); if (!alertsClient) { return siemResponse.error({ statusCode: 404 }); 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 90c7d4a07ddf8..8d7360bae8eb9 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 @@ -16,9 +16,9 @@ import { } from '../../../../../../../../src/core/server'; import { ILicense } from '../../../../../../../plugins/licensing/server'; import { MINIMUM_ML_LICENSE } from '../../../../common/constants'; +import { RuleType } from '../../../../common/detection_engine/types'; +import { isMlRule } from '../../../../common/detection_engine/ml_helpers'; import { BadRequestError } from '../errors/bad_request_error'; -import { RuleType } from '../types'; -import { isMlRule } from './rules/utils'; export interface OutputError { message: string; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/create_rule_actions_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/create_rule_actions_saved_object.ts new file mode 100644 index 0000000000000..23c99b36cb4a7 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/create_rule_actions_saved_object.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RuleAlertAction } from '../../../../common/detection_engine/types'; +import { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { ruleActionsSavedObjectType } from './saved_object_mappings'; +import { IRuleActionsAttributesSavedObjectAttributes } from './types'; +import { getThrottleOptions, getRuleActionsFromSavedObject } from './utils'; + +interface CreateRuleActionsSavedObject { + ruleAlertId: string; + savedObjectsClient: AlertServices['savedObjectsClient']; + actions: RuleAlertAction[] | undefined; + throttle: string | undefined; +} + +export const createRuleActionsSavedObject = async ({ + ruleAlertId, + savedObjectsClient, + actions = [], + throttle, +}: CreateRuleActionsSavedObject) => { + const ruleActionsSavedObject = await savedObjectsClient.create< + IRuleActionsAttributesSavedObjectAttributes + >(ruleActionsSavedObjectType, { + ruleAlertId, + actions, + ...getThrottleOptions(throttle), + }); + + return getRuleActionsFromSavedObject(ruleActionsSavedObject); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/delete_rule_actions_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/delete_rule_actions_saved_object.ts new file mode 100644 index 0000000000000..4e8781dd45692 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/delete_rule_actions_saved_object.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 { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { ruleActionsSavedObjectType } from './saved_object_mappings'; +import { getRuleActionsSavedObject } from './get_rule_actions_saved_object'; + +interface DeleteRuleActionsSavedObject { + ruleAlertId: string; + savedObjectsClient: AlertServices['savedObjectsClient']; +} + +export const deleteRuleActionsSavedObject = async ({ + ruleAlertId, + savedObjectsClient, +}: DeleteRuleActionsSavedObject) => { + const ruleActions = await getRuleActionsSavedObject({ ruleAlertId, savedObjectsClient }); + + if (!ruleActions) return null; + + return savedObjectsClient.delete(ruleActionsSavedObjectType, ruleActions.id); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/get_rule_actions_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/get_rule_actions_saved_object.ts new file mode 100644 index 0000000000000..3ae9090526d69 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/get_rule_actions_saved_object.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { ruleActionsSavedObjectType } from './saved_object_mappings'; +import { IRuleActionsAttributesSavedObjectAttributes } from './types'; +import { getRuleActionsFromSavedObject } from './utils'; + +interface GetRuleActionsSavedObject { + ruleAlertId: string; + savedObjectsClient: AlertServices['savedObjectsClient']; +} + +export const getRuleActionsSavedObject = async ({ + ruleAlertId, + savedObjectsClient, +}: GetRuleActionsSavedObject) => { + const { saved_objects } = await savedObjectsClient.find< + IRuleActionsAttributesSavedObjectAttributes + >({ + type: ruleActionsSavedObjectType, + perPage: 1, + search: `${ruleAlertId}`, + searchFields: ['ruleAlertId'], + }); + + if (!saved_objects[0]) { + return null; + } + + return getRuleActionsFromSavedObject(saved_objects[0]); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/saved_object_mappings.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/saved_object_mappings.ts new file mode 100644 index 0000000000000..f54f43c41ef6e --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/saved_object_mappings.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const ruleActionsSavedObjectType = 'siem-detection-engine-rule-actions'; + +export const ruleActionsSavedObjectMappings = { + [ruleActionsSavedObjectType]: { + properties: { + alertThrottle: { + type: 'keyword', + }, + ruleAlertId: { + type: 'keyword', + }, + ruleThrottle: { + type: 'keyword', + }, + actions: { + properties: { + group: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + action_type_id: { + type: 'keyword', + }, + params: { + dynamic: true, + properties: {}, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/types.ts new file mode 100644 index 0000000000000..525eb74d18fb2 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/types.ts @@ -0,0 +1,68 @@ +/* + * 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 { get } from 'lodash/fp'; +import { SavedObject, SavedObjectAttributes, SavedObjectsFindResponse } from 'kibana/server'; +import { RuleAlertAction } from '../../../../common/detection_engine/types'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface IRuleActionsAttributes extends Record { + ruleAlertId: string; + actions: RuleAlertAction[]; + ruleThrottle: string; + alertThrottle: string | null; +} + +export interface RuleActions { + id: string; + actions: RuleAlertAction[]; + ruleThrottle: string; + alertThrottle: string | null; +} + +export interface IRuleActionsAttributesSavedObjectAttributes + extends IRuleActionsAttributes, + SavedObjectAttributes {} + +export interface RuleActionsResponse { + [key: string]: { + actions: IRuleActionsAttributes | null | undefined; + }; +} + +export interface IRuleActionsSavedObject { + type: string; + id: string; + attributes: Array>; + references: unknown[]; + updated_at: string; + version: string; +} + +export interface IRuleActionsFindType { + page: number; + per_page: number; + total: number; + saved_objects: IRuleActionsSavedObject[]; +} + +export const isRuleActionsSavedObjectType = ( + obj: unknown +): obj is SavedObject => { + return get('attributes', obj) != null; +}; + +export const isRuleActionsFindType = ( + obj: unknown +): obj is SavedObjectsFindResponse => { + return get('saved_objects', obj) != null; +}; + +export const isRuleActionsFindTypes = ( + obj: unknown[] | undefined +): obj is Array> => { + return obj ? obj.every(ruleStatus => isRuleActionsFindType(ruleStatus)) : false; +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_or_create_rule_actions_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_or_create_rule_actions_saved_object.ts new file mode 100644 index 0000000000000..3856f75255262 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_or_create_rule_actions_saved_object.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { RuleAlertAction } from '../../../../common/detection_engine/types'; +import { getRuleActionsSavedObject } from './get_rule_actions_saved_object'; +import { createRuleActionsSavedObject } from './create_rule_actions_saved_object'; +import { updateRuleActionsSavedObject } from './update_rule_actions_saved_object'; +import { RuleActions } from './types'; + +interface UpdateOrCreateRuleActionsSavedObject { + ruleAlertId: string; + savedObjectsClient: AlertServices['savedObjectsClient']; + actions: RuleAlertAction[] | undefined; + throttle: string | undefined; +} + +export const updateOrCreateRuleActionsSavedObject = async ({ + savedObjectsClient, + ruleAlertId, + actions, + throttle, +}: UpdateOrCreateRuleActionsSavedObject): Promise => { + const currentRuleActions = await getRuleActionsSavedObject({ ruleAlertId, savedObjectsClient }); + + if (currentRuleActions) { + return updateRuleActionsSavedObject({ + ruleAlertId, + savedObjectsClient, + actions, + throttle, + }) as Promise; + } + + return createRuleActionsSavedObject({ ruleAlertId, savedObjectsClient, actions, throttle }); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_rule_actions_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_rule_actions_saved_object.ts new file mode 100644 index 0000000000000..56bce3c8b67a3 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_rule_actions_saved_object.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { ruleActionsSavedObjectType } from './saved_object_mappings'; +import { getRuleActionsSavedObject } from './get_rule_actions_saved_object'; +import { RuleAlertAction } from '../../../../common/detection_engine/types'; +import { getThrottleOptions } from './utils'; +import { IRuleActionsAttributesSavedObjectAttributes } from './types'; + +interface DeleteRuleActionsSavedObject { + ruleAlertId: string; + savedObjectsClient: AlertServices['savedObjectsClient']; + actions: RuleAlertAction[] | undefined; + throttle: string | undefined; +} + +export const updateRuleActionsSavedObject = async ({ + ruleAlertId, + savedObjectsClient, + actions, + throttle, +}: DeleteRuleActionsSavedObject) => { + const ruleActions = await getRuleActionsSavedObject({ ruleAlertId, savedObjectsClient }); + + if (!ruleActions) return null; + + const throttleOptions = throttle + ? getThrottleOptions(throttle) + : { + ruleThrottle: ruleActions.ruleThrottle, + alertThrottle: ruleActions.alertThrottle, + }; + + const options = { + actions: actions ?? ruleActions.actions, + ...throttleOptions, + }; + + await savedObjectsClient.update( + ruleActionsSavedObjectType, + ruleActions.id, + { + ruleAlertId, + ...options, + } + ); + + return { + id: ruleActions.id, + ...options, + }; +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/utils.ts new file mode 100644 index 0000000000000..3c297ed848555 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/utils.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsUpdateResponse } from 'kibana/server'; +import { IRuleActionsAttributesSavedObjectAttributes } from './types'; + +export const getThrottleOptions = (throttle = 'no_actions') => ({ + ruleThrottle: throttle, + alertThrottle: ['no_actions', 'rule'].includes(throttle) ? null : throttle, +}); + +export const getRuleActionsFromSavedObject = ( + savedObject: SavedObjectsUpdateResponse +) => ({ + id: savedObject.id, + actions: savedObject.attributes.actions || [], + alertThrottle: savedObject.attributes.alertThrottle || null, + ruleThrottle: savedObject.attributes.ruleThrottle || 'no_actions', +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts index 14b8ffdfdacec..4c8d0f51f251b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts @@ -28,12 +28,10 @@ describe('createRules', () => { await createRules({ alertsClient, actionsClient, - actions: [], ...params, ruleId: 'new-rule-id', enabled: true, interval: '', - throttle: null, name: '', tags: [], }); 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 a45b28ba3e105..bebf4f350483b 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,15 +6,12 @@ import { Alert } from '../../../../../../../plugins/alerting/common'; import { APP_ID, SIGNALS_ID } from '../../../../common/constants'; -import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; import { CreateRuleParams } from './types'; import { addTags } from './add_tags'; import { hasListsFeature } from '../feature_flags'; export const createRules = async ({ alertsClient, - actionsClient, // TODO: Use this actionsClient once we have actions such as email, etc... - actions, anomalyThreshold, description, enabled, @@ -39,7 +36,6 @@ export const createRules = async ({ severity, tags, threat, - throttle, to, type, references, @@ -85,8 +81,8 @@ export const createRules = async ({ }, schedule: { interval }, enabled, - actions: actions?.map(transformRuleToAlertAction), - throttle, + actions: [], + throttle: null, }, }); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts index 20ddcdc3f5362..ca6fb15e1fad9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts @@ -76,6 +76,7 @@ describe('getExportAll', () => { ], }, ], + throttle: 'no_actions', note: '# Investigative notes', version: 1, lists: [ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index e6d4c68d7108d..175c906f7996c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -84,6 +84,7 @@ describe('get_export_by_object_ids', () => { ], }, ], + throttle: 'no_actions', note: '# Investigative notes', version: 1, lists: [ @@ -205,6 +206,7 @@ describe('get_export_by_object_ids', () => { ], }, ], + throttle: 'no_actions', note: '# Investigative notes', version: 1, lists: [ 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 index 801f3d949ed78..bcbe460fb6a66 100644 --- 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 @@ -18,7 +18,6 @@ export const installPrepackagedRules = ( ): Array> => rules.reduce>>((acc, rule) => { const { - actions, anomaly_threshold: anomalyThreshold, description, enabled, @@ -44,7 +43,6 @@ export const installPrepackagedRules = ( to, type, threat, - throttle, references, note, version, @@ -55,7 +53,6 @@ export const installPrepackagedRules = ( createRules({ alertsClient, actionsClient, - actions, anomalyThreshold, description, enabled, @@ -82,7 +79,6 @@ export const installPrepackagedRules = ( to, type, threat, - throttle, references, note, version, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts index cd18bee6f606f..3108fc5f3b718 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts @@ -28,12 +28,10 @@ describe('patchRules', () => { await patchRules({ alertsClient, actionsClient, - actions: [], savedObjectsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ...rule.params, enabled: false, - throttle: null, interval: '', name: '', tags: [], @@ -56,12 +54,10 @@ describe('patchRules', () => { await patchRules({ alertsClient, actionsClient, - actions: [], savedObjectsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ...rule.params, enabled: true, - throttle: null, interval: '', name: '', tags: [], diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts index 5394af526c917..d7655a15499eb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts @@ -6,7 +6,6 @@ import { defaults } from 'lodash/fp'; import { PartialAlert } from '../../../../../../../plugins/alerting/server'; -import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; import { readRules } from './read_rules'; import { PatchRuleParams, IRuleSavedAttributesSavedObjectAttributes } from './types'; import { addTags } from './add_tags'; @@ -16,7 +15,6 @@ import { calculateVersion, calculateName, calculateInterval } from './utils'; export const patchRules = async ({ alertsClient, actionsClient, // TODO: Use this whenever we add feature support for different action types - actions, savedObjectsClient, description, falsePositives, @@ -41,7 +39,6 @@ export const patchRules = async ({ severity, tags, threat, - throttle, to, type, references, @@ -57,7 +54,6 @@ export const patchRules = async ({ } const calculatedVersion = calculateVersion(rule.params.immutable, rule.params.version, { - actions, description, falsePositives, query, @@ -77,7 +73,6 @@ export const patchRules = async ({ severity, tags, threat, - throttle, to, type, references, @@ -125,12 +120,12 @@ export const patchRules = async ({ id: rule.id, data: { tags: addTags(tags ?? rule.tags, rule.params.ruleId, immutable ?? rule.params.immutable), - throttle: throttle !== undefined ? throttle : rule.throttle, + throttle: rule.throttle, name: calculateName({ updatedName: name, originalName: rule.name }), schedule: { interval: calculateInterval(interval, rule.schedule.interval), }, - actions: actions?.map(transformRuleToAlertAction) ?? rule.actions, + actions: rule.actions, params: nextParams, }, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/403_response_to_a_post.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/403_response_to_a_post.json index fd46a09d4ced0..3b043439759c1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/403_response_to_a_post.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/403_response_to_a_post.json @@ -7,7 +7,6 @@ "apm-*-transaction*" ], "language": "kuery", - "max_signals": 100, "name": "Web Application Suspicious Activity: POST Request Declined", "query": "http.response.status_code:403 and http.request.method:post", "references": [ @@ -17,9 +16,9 @@ "rule_id": "a87a4e42-1d82-44bd-b0bf-d9b7f91fb89e", "severity": "medium", "tags": [ - "Elastic", - "APM" + "APM", + "Elastic" ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json index a6235c889902b..12c6a5feabebb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json @@ -7,7 +7,6 @@ "apm-*-transaction*" ], "language": "kuery", - "max_signals": 100, "name": "Web Application Suspicious Activity: Unauthorized Method", "query": "http.response.status_code:405", "references": [ @@ -17,9 +16,9 @@ "rule_id": "75ee75d8-c180-481c-ba88-ee50129a6aef", "severity": "medium", "tags": [ - "Elastic", - "APM" + "APM", + "Elastic" ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json index 397db1367f402..a3302896b7e98 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Adversary Behavior - Detected - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:rules_engine_event", "risk_score": 47, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json index fdd875e95b3d3..8c2c5f32feab7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Credential Dumping - Detected - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:cred_theft_event and endgame.metadata.type:detection", "risk_score": 73, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json index 8ed63c55ef213..6a96da3218bf2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Credential Dumping - Prevented - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:cred_theft_event and endgame.metadata.type:prevention", "risk_score": 47, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json index 98c4e5341d9e8..954e35ccd644a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Credential Manipulation - Detected - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:token_manipulation_event and endgame.metadata.type:detection", "risk_score": 73, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json index 4234e3d955794..0de35891a3e81 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Credential Manipulation - Prevented - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:token_manipulation_event and endgame.metadata.type:prevention", "risk_score": 47, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json index 9971075d7e617..3652b7068ecd2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Exploit - Detected - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:exploit_event and endgame.metadata.type:detection", "risk_score": 73, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json index 233552fc1de13..dbc910c3002a7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Exploit - Prevented - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:exploit_event and endgame.metadata.type:prevention", "risk_score": 47, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json index 64d686fb984c9..efe2806532be0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Malware - Detected - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:file_classification_event and endgame.metadata.type:detection", "risk_score": 99, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json index 72f2134f23ab2..51028b9dbeeb3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Malware - Prevented - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:file_classification_event and endgame.metadata.type:prevention", "risk_score": 73, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json index 3755dd4cd5dac..c30ca0632f410 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Permission Theft - Detected - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:token_protection_event and endgame.metadata.type:detection", "risk_score": 73, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json index d869407586372..ed0c714254743 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Permission Theft - Prevented - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:token_protection_event and endgame.metadata.type:prevention", "risk_score": 47, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json index 1078cf69394e2..63b008849487a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Process Injection - Detected - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:kernel_shellcode_event and endgame.metadata.type:detection", "risk_score": 73, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json index 8b68fc6925f9b..135b4a95e8005 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Process Injection - Prevented - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:kernel_shellcode_event and endgame.metadata.type:prevention", "risk_score": 47, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json index a332c7011e94b..d4042a5e6b9e1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Ransomware - Detected - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:ransomware_event and endgame.metadata.type:detection", "risk_score": 99, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json index 087c91d2105ce..befdf611da223 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json @@ -6,7 +6,6 @@ ], "interval": "10m", "language": "kuery", - "max_signals": 100, "name": "Ransomware - Prevented - Elastic Endpoint", "query": "event.kind:alert and event.module:endgame and event.action:ransomware_event and endgame.metadata.type:prevention", "risk_score": 73, @@ -18,4 +17,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json index 43aead33925c1..6c9b54b8ddb02 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Adding Hidden File Attribute via Attrib", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"attrib.exe\" and process.args:\"+h\"", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:attrib.exe and process.args:+h", "risk_score": 21, "rule_id": "4630d948-40d4-4cef-ac69-4002e29bc3db", "severity": "low", @@ -48,4 +47,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adobe_hijack_persistence.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adobe_hijack_persistence.json index 8b8c510093260..3b4d2bc040217 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adobe_hijack_persistence.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adobe_hijack_persistence.json @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Adobe Hijack Persistence", "query": "file.path:(\"C:\\Program Files (x86)\\Adobe\\Acrobat Reader DC\\Reader\\AcroCEF\\RdrCEF.exe\" or \"C:\\Program Files\\Adobe\\Acrobat Reader DC\\Reader\\AcroCEF\\RdrCEF.exe\") and event.action:\"File created (rule: FileCreate)\" and not process.name:msiexec.exe", "risk_score": 21, @@ -33,4 +32,4 @@ ], "type": "query", "version": 2 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json index 135e81148475f..244d329cc4bb7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Clearing Windows Event Logs", - "query": "event.action:\"Process Create (rule: ProcessCreate)\" and (process.name:\"wevtutil.exe\" and process.args:\"cl\") or (process.name:\"powershell.exe\" and process.args:\"Clear-EventLog\")", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:wevtutil.exe and process.args:cl or process.name:powershell.exe and process.args:Clear-EventLog", "risk_score": 21, "rule_id": "d331bbe2-6db4-4941-80a5-8270db72eb61", "severity": "low", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json index 815e2abd0fc96..4087542816588 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Delete Volume USN Journal with Fsutil", - "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"fsutil.exe\" and process.args:(\"usn\" and \"deletejournal\")", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:fsutil.exe and process.args:(deletejournal and usn)", "risk_score": 21, "rule_id": "f675872f-6d85-40a3-b502-c0d2ef101e92", "severity": "low", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json index d990e071b2123..eca06723e68b8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Deleting Backup Catalogs with Wbadmin", - "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"wbadmin.exe\" and process.args:(\"delete\" and \"catalog\")", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:wbadmin.exe and process.args:(catalog and delete)", "risk_score": 21, "rule_id": "581add16-df76-42bb-af8e-c979bfb39a59", "severity": "low", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json index 9d1cebb32c865..e37c877c62889 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Direct Outbound SMB Connection", - "query": " event.action:\"Network connection detected (rule: NetworkConnect)\" and destination.port:445 and not process.pid:4 and not destination.ip:(\"127.0.0.1\" or \"::1\")", + "query": "event.action:\"Network connection detected (rule: NetworkConnect)\" and destination.port:445 and not process.pid:4 and not destination.ip:(127.0.0.1 or \"::1\")", "risk_score": 47, "rule_id": "c82c7d8f-fb9e-4874-a4bd-fd9e3f9becf1", "severity": "medium", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json index 7ead979f27bb1..f6b4bc67ed9b1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Disable Windows Firewall Rules via Netsh", - "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"netsh.exe\" and process.args:(\"firewall\" and \"set\" and \"disable\") or process.args:(\"advfirewall\" and \"state\" and \"off\")", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:netsh.exe and process.args:(disable and firewall and set) or process.args:(advfirewall and off and state)", "risk_score": 47, "rule_id": "4b438734-3793-4fda-bd42-ceeada0be8f9", "severity": "medium", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json index 2cb92f0a26c90..38162889737ff 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Encoding or Decoding Files via CertUtil", - "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"certutil.exe\" and process.args:(\"-encode\" or \"/encode\" or \"-decode\" or \"/decode\")", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:certutil.exe and process.args:(-decode or -encode or /decode or /encode)", "risk_score": 47, "rule_id": "fd70c98a-c410-42dc-a2e3-761c71848acf", "severity": "medium", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json index e7f46b46c2ce2..42007f153bd55 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json @@ -7,9 +7,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Local Scheduled Task Commands", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:schtasks.exe and process.args:(\"/create\" or \"-create\" or \"/S\" or \"-s\" or \"/run\" or \"-run\" or \"/change\" or \"-change\")", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:schtasks.exe and process.args:(-change or -create or -run or -s or /S or /change or /create or /run)", "risk_score": 21, "rule_id": "afcce5ad-65de-4ed2-8516-5e093d3ac99a", "severity": "low", @@ -36,4 +35,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json index b018435ea0214..9559baabe0e40 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Local Service Commands", - "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:sc.exe and process.args:(\"create\" or \"config\" or \"failure\" or \"start\")", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:sc.exe and process.args:(config or create or failure or start)", "risk_score": 21, "rule_id": "e8571d5f-bea1-46c2-9f56-998de2d3ed95", "severity": "low", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json index 7d84e0bda06e5..3e34aacf605c7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "MsBuild Making Network Connections", - "query": " event.action:\"Network connection detected (rule: NetworkConnect)\" and process.name:MSBuild.exe and not destination.ip:(\"127.0.0.1\" or \"::1\")", + "query": "event.action:\"Network connection detected (rule: NetworkConnect)\" and process.name:MSBuild.exe and not destination.ip:(127.0.0.1 or \"::1\")", "risk_score": 47, "rule_id": "0e79980b-4250-4a50-a509-69294c14e84b", "severity": "medium", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json index 44141b08fb8f0..769614e8faf53 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Network Connection via Mshta", - "query": "event.action:\"Network connection detected (rule: NetworkConnect)\" and process.name:\"mshta.exe\"", + "query": "event.action:\"Network connection detected (rule: NetworkConnect)\" and process.name:mshta.exe", "references": [ "https://www.fireeye.com/blog/threat-research/2017/05/cyber-espionage-apt32.html" ], @@ -36,4 +35,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json index 580f73c25a4a2..ac170665042f6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json @@ -7,9 +7,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "PsExec Network Connection", - "query": "process.name:PsExec.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" ", + "query": "process.name:PsExec.exe and event.action:\"Network connection detected (rule: NetworkConnect)\"", "risk_score": 21, "rule_id": "55d551c6-333b-4665-ab7e-5d14a59715ce", "severity": "low", @@ -51,4 +50,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_office_child_process.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_office_child_process.json index 95aabc49b5302..95c9c6b72f8f4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_office_child_process.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_office_child_process.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Suspicious MS Office Child Process", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:(\"winword.exe\" or \"excel.exe\" or \"powerpnt.exe\" or \"eqnedt32.exe\" or \"fltldr.exe\" or \"mspub.exe\" or \"msaccess.exe\") and process.name:(\"arp.exe\" or \"dsquery.exe\" or \"dsget.exe\" or \"gpresult.exe\" or \"hostname.exe\" or \"ipconfig.exe\" or \"nbtstat.exe\" or \"net.exe\" or \"net1.exe\" or \"netsh.exe\" or \"netstat.exe\" or \"nltest.exe\" or \"ping.exe\" or \"qprocess.exe\" or \"quser.exe\" or \"qwinsta.exe\" or \"reg.exe\" or \"sc.exe\" or \"systeminfo.exe\" or \"tasklist.exe\" or \"tracert.exe\" or \"whoami.exe\" or \"bginfo.exe\" or \"cdb.exe\" or \"cmstp.exe\" or \"csi.exe\" or \"dnx.exe\" or \"fsi.exe\" or \"ieexec.exe\" or \"iexpress.exe\" or \"installutil.exe\" or \"Microsoft.Workflow.Compiler.exe\" or \"msbuild.exe\" or \"mshta.exe\" or \"msxsl.exe\" or \"odbcconf.exe\" or \"rcsi.exe\" or \"regsvr32.exe\" or \"xwizard.exe\" or \"atbroker.exe\" or \"forfiles.exe\" or \"schtasks.exe\" or \"regasm.exe\" or \"regsvcs.exe\" or \"cmd.exe\" or \"cscript.exe\" or \"powershell.exe\" or \"pwsh.exe\" or \"wmic.exe\" or \"wscript.exe\" or \"bitsadmin.exe\" or \"certutil.exe\" or \"ftp.exe\") ", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:(eqnedt32.exe or excel.exe or fltldr.exe or msaccess.exe or mspub.exe or powerpnt.exe or winword.exe) and process.name:(Microsoft.Workflow.Compiler.exe or arp.exe or atbroker.exe or bginfo.exe or bitsadmin.exe or cdb.exe or certutil.exe or cmd.exe or cmstp.exe or cscript.exe or csi.exe or dnx.exe or dsget.exe or dsquery.exe or forfiles.exe or fsi.exe or ftp.exe or gpresult.exe or hostname.exe or ieexec.exe or iexpress.exe or installutil.exe or ipconfig.exe or mshta.exe or msxsl.exe or nbtstat.exe or net.exe or net1.exe or netsh.exe or netstat.exe or nltest.exe or odbcconf.exe or ping.exe or powershell.exe or pwsh.exe or qprocess.exe or quser.exe or qwinsta.exe or rcsi.exe or reg.exe or regasm.exe or regsvcs.exe or regsvr32.exe or sc.exe or schtasks.exe or systeminfo.exe or tasklist.exe or tracert.exe or whoami.exe or wmic.exe or wscript.exe or xwizard.exe)", "risk_score": 21, "rule_id": "a624863f-a70d-417f-a7d2-7a404638d47f", "severity": "low", @@ -32,5 +31,5 @@ } ], "type": "query", - "version": 1 -} + "version": 2 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_outlook_child_process.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_outlook_child_process.json index f31228d0130f8..7f6c9257fabfd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_outlook_child_process.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_outlook_child_process.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Suspicious MS Outlook Child Process", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:\"outlook.exe\" and process.name:(\"arp.exe\" or \"dsquery.exe\" or \"dsget.exe\" or \"gpresult.exe\" or \"hostname.exe\" or \"ipconfig.exe\" or \"nbtstat.exe\" or \"net.exe\" or \"net1.exe\" or \"netsh.exe\" or \"netstat.exe\" or \"nltest.exe\" or \"ping.exe\" or \"qprocess.exe\" or \"quser.exe\" or \"qwinsta.exe\" or \"reg.exe\" or \"sc.exe\" or \"systeminfo.exe\" or \"tasklist.exe\" or \"tracert.exe\" or \"whoami.exe\" or \"bginfo.exe\" or \"cdb.exe\" or \"cmstp.exe\" or \"csi.exe\" or \"dnx.exe\" or \"fsi.exe\" or \"ieexec.exe\" or \"iexpress.exe\" or \"installutil.exe\" or \"Microsoft.Workflow.Compiler.exe\" or \"msbuild.exe\" or \"mshta.exe\" or \"msxsl.exe\" or \"odbcconf.exe\" or \"rcsi.exe\" or \"regsvr32.exe\" or \"xwizard.exe\" or \"atbroker.exe\" or \"forfiles.exe\" or \"schtasks.exe\" or \"regasm.exe\" or \"regsvcs.exe\" or \"cmd.exe\" or \"cscript.exe\" or \"powershell.exe\" or \"pwsh.exe\" or \"wmic.exe\" or \"wscript.exe\" or \"bitsadmin.exe\" or \"certutil.exe\" or \"ftp.exe\") ", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:outlook.exe and process.name:(Microsoft.Workflow.Compiler.exe or arp.exe or atbroker.exe or bginfo.exe or bitsadmin.exe or cdb.exe or certutil.exe or cmd.exe or cmstp.exe or cscript.exe or csi.exe or dnx.exe or dsget.exe or dsquery.exe or forfiles.exe or fsi.exe or ftp.exe or gpresult.exe or hostname.exe or ieexec.exe or iexpress.exe or installutil.exe or ipconfig.exe or mshta.exe or msxsl.exe or nbtstat.exe or net.exe or net1.exe or netsh.exe or netstat.exe or nltest.exe or odbcconf.exe or ping.exe or powershell.exe or pwsh.exe or qprocess.exe or quser.exe or qwinsta.exe or rcsi.exe or reg.exe or regasm.exe or regsvcs.exe or regsvr32.exe or sc.exe or schtasks.exe or systeminfo.exe or tasklist.exe or tracert.exe or whoami.exe or wmic.exe or wscript.exe or xwizard.exe)", "risk_score": 21, "rule_id": "32f4675e-6c49-4ace-80f9-97c9259dca2e", "severity": "low", @@ -32,5 +31,5 @@ } ], "type": "query", - "version": 1 -} + "version": 2 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json index a38232f37843e..1c001caa1539c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "System Shells via Services", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:\"services.exe\" and process.name:(\"cmd.exe\" or \"powershell.exe\")", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:services.exe and process.name:(cmd.exe or powershell.exe)", "risk_score": 47, "rule_id": "0022d47d-39c7-4f69-a232-4fe9dc7a3acd", "severity": "medium", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json index 820c69cb3c809..0165f4d7512e4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Unusual Network Connection via RunDLL32", - "query": "process.name:rundll32.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", + "query": "process.name:rundll32.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)", "risk_score": 21, "rule_id": "52aaab7b-b51c-441a-89ce-4387b3aea886", "severity": "low", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json index 21d3d2741378a..0b4bf9ff32945 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, - "name": "Unusual Parent-Child Relationship ", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.executable:* and ( (process.name:\"smss.exe\" and not process.parent.name:(\"System\" or \"smss.exe\")) or (process.name:\"csrss.exe\" and not process.parent.name:(\"smss.exe\" or \"svchost.exe\")) or (process.name:\"wininit.exe\" and not process.parent.name:\"smss.exe\") or (process.name:\"winlogon.exe\" and not process.parent.name:\"smss.exe\") or (process.name:\"lsass.exe\" and not process.parent.name:\"wininit.exe\") or (process.name:\"LogonUI.exe\" and not process.parent.name:(\"winlogon.exe\" or \"wininit.exe\")) or (process.name:\"services.exe\" and not process.parent.name:\"wininit.exe\") or (process.name:\"svchost.exe\" and not process.parent.name:(\"services.exe\" or \"MsMpEng.exe\")) or (process.name:\"spoolsv.exe\" and not process.parent.name:\"services.exe\") or (process.name:\"taskhost.exe\" and not process.parent.name:(\"services.exe\" or \"svchost.exe\")) or (process.name:\"taskhostw.exe\" and not process.parent.name:(\"services.exe\" or \"svchost.exe\")) or (process.name:\"userinit.exe\" and not process.parent.name:(\"dwm.exe\" or \"winlogon.exe\")) )", + "name": "Unusual Parent-Child Relationship", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.executable:* and (process.name:smss.exe and not process.parent.name:(System or smss.exe) or process.name:csrss.exe and not process.parent.name:(smss.exe or svchost.exe) or process.name:wininit.exe and not process.parent.name:smss.exe or process.name:winlogon.exe and not process.parent.name:smss.exe or process.name:lsass.exe and not process.parent.name:wininit.exe or process.name:LogonUI.exe and not process.parent.name:(wininit.exe or winlogon.exe) or process.name:services.exe and not process.parent.name:wininit.exe or process.name:svchost.exe and not process.parent.name:(MsMpEng.exe or services.exe) or process.name:spoolsv.exe and not process.parent.name:services.exe or process.name:taskhost.exe and not process.parent.name:(services.exe or svchost.exe) or process.name:taskhostw.exe and not process.parent.name:(services.exe or svchost.exe) or process.name:userinit.exe and not process.parent.name:(dwm.exe or winlogon.exe))", "risk_score": 47, "rule_id": "35df0dd8-092d-4a83-88c1-5151a804f31b", "severity": "medium", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json index ee861e19341af..2c88a2061844c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Unusual Process Network Connection", - "query": " event.action:\"Network connection detected (rule: NetworkConnect)\" and process.name:(bginfo.exe or cdb.exe or cmstp.exe or csi.exe or dnx.exe or fsi.exe or ieexec.exe or iexpress.exe or Microsoft.Workflow.Compiler.exe or odbcconf.exe or rcsi.exe or xwizard.exe)", + "query": "event.action:\"Network connection detected (rule: NetworkConnect)\" and process.name:(Microsoft.Workflow.Compiler.exe or bginfo.exe or cdb.exe or cmstp.exe or csi.exe or dnx.exe or fsi.exe or ieexec.exe or iexpress.exe or odbcconf.exe or rcsi.exe or xwizard.exe)", "risk_score": 21, "rule_id": "610949a1-312f-4e04-bb55-3a79b8c95267", "severity": "low", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json index 5a7aeab224548..240df34419132 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "User Account Creation", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:(\"net.exe\" or \"net1.exe\") and not process.parent.name:\"net.exe\" and process.args:(\"user\" and (\"/add\" or \"/ad\")) ", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:(net.exe or net1.exe) and not process.parent.name:net.exe and process.args:(user and (/ad or /add))", "risk_score": 21, "rule_id": "1aa9181a-492b-4c01-8b16-fa0735786b2b", "severity": "low", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json index 80c0dd962c099..e12c2e70138c9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json @@ -1,12 +1,11 @@ { - "description": "Identifies use of vssadmin.exe for shadow copy deletion on endpoints. This commonly occurs in tandem with ransomware or other destructive attacks.", + "description": "Identifies use of vssadmin.exe for shadow copy deletion on endpoints. This commonly occurs in tandem with ransomware or other destructive attacks.", "index": [ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Volume Shadow Copy Deletion via VssAdmin", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"vssadmin.exe\" and process.args:(\"delete\" and \"shadows\") ", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:vssadmin.exe and process.args:(delete and shadows)", "risk_score": 73, "rule_id": "b5ea4bfe-a1b2-421f-9d47-22a75a6f2921", "severity": "high", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json index d90aca1e2eaf4..94b8846741e3e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Volume Shadow Copy Deletion via WMIC", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"WMIC.exe\" and process.args:(\"shadowcopy\" and \"delete\")", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:WMIC.exe and process.args:(delete and shadowcopy)", "risk_score": 73, "rule_id": "dc9c1f74-dac3-48e3-b47f-eb79db358f57", "severity": "high", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json index 8f6e97cdd7bdb..b0a754a662c0e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Windows Script Executing PowerShell", - "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:(\"wscript.exe\" or \"cscript.exe\") and process.name:\"powershell.exe\"", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:(cscript.exe or wscript.exe) and process.name:powershell.exe", "risk_score": 21, "rule_id": "f545ff26-3c94-4fd0-bd33-3c7f95a3a0fc", "severity": "low", @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index d9841948f35a5..c24f5bb64ef5e 100644 --- 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 @@ -47,58 +47,96 @@ import rule37 from './eql_user_account_creation.json'; import rule38 from './eql_volume_shadow_copy_deletion_via_vssadmin.json'; import rule39 from './eql_volume_shadow_copy_deletion_via_wmic.json'; import rule40 from './eql_windows_script_executing_powershell.json'; -import rule41 from './linux_hping_activity.json'; -import rule42 from './linux_iodine_activity.json'; -import rule43 from './linux_kernel_module_activity.json'; -import rule44 from './linux_mknod_activity.json'; -import rule45 from './linux_netcat_network_connection.json'; -import rule46 from './linux_nmap_activity.json'; -import rule47 from './linux_nping_activity.json'; -import rule48 from './linux_process_started_in_temp_directory.json'; -import rule49 from './linux_shell_activity_by_web_server.json'; -import rule50 from './linux_socat_activity.json'; -import rule51 from './linux_strace_activity.json'; -import rule52 from './linux_tcpdump_activity.json'; -import rule53 from './linux_whoami_commmand.json'; -import rule54 from './network_dns_directly_to_the_internet.json'; -import rule55 from './network_ftp_file_transfer_protocol_activity_to_the_internet.json'; -import rule56 from './network_irc_internet_relay_chat_protocol_activity_to_the_internet.json'; -import rule57 from './network_nat_traversal_port_activity.json'; -import rule58 from './network_port_26_activity.json'; -import rule59 from './network_port_8000_activity_to_the_internet.json'; -import rule60 from './network_pptp_point_to_point_tunneling_protocol_activity.json'; -import rule61 from './network_proxy_port_activity_to_the_internet.json'; -import rule62 from './network_rdp_remote_desktop_protocol_from_the_internet.json'; -import rule63 from './network_rdp_remote_desktop_protocol_to_the_internet.json'; -import rule64 from './network_rpc_remote_procedure_call_from_the_internet.json'; -import rule65 from './network_rpc_remote_procedure_call_to_the_internet.json'; -import rule66 from './network_smb_windows_file_sharing_activity_to_the_internet.json'; -import rule67 from './network_smtp_to_the_internet.json'; -import rule68 from './network_sql_server_port_activity_to_the_internet.json'; -import rule69 from './network_ssh_secure_shell_from_the_internet.json'; -import rule70 from './network_ssh_secure_shell_to_the_internet.json'; -import rule71 from './network_telnet_port_activity.json'; -import rule72 from './network_tor_activity_to_the_internet.json'; -import rule73 from './network_vnc_virtual_network_computing_from_the_internet.json'; -import rule74 from './network_vnc_virtual_network_computing_to_the_internet.json'; -import rule75 from './null_user_agent.json'; -import rule76 from './sqlmap_user_agent.json'; -import rule77 from './windows_command_prompt_connecting_to_the_internet.json'; -import rule78 from './windows_command_shell_started_by_powershell.json'; -import rule79 from './windows_command_shell_started_by_svchost.json'; -import rule80 from './windows_defense_evasion_via_filter_manager.json'; -import rule81 from './windows_execution_via_compiled_html_file.json'; -import rule82 from './windows_execution_via_regsvr32.json'; -import rule83 from './windows_execution_via_trusted_developer_utilities.json'; -import rule84 from './windows_html_help_executable_program_connecting_to_the_internet.json'; -import rule85 from './windows_misc_lolbin_connecting_to_the_internet.json'; -import rule86 from './windows_persistence_via_application_shimming.json'; -import rule87 from './windows_priv_escalation_via_accessibility_features.json'; -import rule88 from './windows_process_discovery_via_tasklist_command.json'; -import rule89 from './windows_register_server_program_connecting_to_the_internet.json'; -import rule90 from './windows_signed_binary_proxy_execution.json'; -import rule91 from './windows_suspicious_process_started_by_a_script.json'; -import rule92 from './windows_whoami_command_activity.json'; +import rule41 from './linux_anomalous_network_activity.json'; +import rule42 from './linux_anomalous_network_port_activity.json'; +import rule43 from './linux_anomalous_network_service.json'; +import rule44 from './linux_anomalous_network_url_activity.json'; +import rule45 from './linux_anomalous_process_all_hosts.json'; +import rule46 from './linux_anomalous_user_name.json'; +import rule47 from './linux_hping_activity.json'; +import rule48 from './linux_iodine_activity.json'; +import rule49 from './linux_kernel_module_activity.json'; +import rule50 from './linux_mknod_activity.json'; +import rule51 from './linux_netcat_network_connection.json'; +import rule52 from './linux_nmap_activity.json'; +import rule53 from './linux_nping_activity.json'; +import rule54 from './linux_process_started_in_temp_directory.json'; +import rule55 from './linux_shell_activity_by_web_server.json'; +import rule56 from './linux_socat_activity.json'; +import rule57 from './linux_strace_activity.json'; +import rule58 from './linux_tcpdump_activity.json'; +import rule59 from './linux_whoami_commmand.json'; +import rule60 from './network_dns_directly_to_the_internet.json'; +import rule61 from './network_ftp_file_transfer_protocol_activity_to_the_internet.json'; +import rule62 from './network_irc_internet_relay_chat_protocol_activity_to_the_internet.json'; +import rule63 from './network_nat_traversal_port_activity.json'; +import rule64 from './network_port_26_activity.json'; +import rule65 from './network_port_8000_activity_to_the_internet.json'; +import rule66 from './network_pptp_point_to_point_tunneling_protocol_activity.json'; +import rule67 from './network_proxy_port_activity_to_the_internet.json'; +import rule68 from './network_rdp_remote_desktop_protocol_from_the_internet.json'; +import rule69 from './network_rdp_remote_desktop_protocol_to_the_internet.json'; +import rule70 from './network_rpc_remote_procedure_call_from_the_internet.json'; +import rule71 from './network_rpc_remote_procedure_call_to_the_internet.json'; +import rule72 from './network_smb_windows_file_sharing_activity_to_the_internet.json'; +import rule73 from './network_smtp_to_the_internet.json'; +import rule74 from './network_sql_server_port_activity_to_the_internet.json'; +import rule75 from './network_ssh_secure_shell_from_the_internet.json'; +import rule76 from './network_ssh_secure_shell_to_the_internet.json'; +import rule77 from './network_telnet_port_activity.json'; +import rule78 from './network_tor_activity_to_the_internet.json'; +import rule79 from './network_vnc_virtual_network_computing_from_the_internet.json'; +import rule80 from './network_vnc_virtual_network_computing_to_the_internet.json'; +import rule81 from './null_user_agent.json'; +import rule82 from './packetbeat_dns_tunneling.json'; +import rule83 from './packetbeat_rare_dns_question.json'; +import rule84 from './packetbeat_rare_server_domain.json'; +import rule85 from './packetbeat_rare_urls.json'; +import rule86 from './packetbeat_rare_user_agent.json'; +import rule87 from './rare_process_by_host_linux.json'; +import rule88 from './rare_process_by_host_windows.json'; +import rule89 from './sqlmap_user_agent.json'; +import rule90 from './suspicious_login_activity.json'; +import rule91 from './windows_anomalous_network_activity.json'; +import rule92 from './windows_anomalous_path_activity.json'; +import rule93 from './windows_anomalous_process_all_hosts.json'; +import rule94 from './windows_anomalous_process_creation.json'; +import rule95 from './windows_anomalous_script.json'; +import rule96 from './windows_anomalous_service.json'; +import rule97 from './windows_anomalous_user_name.json'; +import rule98 from './windows_certutil_network_connection.json'; +import rule99 from './windows_command_prompt_connecting_to_the_internet.json'; +import rule100 from './windows_command_shell_started_by_powershell.json'; +import rule101 from './windows_command_shell_started_by_svchost.json'; +import rule102 from './windows_credential_dumping_msbuild.json'; +import rule103 from './windows_cve_2020_0601.json'; +import rule104 from './windows_defense_evasion_via_filter_manager.json'; +import rule105 from './windows_execution_msbuild_started_by_office_app.json'; +import rule106 from './windows_execution_msbuild_started_by_script.json'; +import rule107 from './windows_execution_msbuild_started_by_system_process.json'; +import rule108 from './windows_execution_msbuild_started_renamed.json'; +import rule109 from './windows_execution_msbuild_started_unusal_process.json'; +import rule110 from './windows_execution_via_compiled_html_file.json'; +import rule111 from './windows_execution_via_net_com_assemblies.json'; +import rule112 from './windows_execution_via_regsvr32.json'; +import rule113 from './windows_execution_via_trusted_developer_utilities.json'; +import rule114 from './windows_html_help_executable_program_connecting_to_the_internet.json'; +import rule115 from './windows_injection_msbuild.json'; +import rule116 from './windows_misc_lolbin_connecting_to_the_internet.json'; +import rule117 from './windows_modification_of_boot_config.json'; +import rule118 from './windows_msxsl_network.json'; +import rule119 from './windows_net_command_system_account.json'; +import rule120 from './windows_persistence_via_application_shimming.json'; +import rule121 from './windows_priv_escalation_via_accessibility_features.json'; +import rule122 from './windows_process_discovery_via_tasklist_command.json'; +import rule123 from './windows_rare_user_runas_event.json'; +import rule124 from './windows_rare_user_type10_remote_login.json'; +import rule125 from './windows_register_server_program_connecting_to_the_internet.json'; +import rule126 from './windows_signed_binary_proxy_execution.json'; +import rule127 from './windows_suspicious_pdf_reader.json'; +import rule128 from './windows_suspicious_process_started_by_a_script.json'; +import rule129 from './windows_uac_bypass_event_viewer.json'; +import rule130 from './windows_whoami_command_activity.json'; export const rawRules = [ rule1, rule2, @@ -192,4 +230,42 @@ export const rawRules = [ 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, ]; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_activity.json new file mode 100644 index 0000000000000..1123c1161c4ce --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_activity.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "Identifies Linux processes that do not usually use the network but have unexpected network activity, which can indicate command-and-control, lateral movement, persistence, or data exfiltration activity. A process with unusual network activity can denote process exploitation or injection, where the process is used to run persistence mechanisms that allow a malicious actor remote access or control of the host, data exfiltration, and execution of unauthorized network applications.", + "false_positives": [ + "A newly installed program or one that rarely uses the network could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "linux_anomalous_network_activity_ecs", + "name": "Unusual Linux Network Activity", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "52afbdc5-db15-485e-bc24-f5707f820c4b", + "severity": "low", + "tags": [ + "Elastic", + "Linux", + "ML" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_port_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_port_activity.json new file mode 100644 index 0000000000000..19dd643945b17 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_port_activity.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "Identifies unusual destination port activity that can indicate command-and-control, persistence mechanism, or data exfiltration activity. Rarely used destination port activity is generally unusual in Linux fleets, and can indicate unauthorized access or threat actor activity.", + "false_positives": [ + "A newly installed program or one that rarely uses the network could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "linux_anomalous_network_port_activity_ecs", + "name": "Unusual Linux Network Port Activity", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "3c7e32e6-6104-46d9-a06e-da0f8b5795a0", + "severity": "low", + "tags": [ + "Elastic", + "Linux", + "ML" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_service.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_service.json new file mode 100644 index 0000000000000..e2e5803618d06 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_service.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "Identifies unusual listening ports on Linux instances that can indicate execution of unauthorized services, backdoors, or persistence mechanisms.", + "false_positives": [ + "A newly installed program or one that rarely uses the network could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "linux_anomalous_network_service", + "name": "Unusual Linux Network Service", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "52afbdc5-db15-596e-bc35-f5707f820c4b", + "severity": "low", + "tags": [ + "Elastic", + "Linux", + "ML" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_url_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_url_activity.json new file mode 100644 index 0000000000000..40dd2e76c7214 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_network_url_activity.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected an unusual web URL request from a Linux host, which can indicate malware delivery and execution. Wget and cURL are commonly used by Linux programs to download code and data. Most of the time, their usage is entirely normal. Generally, because they use a list of URLs, they repeatedly download from the same locations. However, Wget and cURL are sometimes used to deliver Linux exploit payloads, and threat actors use these tools to download additional software and code. For these reasons, unusual URLs can indicate unauthorized downloads or threat activity.", + "false_positives": [ + "A new and unusual program or artifact download in the course of software upgrades, debugging, or troubleshooting could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "linux_anomalous_network_url_activity_ecs", + "name": "Unusual Linux Web Activity", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "52afbdc5-db15-485e-bc35-f5707f820c4c", + "severity": "low", + "tags": [ + "Elastic", + "Linux", + "ML" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_process_all_hosts.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_process_all_hosts.json new file mode 100644 index 0000000000000..6bac2f25fd7de --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_process_all_hosts.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "Searches for rare processes running on multiple Linux hosts in an entire fleet or network. This reduces the detection of false positives since automated maintenance processes usually only run occasionally on a single machine but are common to all or many hosts in a fleet.", + "false_positives": [ + "A newly installed program or one that runs rarely as part of a monthly or quarterly workflow could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "linux_anomalous_process_all_hosts_ecs", + "name": "Anomalous Process For a Linux Population", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "647fc812-7996-4795-8869-9c4ea595fe88", + "severity": "low", + "tags": [ + "Elastic", + "Linux", + "ML" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_user_name.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_user_name.json new file mode 100644 index 0000000000000..8b7e6c89482f7 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_anomalous_user_name.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected activity for a username that is not normally active, which can indicate unauthorized changes, activity by unauthorized users, lateral movement, or compromised credentials. In many organizations, new usernames are not often created apart from specific types of system activities, such as creating new accounts for new employees. These user accounts quickly become active and routine. Events from rarely used usernames can point to suspicious activity. Additionally, automated Linux fleets tend to see activity from rarely used usernames only when personnel log in to make authorized or unauthorized changes, or threat actors have acquired credentials and log in for malicious purposes. Unusual usernames can also indicate pivoting, where compromised credentials are used to try and move laterally from one host to another.", + "false_positives": [ + "Uncommon user activity can be due to an engineer logging onto a server instance in order to perform manual troubleshooting or reconfiguration." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "linux_anomalous_user_name_ecs", + "name": "Unusual Linux Username", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "b347b919-665f-4aac-b9e8-68369bf2340c", + "severity": "low", + "tags": [ + "Elastic", + "Linux", + "ML" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file 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 index c4b14389c0f75..bb8e8983661e6 100644 --- 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 @@ -7,9 +7,8 @@ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Hping Process Activity", - "query": "process.name: (hping3 or hping2 or hping) and event.action:executed", + "query": "process.name:(hping or hping2 or hping3) and event.action:executed", "references": [ "https://en.wikipedia.org/wiki/Hping" ], @@ -22,4 +21,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index b8455a4d2b21d..4e49702855a76 100644 --- 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 @@ -7,9 +7,8 @@ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Potential DNS Tunneling via Iodine", - "query": "process.name: (iodine or iodined) and event.action:executed", + "query": "process.name:(iodine or iodined) and event.action:executed", "references": [ "https://code.kryo.se/iodine/" ], @@ -22,4 +21,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index f1d12de674488..cf8cd72b7aa6f 100644 --- 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 @@ -7,9 +7,8 @@ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Persistence via Kernel Module Modification", - "query": "process.name: (insmod or kmod or modprobe or rmod) and event.action:executed", + "query": "process.name:(insmod or kmod or modprobe or rmod) and event.action:executed", "references": [ "https://www.hackers-arise.com/single-post/2017/11/03/Linux-for-Hackers-Part-10-Loadable-Kernel-Modules-LKM" ], @@ -39,4 +38,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index b0d4c29dc00d3..3bd3848c07581 100644 --- 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 @@ -1,5 +1,5 @@ { - "description": "The Linux mknod program is sometimes used in the command payload of a remote command injection (RCI) and other exploits. It is used to export a command shell when the traditional version of netcat is not available to the payload.", + "description": "The Linux mknod program is sometimes used in the command payload of a remote command injection (RCI) and other exploits. It is used to export a command shell when the traditional version of netcat is not available to the payload.", "false_positives": [ "Mknod is a Linux system program. Some normal use of this program, at varying levels of frequency, may originate from scripts, automation tools, and frameworks. Usage by web servers is more likely to be suspicious." ], @@ -7,9 +7,8 @@ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Mknod Process Activity", - "query": "process.name: mknod and event.action:executed", + "query": "process.name:mknod and event.action:executed", "references": [ "https://pen-testing.sans.org/blog/2013/05/06/netcat-without-e-no-problem" ], @@ -22,4 +21,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index 6ab1c1285c0d7..cd523b6594ccd 100644 --- 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 @@ -7,9 +7,8 @@ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Netcat Network Activity", - "query": "process.name: (nc or ncat or netcat or netcat.openbsd or netcat.traditional) and event.action: (connected-to or bound-socket or socket_opened)", + "query": "process.name:(nc or ncat or netcat or netcat.openbsd or netcat.traditional) and event.action:(bound-socket or connected-to or socket_opened)", "references": [ "http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet", "https://www.sans.org/security-resources/sec560/netcat_cheat_sheet_v1.pdf", @@ -24,4 +23,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index 5d7169219a6f1..604cfa172fd84 100644 --- 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 @@ -1,5 +1,5 @@ { - "description": "Nmap was executed on a Linux host. Nmap is a FOSS tool for network scanning and security testing. It can map and discover networks, and identify listening services and operating systems. It is sometimes used to gather information in support of exploitation, execution or lateral movement.", + "description": "Nmap was executed on a Linux host. Nmap is a FOSS tool for network scanning and security testing. It can map and discover networks, and identify listening services and operating systems. It is sometimes used to gather information in support of exploitation, execution or lateral movement.", "false_positives": [ "Security testing tools and frameworks may run `Nmap` in the course of security auditing. Some normal use of this command may originate from security engineers and network or server administrators. Use of nmap by ordinary users is uncommon." ], @@ -7,9 +7,8 @@ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Nmap Process Activity", - "query": "process.name: nmap", + "query": "process.name:nmap", "references": [ "https://en.wikipedia.org/wiki/Nmap" ], @@ -22,4 +21,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index 6a713d22e3219..8e71b5b906711 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "Nping ran on a Linux host. Nping is part of the Nmap tool suite and has the ability to construct raw packets for a wide variety of security testing applications, including denial of service testing.", + "description": "Nping ran on a Linux host. Nping is part of the Nmap tool suite and has the ability to construct raw packets for a wide variety of security testing applications, including denial of service testing.", "false_positives": [ - "Some normal use of this command may originate from security engineers and network or server administrators, but this is usually not routine or unannounced. Use of `Nping` by non-engineers or ordinary users is uncommon." + "Some normal use of this command may originate from security engineers and network or server administrators, but this is usually not routine or unannounced. Use of `Nping` by non-engineers or ordinary users is uncommon." ], "index": [ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Nping Process Activity", - "query": "process.name: nping and event.action:executed", + "query": "process.name:nping and event.action:executed", "references": [ "https://en.wikipedia.org/wiki/Nmap" ], @@ -22,4 +21,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index c80bb4eb41615..c50026d7736ae 100644 --- 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 @@ -1,15 +1,14 @@ { "description": "Identifies processes running in a temporary folder. This is sometimes done by adversaries to hide malware.", "false_positives": [ - "Build systems, like Jenkins, may start processes in the `/tmp` directory. These can be exempted by name or by username." + "Build systems, like Jenkins, may start processes in the `/tmp` directory. These can be exempted by name or by username." ], "index": [ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Unusual Process Execution - Temp", - "query": "process.working_directory: /tmp and event.action:executed", + "query": "process.working_directory:/tmp and event.action:executed", "risk_score": 47, "rule_id": "df959768-b0c9-4d45-988c-5606a2be8e5a", "severity": "medium", @@ -19,4 +18,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index d9455ab7d5b3e..01f117e0a225b 100644 --- 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 @@ -7,9 +7,8 @@ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Potential Shell via Web Server", - "query": "process.name: bash and user.name: (apache or www or \"www-data\") and event.action:executed", + "query": "process.name:bash and user.name:(apache or www or www-data) and event.action:executed", "references": [ "https://pentestlab.blog/tag/web-shell/" ], @@ -39,4 +38,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json index 56fb41dc5f78f..a16b164e9ee4a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json @@ -1,15 +1,14 @@ { "description": "A Socat process is running on a Linux host. Socat is often used as a persistence mechanism by exporting a reverse shell, or by serving a shell on a listening port. Socat is also sometimes used for lateral movement.", "false_positives": [ - "Socat is a dual-use tool that can be used for benign or malicious activity. Some normal use of this program, at varying levels of frequency, may originate from scripts, automation tools, and frameworks. Usage by web servers is more likely to be suspicious." + "Socat is a dual-use tool that can be used for benign or malicious activity. Some normal use of this program, at varying levels of frequency, may originate from scripts, automation tools, and frameworks. Usage by web servers is more likely to be suspicious." ], "index": [ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Socat Process Activity", - "query": "process.name:socat and not process.args:\"-V\" and event.action:executed", + "query": "process.name:socat and not process.args:-V and event.action:executed", "references": [ "https://blog.ropnop.com/upgrading-simple-shells-to-fully-interactive-ttys/#method-2-using-socat" ], @@ -22,4 +21,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index fdf52e7c728c6..9b18039b63fd0 100644 --- 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 @@ -7,9 +7,8 @@ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Strace Process Activity", - "query": "process.name: strace and event.action:executed", + "query": "process.name:strace and event.action:executed", "references": [ "https://en.wikipedia.org/wiki/Strace" ], @@ -22,4 +21,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index 908e892026ee3..5ae48c8db9984 100644 --- 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 @@ -7,9 +7,8 @@ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Network Sniffing via Tcpdump", - "query": "process.name: tcpdump and event.action:executed", + "query": "process.name:tcpdump and event.action:executed", "risk_score": 21, "rule_id": "7a137d76-ce3d-48e2-947d-2747796a78c0", "severity": "low", @@ -51,4 +50,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index 052ff34d15dcd..7fef4e813da98 100644 --- 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 @@ -1,5 +1,5 @@ { - "description": "The whoami application was executed on a Linux host. This is often used by tools and persistence mechanisms to test for privileged access.", + "description": "The whoami application was executed on a Linux host. This is often used by tools and persistence mechanisms to test for privileged access.", "false_positives": [ "Security testing tools and frameworks may run this command. Some normal use of this command may originate from automation tools and frameworks." ], @@ -7,9 +7,8 @@ "auditbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "User Discovery via Whoami", - "query": "process.name: whoami and event.action:executed", + "query": "process.name:whoami and event.action:executed", "risk_score": 21, "rule_id": "120559c6-5e24-49f4-9e30-8ffe697df6b9", "severity": "low", @@ -36,4 +35,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index 56c11c236eecb..e08d681d14463 100644 --- 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 @@ -1,5 +1,5 @@ { - "description": "This rule detects when an internal network client sends DNS traffic directly to the Internet.\nThis is atypical behavior for a managed network, and can be indicative of malware,\nexfiltration, command and control, or, simply, misconfiguration. This DNS activity also impacts your\norganization's ability to provide enterprise monitoring and logging of DNS, and opens\nyour network to a variety of abuses and malicious communications.\n", + "description": "This rule detects when an internal network client sends DNS traffic directly to the Internet. This is atypical behavior for a managed network, and can be indicative of malware, exfiltration, command and control, or, simply, misconfiguration. This DNS activity also impacts your organization's ability to provide enterprise monitoring and logging of DNS, and opens your network to a variety of abuses and malicious communications.", "false_positives": [ "Exclude DNS servers from this rule as this is expected behavior. Endpoints usually query local DNS servers defined in their DHCP scopes, but this may be overridden if a user configures their endpoint to use a remote DNS server. This is uncommon in managed enterprise networks because it could break intranet name resolution when split horizon DNS is utilized. Some consumer VPN services and browser plug-ins may send DNS traffic to remote Internet destinations. In that case, such devices or networks can be excluded from this rule when this is expected behavior." ], @@ -7,9 +7,8 @@ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "DNS Activity to the Internet", - "query": "destination.port:53 and (\n network.direction: outbound or (\n source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip:( 169.254.169.254/32 or 127.0.0.53/32 or 10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.251 or ff02\\:\\:fb or 255.255.255.255 )\n )\n)\n", + "query": "destination.port:53 and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 169.254.169.254/32 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.251 or 224.0.0.252 or 255.255.255.255 or \"::1\" or \"ff02::fb\")", "references": [ "https://www.us-cert.gov/ncas/alerts/TA15-240A", "https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-81-2.pdf" @@ -39,5 +38,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index a3a692596090c..24c3bad817227 100644 --- 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 @@ -1,5 +1,5 @@ { - "description": "This rule detects events that may indicate the use of FTP network connections to the Internet.\nThe File Transfer Protocol (FTP) has been around in its current form since the\n1980s. It can be a common and efficient procedure on your network to send and\nreceive files. Because of this, adversaries will also often use this protocol\nto exfiltrate data from your network or download new tools. Additionally, FTP\nis a plain-text protocol which, if intercepted, may expose usernames and\npasswords. FTP activity involving servers subject to regulations or compliance\nstandards may be unauthorized.\n", + "description": "This rule detects events that may indicate the use of FTP network connections to the Internet. The File Transfer Protocol (FTP) has been around in its current form since the 1980s. It can be a common and efficient procedure on your network to send and receive files. Because of this, adversaries will also often use this protocol to exfiltrate data from your network or download new tools. Additionally, FTP is a plain-text protocol which, if intercepted, may expose usernames and passwords. FTP activity involving servers subject to regulations or compliance standards may be unauthorized.", "false_positives": [ "FTP servers should be excluded from this rule as this is expected behavior. Some business workflows may use FTP for data exchange. These workflows often have expected characteristics such as users, sources, and destinations. FTP activity involving an unusual source or destination may be more suspicious. FTP activity involving a production server that has no known associated FTP workflow or business requirement is often suspicious." ], @@ -7,9 +7,8 @@ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "FTP (File Transfer Protocol) Activity to the Internet", - "query": "network.transport: tcp and destination.port: (20 or 21) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:(20 or 21) and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 21, "rule_id": "87ec6396-9ac4-4706-bcf0-2ebb22002f43", "severity": "low", @@ -50,5 +49,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index 0b5259d3417f5..bf286d4cab506 100644 --- 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 @@ -1,5 +1,5 @@ { - "description": "This rule detects events that use common ports for Internet Relay Chat (IRC) to the Internet.\nIRC is a common protocol that can be used for chat and file transfers. This\nprotocol is also a good candidate for remote control of malware and data\ntransfers to and from a network.\n", + "description": "This rule detects events that use common ports for Internet Relay Chat (IRC) to the Internet. IRC is a common protocol that can be used for chat and file transfers. This protocol is also a good candidate for remote control of malware and data transfers to and from a network.", "false_positives": [ "IRC activity may be normal behavior for developers and engineers but is unusual for non-engineering end users. IRC activity involving an unusual source or destination may be more suspicious. IRC activity involving a production server is often suspicious. Because these ports are in the ephemeral range, this rule may false under certain conditions, such as when a NAT-ed web server replies to a client which has used a port in the range by coincidence. In this case, these servers can be excluded. Some legacy applications may use these ports, but this is very uncommon and usually only appears in local traffic using private IPs, which does not match this rule's conditions." ], @@ -7,9 +7,8 @@ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "IRC (Internet Relay Chat) Protocol Activity to the Internet", - "query": "network.transport: tcp and destination.port:(6667 or 6697) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:(6667 or 6697) and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 47, "rule_id": "c6474c34-4953-447a-903e-9fcb7b6661aa", "severity": "medium", @@ -50,5 +49,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index 675fd588a1834..61c1e3d47cf7a 100644 --- 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 @@ -1,5 +1,5 @@ { - "description": "This rule detects events that could be describing IPSEC NAT Traversal traffic.\nIPSEC is a VPN technology that allows one system to talk to another using\nencrypted tunnels. NAT Traversal enables these tunnels to communicate over the\nInternet where one of the sides is behind a NAT router gateway. This may be\ncommon on your network, but this technique is also used by threat actors to\navoid detection.\n", + "description": "This rule detects events that could be describing IPSEC NAT Traversal traffic. IPSEC is a VPN technology that allows one system to talk to another using encrypted tunnels. NAT Traversal enables these tunnels to communicate over the Internet where one of the sides is behind a NAT router gateway. This may be common on your network, but this technique is also used by threat actors to avoid detection.", "false_positives": [ "Some networks may utilize these protocols but usage that is unfamiliar to local network administrators can be unexpected and suspicious. Because this port is in the ephemeral range, this rule may false under certain conditions, such as when an application server with a public IP address replies to a client which has used a UDP port in the range by coincidence. This is uncommon but such servers can be excluded." ], @@ -7,9 +7,8 @@ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "IPSEC NAT Traversal Port Activity", - "query": "network.transport: udp and destination.port: 4500", + "query": "network.transport:udp and destination.port:4500", "risk_score": 21, "rule_id": "a9cb3641-ff4b-4cdc-a063-b4b8d02a67c7", "severity": "low", @@ -36,4 +35,4 @@ ], "type": "query", "version": 2 -} +} \ No newline at end of file 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 index bc00383f94528..a9a39b61884c5 100644 --- 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 @@ -1,5 +1,5 @@ { - "description": "This rule detects events that may indicate use of SMTP on TCP port 26. This\nport is commonly used by several popular mail transfer agents to deconflict\nwith the default SMTP port 25. This port has also been used by a malware family\ncalled BadPatch for command and control of Windows systems.\n", + "description": "This rule detects events that may indicate use of SMTP on TCP port 26. This port is commonly used by several popular mail transfer agents to deconflict with the default SMTP port 25. This port has also been used by a malware family called BadPatch for command and control of Windows systems.", "false_positives": [ "Servers that process email traffic may cause false positives and should be excluded from this rule as this is expected behavior." ], @@ -7,9 +7,8 @@ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "SMTP on Port 26/TCP", - "query": "network.transport: tcp and destination.port: 26\n", + "query": "network.transport:tcp and destination.port:26", "references": [ "https://unit42.paloaltonetworks.com/unit42-badpatch/", "https://isc.sans.edu/forums/diary/Next+up+whats+up+with+TCP+port+26/25564/" @@ -55,4 +54,4 @@ ], "type": "query", "version": 2 -} +} \ No newline at end of file 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 index f418648bebdb9..2f1390411f97b 100644 --- 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 @@ -1,5 +1,5 @@ { - "description": "TCP Port 8000 is commonly used for development environments of web server\nsoftware. It generally should not be exposed directly to the Internet. If you are\nrunning software like this on the Internet, you should consider placing it behind\na reverse proxy.\n", + "description": "TCP Port 8000 is commonly used for development environments of web server software. It generally should not be exposed directly to the Internet. If you are running software like this on the Internet, you should consider placing it behind a reverse proxy.", "false_positives": [ "Because this port is in the ephemeral range, this rule may false under certain conditions, such as when a NATed web server replies to a client which has used a port in the range by coincidence. In this case, such servers can be excluded. Some applications may use this port but this is very uncommon and usually appears in local traffic using private IPs, which this rule does not match. Some cloud environments, particularly development environments, may use this port when VPNs or direct connects are not in use and cloud instances are accessed across the Internet." ], @@ -7,9 +7,8 @@ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "TCP Port 8000 Activity to the Internet", - "query": "network.transport: tcp and destination.port: 8000 and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:8000 and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 21, "rule_id": "08d5d7e2-740f-44d8-aeda-e41f4263efaf", "severity": "low", @@ -35,5 +34,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index 2321b813a1552..f7170d8d33a51 100644 --- 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 @@ -1,5 +1,5 @@ { - "description": "This rule detects events that may indicate use of a PPTP VPN connection. Some\nthreat actors use these types of connections to tunnel their traffic while\navoiding detection.\n", + "description": "This rule detects events that may indicate use of a PPTP VPN connection. Some threat actors use these types of connections to tunnel their traffic while avoiding detection.", "false_positives": [ "Some networks may utilize PPTP protocols but this is uncommon as more modern VPN technologies are available. Usage that is unfamiliar to local network administrators can be unexpected and suspicious. Torrenting applications may use this port. Because this port is in the ephemeral range, this rule may false under certain conditions, such as when an application server replies to a client that used this port by coincidence. This is uncommon but such servers can be excluded." ], @@ -7,9 +7,8 @@ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "PPTP (Point to Point Tunneling Protocol) Activity", - "query": "network.transport: tcp and destination.port: 1723\n", + "query": "network.transport:tcp and destination.port:1723", "risk_score": 21, "rule_id": "d2053495-8fe7-4168-b3df-dad844046be3", "severity": "low", @@ -36,4 +35,4 @@ ], "type": "query", "version": 2 -} +} \ No newline at end of file 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 index 58bba5b3fa712..da4319cf15307 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "This rule detects events that may describe network events of proxy use to the\nInternet. It includes popular HTTP proxy ports and SOCKS proxy ports. Typically,\nenvironments will use an internal IP address for a proxy server. It can also\nbe used to circumvent network controls and detection mechanisms.\n", + "description": "This rule detects events that may describe network events of proxy use to the Internet. It includes popular HTTP proxy ports and SOCKS proxy ports. Typically, environments will use an internal IP address for a proxy server. It can also be used to circumvent network controls and detection mechanisms.", "false_positives": [ - "Some proxied applications may use these ports but this usually occurs in local traffic using private IPs\n which this rule does not match. Proxies are widely used as a security technology but in enterprise environments\n this is usually local traffic which this rule does not match. Internet proxy services using these ports can be\n white-listed if desired. Some screen recording applications may use these ports. Proxy port activity involving\n an unusual source or destination may be more suspicious. Some cloud environments may use this port when VPNs or\n direct connects are not in use and cloud instances are accessed across the Internet. Because these ports are in\n the ephemeral range, this rule may false under certain conditions such as when a NATed web server replies to a\n client which has used a port in the range by coincidence. In this case, such servers can be excluded if desired." + "Some proxied applications may use these ports but this usually occurs in local traffic using private IPs which this rule does not match. Proxies are widely used as a security technology but in enterprise environments this is usually local traffic which this rule does not match. Internet proxy services using these ports can be white-listed if desired. Some screen recording applications may use these ports. Proxy port activity involving an unusual source or destination may be more suspicious. Some cloud environments may use this port when VPNs or direct connects are not in use and cloud instances are accessed across the Internet. Because these ports are in the ephemeral range, this rule may false under certain conditions such as when a NATed web server replies to a client which has used a port in the range by coincidence. In this case, such servers can be excluded if desired." ], "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Proxy Port Activity to the Internet", - "query": "network.transport: tcp and destination.port: (3128 or 8080 or 1080) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:(1080 or 3128 or 8080) and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 47, "rule_id": "ad0e5e75-dd89-4875-8d0a-dfdc1828b5f3", "severity": "medium", @@ -35,5 +34,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index 03e507753cd22..d3b65a36f084b 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "This rule detects network events that may indicate the use of RDP traffic\nfrom the Internet. RDP is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targeted and\nexploited by threat actors as an initial access or back-door vector.\n", + "description": "This rule detects network events that may indicate the use of RDP traffic from the Internet. RDP is commonly used by system administrators to remotely control a system for maintenance or to use shared resources. It should almost never be directly exposed to the Internet, as it is frequently targeted and exploited by threat actors as an initial access or back-door vector.", "false_positives": [ - " Some network security policies allow RDP directly from the Internet but usage that is unfamiliar to\n server or network owners can be unexpected and suspicious. RDP services may be exposed directly to the\n Internet in some networks such as cloud environments. In such cases, only RDP gateways, bastions or jump\n servers may be expected expose RDP directly to the Internet and can be exempted from this rule. RDP may\n be required by some work-flows such as remote access and support for specialized software products and\n servers. Such work-flows are usually known and not unexpected." + "Some network security policies allow RDP directly from the Internet but usage that is unfamiliar to server or network owners can be unexpected and suspicious. RDP services may be exposed directly to the Internet in some networks such as cloud environments. In such cases, only RDP gateways, bastions or jump servers may be expected expose RDP directly to the Internet and can be exempted from this rule. RDP may be required by some work-flows such as remote access and support for specialized software products and servers. Such work-flows are usually known and not unexpected." ], "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "RDP (Remote Desktop Protocol) from the Internet", - "query": "network.transport: tcp and destination.port: 3389 and (\n network.direction: inbound or (\n not source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n and destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:3389 and not source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 47, "rule_id": "8c1bdde8-4204-45c0-9e0c-c85ca3902488", "severity": "medium", @@ -65,5 +64,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index af2279b2d9008..79618a867c73f 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "This rule detects network events that may indicate the use of RDP traffic\nto the Internet. RDP is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targeted and\nexploited by threat actors as an initial access or back-door vector.\n", + "description": "This rule detects network events that may indicate the use of RDP traffic to the Internet. RDP is commonly used by system administrators to remotely control a system for maintenance or to use shared resources. It should almost never be directly exposed to the Internet, as it is frequently targeted and exploited by threat actors as an initial access or back-door vector.", "false_positives": [ - "RDP connections may be made directly to Internet destinations in order to access\n Windows cloud server instances but such connections are usually made only by engineers.\n In such cases, only RDP gateways, bastions or jump servers may be expected Internet\n destinations and can be exempted from this rule. RDP may be required by some work-flows\n such as remote access and support for specialized software products and servers. Such\n work-flows are usually known and not unexpected. Usage that is unfamiliar to server or\n network owners can be unexpected and suspicious." + "RDP connections may be made directly to Internet destinations in order to access Windows cloud server instances but such connections are usually made only by engineers. In such cases, only RDP gateways, bastions or jump servers may be expected Internet destinations and can be exempted from this rule. RDP may be required by some work-flows such as remote access and support for specialized software products and servers. Such work-flows are usually known and not unexpected. Usage that is unfamiliar to server or network owners can be unexpected and suspicious." ], "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "RDP (Remote Desktop Protocol) to the Internet", - "query": "network.transport: tcp and destination.port: 3389 and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:3389 and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 21, "rule_id": "e56993d2-759c-4120-984c-9ec9bb940fd5", "severity": "low", @@ -50,5 +49,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index 4539d639a593a..da1e46750f3bd 100644 --- 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 @@ -1,12 +1,11 @@ { - "description": "This rule detects network events that may indicate the use of RPC traffic\nfrom the Internet. RPC is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targeted and\nexploited by threat actors as an initial access or back-door vector.\n", + "description": "This rule detects network events that may indicate the use of RPC traffic from the Internet. RPC is commonly used by system administrators to remotely control a system for maintenance or to use shared resources. It should almost never be directly exposed to the Internet, as it is frequently targeted and exploited by threat actors as an initial access or back-door vector.", "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "RPC (Remote Procedure Call) from the Internet", - "query": "network.transport: tcp and destination.port: 135 and (\n network.direction: inbound or (\n not source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:135 and not source.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\") and destination.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)", "risk_score": 73, "rule_id": "143cb236-0956-4f42-a706-814bcaa0cf5a", "severity": "high", @@ -32,5 +31,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index dd1b57572bcb3..d07d19b8fffee 100644 --- 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 @@ -1,12 +1,11 @@ { - "description": "This rule detects network events that may indicate the use of RPC traffic\nto the Internet. RPC is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targeted and\nexploited by threat actors as an initial access or back-door vector.\n", + "description": "This rule detects network events that may indicate the use of RPC traffic to the Internet. RPC is commonly used by system administrators to remotely control a system for maintenance or to use shared resources. It should almost never be directly exposed to the Internet, as it is frequently targeted and exploited by threat actors as an initial access or back-door vector.", "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "RPC (Remote Procedure Call) to the Internet", - "query": "network.transport: tcp and destination.port: 135 and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:135 and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 73, "rule_id": "32923416-763a-4531-bb35-f33b9232ecdb", "severity": "high", @@ -32,5 +31,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index 8b97df2182992..93a4b0ebbbd8e 100644 --- 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 @@ -1,12 +1,11 @@ { - "description": "This rule detects network events that may indicate the use of Windows\nfile sharing (also called SMB or CIFS) traffic to the Internet. SMB is commonly\nused within networks to share files, printers, and other system resources amongst\ntrusted systems. It should almost never be directly exposed to the Internet, as\nit is frequently targeted and exploited by threat actors as an initial access\nor back-door vector or for data exfiltration.\n", + "description": "This rule detects network events that may indicate the use of Windows file sharing (also called SMB or CIFS) traffic to the Internet. SMB is commonly used within networks to share files, printers, and other system resources amongst trusted systems. It should almost never be directly exposed to the Internet, as it is frequently targeted and exploited by threat actors as an initial access or back-door vector or for data exfiltration.", "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "SMB (Windows File Sharing) Activity to the Internet", - "query": "network.transport: tcp and destination.port: (139 or 445) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:(139 or 445) and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 73, "rule_id": "c82b2bd8-d701-420c-ba43-f11a155b681a", "severity": "high", @@ -47,5 +46,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index c6aa5eef372f4..ca287605490ef 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "This rule detects events that may describe SMTP traffic from internal\nhosts to a host across the Internet. In an enterprise network, there is typically\na dedicated internal host that performs this function. It is also\nfrequently abused by threat actors for command and control, or data exfiltration.\n", + "description": "This rule detects events that may describe SMTP traffic from internal hosts to a host across the Internet. In an enterprise network, there is typically a dedicated internal host that performs this function. It is also frequently abused by threat actors for command and control, or data exfiltration.", "false_positives": [ - "NATed servers that process email traffic may false and should be excluded from this rule as this is expected behavior for them. Consumer and personal devices may send email traffic to remote Internet destinations. In this case, such devices or networks can be excluded from this rule if this is expected behavior." + "NATed servers that process email traffic may false and should be excluded from this rule as this is expected behavior for them. Consumer and personal devices may send email traffic to remote Internet destinations. In this case, such devices or networks can be excluded from this rule if this is expected behavior." ], "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "SMTP to the Internet", - "query": "network.transport: tcp and destination.port: (25 or 465 or 587) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:(25 or 465 or 587) and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 21, "rule_id": "67a9beba-830d-4035-bfe8-40b7e28f8ac4", "severity": "low", @@ -50,5 +49,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index f11d9705bbda4..3a5bd5bff98f5 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "This rule detects events that may describe database traffic\n(MS SQL, Oracle, MySQL, and Postgresql) across the Internet. Databases\nshould almost never be directly exposed to the Internet, as they are\nfrequently targeted by threat actors to gain initial access to network resources.\n", + "description": "This rule detects events that may describe database traffic (MS SQL, Oracle, MySQL, and Postgresql) across the Internet. Databases should almost never be directly exposed to the Internet, as they are frequently targeted by threat actors to gain initial access to network resources.", "false_positives": [ - "Because these ports are in the ephemeral range, this rule may false under certain conditions\n such as when a NATed web server replies to a client which has used a port in the range by\n coincidence. In this case, such servers can be excluded if desired. Some cloud environments may\n use this port when VPNs or direct connects are not in use and database instances are accessed\n directly across the Internet." + "Because these ports are in the ephemeral range, this rule may false under certain conditions such as when a NATed web server replies to a client which has used a port in the range by coincidence. In this case, such servers can be excluded if desired. Some cloud environments may use this port when VPNs or direct connects are not in use and database instances are accessed directly across the Internet." ], "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "SQL Traffic to the Internet", - "query": "network.transport: tcp and destination.port: (1433 or 1521 or 3336 or 5432) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:(1433 or 1521 or 3336 or 5432) and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 47, "rule_id": "139c7458-566a-410c-a5cd-f80238d6a5cd", "severity": "medium", @@ -35,5 +34,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index a95447fc088df..429a91183e88a 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "This rule detects network events that may indicate the use of SSH traffic\nfrom the Internet. SSH is commonly used by system administrators to remotely\ncontrol a system using the command line shell. If it is exposed to the Internet,\nit should be done with strong security controls as it is frequently targeted and\nexploited by threat actors as an initial access or back-door vector.\n", + "description": "This rule detects network events that may indicate the use of SSH traffic from the Internet. SSH is commonly used by system administrators to remotely control a system using the command line shell. If it is exposed to the Internet, it should be done with strong security controls as it is frequently targeted and exploited by threat actors as an initial access or back-door vector.", "false_positives": [ - "Some network security policies allow SSH directly from the Internet but usage that is\n unfamiliar to server or network owners can be unexpected and suspicious. SSH services may\n be exposed directly to the Internet in some networks such as cloud environments. In such\n cases, only SSH gateways, bastions or jump servers may be expected expose SSH directly to\n the Internet and can be exempted from this rule. SSH may be required by some work-flows\n such as remote access and support for specialized software products and servers. Such\n work-flows are usually known and not unexpected." + "Some network security policies allow SSH directly from the Internet but usage that is unfamiliar to server or network owners can be unexpected and suspicious. SSH services may be exposed directly to the Internet in some networks such as cloud environments. In such cases, only SSH gateways, bastions or jump servers may be expected expose SSH directly to the Internet and can be exempted from this rule. SSH may be required by some work-flows such as remote access and support for specialized software products and servers. Such work-flows are usually known and not unexpected." ], "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "SSH (Secure Shell) from the Internet", - "query": "network.transport: tcp and destination.port:22 and (\n network.direction: inbound or (\n not source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:22 and not source.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\") and destination.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)", "risk_score": 47, "rule_id": "ea0784f0-a4d7-4fea-ae86-4baaf27a6f17", "severity": "medium", @@ -65,5 +64,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index b17d35f96324d..a260245b4dade 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "This rule detects network events that may indicate the use of SSH traffic\nfrom the Internet. SSH is commonly used by system administrators to remotely\ncontrol a system using the command line shell. If it is exposed to the Internet,\nit should be done with strong security controls as it is frequently targeted and\nexploited by threat actors as an initial access or back-door vector.\n", + "description": "This rule detects network events that may indicate the use of SSH traffic from the Internet. SSH is commonly used by system administrators to remotely control a system using the command line shell. If it is exposed to the Internet, it should be done with strong security controls as it is frequently targeted and exploited by threat actors as an initial access or back-door vector.", "false_positives": [ - "SSH connections may be made directly to Internet destinations in order to access Linux\n cloud server instances but such connections are usually made only by engineers. In such cases,\n only SSH gateways, bastions or jump servers may be expected Internet destinations and can be\n exempted from this rule. SSH may be required by some work-flows such as remote access and support\n for specialized software products and servers. Such work-flows are usually known and not unexpected.\n Usage that is unfamiliar to server or network owners can be unexpected and suspicious." + "SSH connections may be made directly to Internet destinations in order to access Linux cloud server instances but such connections are usually made only by engineers. In such cases, only SSH gateways, bastions or jump servers may be expected Internet destinations and can be exempted from this rule. SSH may be required by some work-flows such as remote access and support for specialized software products and servers. Such work-flows are usually known and not unexpected. Usage that is unfamiliar to server or network owners can be unexpected and suspicious." ], "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "SSH (Secure Shell) to the Internet", - "query": "network.transport: tcp and destination.port:22 and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:22 and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 21, "rule_id": "6f1500bc-62d7-4eb9-8601-7485e87da2f4", "severity": "low", @@ -35,5 +34,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index 99813595013cf..4cfe15683c825 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "This rule detects network events that may indicate the use of Telnet traffic.\nTelnet is commonly used by system administrators to remotely control older or embed ed\nsystems using the command line shell. It should almost never be directly exposed to\nthe Internet, as it is frequently targeted and exploited by threat actors as an\ninitial access or back-door vector. As a plain-text protocol, it may also expose\nusernames and passwords to anyone capable of observing the traffic.\n", + "description": "This rule detects network events that may indicate the use of Telnet traffic. Telnet is commonly used by system administrators to remotely control older or embed ed systems using the command line shell. It should almost never be directly exposed to the Internet, as it is frequently targeted and exploited by threat actors as an initial access or back-door vector. As a plain-text protocol, it may also expose usernames and passwords to anyone capable of observing the traffic.", "false_positives": [ - "IoT (Internet of Things) devices and networks may use telnet and can be excluded if\n desired. Some business work-flows may use Telnet for administration of older devices. These\n often have a predictable behavior. Telnet activity involving an unusual source or destination\n may be more suspicious. Telnet activity involving a production server that has no known\n associated Telnet work-flow or business requirement is often suspicious." + "IoT (Internet of Things) devices and networks may use telnet and can be excluded if desired. Some business work-flows may use Telnet for administration of older devices. These often have a predictable behavior. Telnet activity involving an unusual source or destination may be more suspicious. Telnet activity involving a production server that has no known associated Telnet work-flow or business requirement is often suspicious." ], "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Telnet Port Activity", - "query": "network.transport: tcp and destination.port: 23", + "query": "network.transport:tcp and destination.port:23", "risk_score": 47, "rule_id": "34fde489-94b0-4500-a76f-b8a157cf9269", "severity": "medium", @@ -66,4 +65,4 @@ ], "type": "query", "version": 2 -} +} \ No newline at end of file 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 index 47960f879dfb6..8c8bb809c9fec 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "This rule detects network events that may indicate the use of Tor traffic\nto the Internet. Tor is a network protocol that sends traffic through a\nseries of encrypted tunnels used to conceal a user's location and usage.\nTor may be used by threat actors as an alternate communication pathway to\nconceal the actor's identity and avoid detection.\n", + "description": "This rule detects network events that may indicate the use of Tor traffic to the Internet. Tor is a network protocol that sends traffic through a series of encrypted tunnels used to conceal a user's location and usage. Tor may be used by threat actors as an alternate communication pathway to conceal the actor's identity and avoid detection.", "false_positives": [ - "Tor client activity is uncommon in managed enterprise networks but may be common\n in unmanaged or public networks where few security policies apply. Because these ports\n are in the ephemeral range, this rule may false under certain conditions such as when a\n NATed web server replies to a client which has used one of these ports by coincidence.\n In this case, such servers can be excluded if desired." + "Tor client activity is uncommon in managed enterprise networks but may be common in unmanaged or public networks where few security policies apply. Because these ports are in the ephemeral range, this rule may false under certain conditions such as when a NATed web server replies to a client which has used one of these ports by coincidence. In this case, such servers can be excluded if desired." ], "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Tor Activity to the Internet", - "query": "network.transport: tcp and destination.port: (9001 or 9030) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port:(9001 or 9030) and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 47, "rule_id": "7d2c38d7-ede7-4bdf-b140-445906e6c540", "severity": "medium", @@ -50,5 +49,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index d9195a2d2e98c..4204a4fe62e88 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "This rule detects network events that may indicate the use of VNC traffic\nfrom the Internet. VNC is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targeted and\nexploited by threat actors as an initial access or back-door vector.\n", + "description": "This rule detects network events that may indicate the use of VNC traffic from the Internet. VNC is commonly used by system administrators to remotely control a system for maintenance or to use shared resources. It should almost never be directly exposed to the Internet, as it is frequently targeted and exploited by threat actors as an initial access or back-door vector.", "false_positives": [ - "VNC connections may be received directly to Linux cloud server instances but\n such connections are usually made only by engineers. VNC is less common than SSH\n or RDP but may be required by some work-flows such as remote access and support\n for specialized software products or servers. Such work-flows are usually known\n and not unexpected. Usage that is unfamiliar to server or network owners can be\n unexpected and suspicious." + "VNC connections may be received directly to Linux cloud server instances but such connections are usually made only by engineers. VNC is less common than SSH or RDP but may be required by some work-flows such as remote access and support for specialized software products or servers. Such work-flows are usually known and not unexpected. Usage that is unfamiliar to server or network owners can be unexpected and suspicious." ], "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "VNC (Virtual Network Computing) from the Internet", - "query": "network.transport: tcp and (destination.port >= 5800 and destination.port <= 5810) and (\n network.direction: inbound or (\n not source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port >= 5800 and destination.port <= 5810 and not source.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\") and destination.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)", "risk_score": 73, "rule_id": "5700cb81-df44-46aa-a5d7-337798f53eb8", "severity": "high", @@ -50,5 +49,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file 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 index 57131e28ee9a7..898282e36df19 100644 --- 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 @@ -1,15 +1,14 @@ { - "description": "This rule detects network events that may indicate the use of VNC traffic\nto the Internet. VNC is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targeted and\nexploited by threat actors as an initial access or back-door vector.", + "description": "This rule detects network events that may indicate the use of VNC traffic to the Internet. VNC is commonly used by system administrators to remotely control a system for maintenance or to use shared resources. It should almost never be directly exposed to the Internet, as it is frequently targeted and exploited by threat actors as an initial access or back-door vector.", "false_positives": [ - "VNC connections may be made directly to Linux cloud server instances but such\n connections are usually made only by engineers. VNC is less common than SSH or RDP\n but may be required by some work flows such as remote access and support for\n specialized software products or servers. Such work-flows are usually known and not\n unexpected. Usage that is unfamiliar to server or network owners can be unexpected\n and suspicious." + "VNC connections may be made directly to Linux cloud server instances but such connections are usually made only by engineers. VNC is less common than SSH or RDP but may be required by some work flows such as remote access and support for specialized software products or servers. Such work-flows are usually known and not unexpected. Usage that is unfamiliar to server or network owners can be unexpected and suspicious." ], "index": [ "filebeat-*" ], "language": "kuery", - "max_signals": 100, "name": "VNC (Virtual Network Computing) to the Internet", - "query": "network.transport: tcp and (destination.port >= 5800 and destination.port <= 5810) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "query": "network.transport:tcp and destination.port >= 5800 and destination.port <= 5810 and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or \"::1\")", "risk_score": 47, "rule_id": "3ad49c61-7adc-42c1-b788-732eda2f5abf", "severity": "medium", @@ -35,5 +34,5 @@ } ], "type": "query", - "version": 2 -} + "version": 3 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json index c08d910a3b355..afbbb2a34d545 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json @@ -25,9 +25,8 @@ "apm-*-transaction*" ], "language": "kuery", - "max_signals": 100, "name": "Web Application Suspicious Activity: No User Agent", - "query": "url.path: *", + "query": "url.path:*", "references": [ "https://en.wikipedia.org/wiki/User_agent" ], @@ -35,9 +34,9 @@ "rule_id": "43303fd4-4839-4e48-b2b2-803ab060758d", "severity": "medium", "tags": [ - "Elastic", - "APM" + "APM", + "Elastic" ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_dns_tunneling.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_dns_tunneling.json new file mode 100644 index 0000000000000..c70725dcb645a --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_dns_tunneling.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected unusually large numbers of DNS queries for a single top-level DNS domain, which is often used for DNS tunneling. DNS tunneling can be used for command-and-control, persistence, or data exfiltration activity. For example, dnscat tends to generate many DNS questions for a top-level domain as it uses the DNS protocol to tunnel data.", + "false_positives": [ + "DNS domains that use large numbers of child domains, such as software or content distribution networks, can trigger this signal and such parent domains can be excluded." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "packetbeat_dns_tunneling", + "name": "DNS Tunneling", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "91f02f01-969f-4167-8f66-07827ac3bdd9", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Packetbeat" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_dns_question.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_dns_question.json new file mode 100644 index 0000000000000..3ed40ddf27864 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_dns_question.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected a rare and unusual DNS query that indicate network activity with unusual DNS domains. This can be due to initial access, persistence, command-and-control, or exfiltration activity. For example, when a user clicks on a link in a phishing email or opens a malicious document, a request may be sent to download and run a payload from an uncommon domain. When malware is already running, it may send requests to an uncommon DNS domain the malware uses for command-and-control communication.", + "false_positives": [ + "A newly installed program or one that runs rarely as part of a monthly or quarterly workflow could trigger this signal. Network activity that occurs rarely, in small quantities, can trigger this signal. Possible examples are browsing technical support or vendor networks sparsely. A user who visits a new or unique web destination may trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "packetbeat_rare_dns_question", + "name": "Unusual DNS Activity", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "746edc4c-c54c-49c6-97a1-651223819448", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Packetbeat" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_server_domain.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_server_domain.json new file mode 100644 index 0000000000000..c49bc95be75d2 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_server_domain.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected an unusual network destination domain name. This can be due to initial access, persistence, command-and-control, or exfiltration activity. For example, when a user clicks on a link in a phishing email or opens a malicious document, a request may be sent to download and run a payload from an uncommon web server name. When malware is already running, it may send requests to an uncommon DNS domain the malware uses for command-and-control communication.", + "false_positives": [ + "Web activity that occurs rarely in small quantities can trigger this signal. Possible examples are browsing technical support or vendor URLs that are used very sparsely. A user who visits a new and unique web destination may trigger this signal when the activity is sparse. Web applications that generate URLs unique to a transaction may trigger this when they are used sparsely. Web domains can be excluded in cases such as these." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "packetbeat_rare_server_domain", + "name": "Unusual Network Destination Domain Name", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "17e68559-b274-4948-ad0b-f8415bb31126", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Packetbeat" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_urls.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_urls.json new file mode 100644 index 0000000000000..02a4a5f729a16 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_urls.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected a rare and unusual URL that indicates unusual web browsing activity. This can be due to initial access, persistence, command-and-control, or exfiltration activity. For example, in a strategic web compromise or watering hole attack, when a trusted website is compromised to target a particular sector or organization, targeted users may receive emails with uncommon URLs for trusted websites. These URLs can be used to download and run a payload. When malware is already running, it may send requests to uncommon URLs on trusted websites the malware uses for command-and-control communication. When rare URLs are observed being requested for a local web server by a remote source, these can be due to web scanning, enumeration or attack traffic, or they can be due to bots and web scrapers which are part of common Internet background traffic.", + "false_positives": [ + "Web activity that occurs rarely in small quantities can trigger this signal. Possible examples are browsing technical support or vendor URLs that are used very sparsely. A user who visits a new and unique web destination may trigger this signal when the activity is sparse. Web applications that generate URLs unique to a transaction may trigger this when they are used sparsely. Web domains can be excluded in cases such as these." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "packetbeat_rare_urls", + "name": "Unusual Web Request", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "91f02f01-969f-4167-8f55-07827ac3acc9", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Packetbeat" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_user_agent.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_user_agent.json new file mode 100644 index 0000000000000..76ed6b263a704 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/packetbeat_rare_user_agent.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected a rare and unusual user agent indicating web browsing activity by an unusual process other than a web browser. This can be due to persistence, command-and-control, or exfiltration activity. Uncommon user agents coming from remote sources to local destinations are often the result of scanners, bots, and web scrapers, which are part of common Internet background traffic. Much of this is noise, but more targeted attacks on websites using tools like Burp or SQLmap can sometimes be discovered by spotting uncommon user agents. Uncommon user agents in traffic from local sources to remote destinations can be any number of things, including harmless programs like weather monitoring or stock-trading programs. However, uncommon user agents from local sources can also be due to malware or scanning activity.", + "false_positives": [ + "Web activity that is uncommon, like security scans, may trigger this signal and may need to be excluded. A new or rarely used program that calls web services may trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "packetbeat_rare_user_agent", + "name": "Unusual Web User Agent", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "91f02f01-969f-4167-8d77-07827ac4cee0", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Packetbeat" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/rare_process_by_host_linux.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/rare_process_by_host_linux.json new file mode 100644 index 0000000000000..048f93e170656 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/rare_process_by_host_linux.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "Identifies rare processes that do not usually run on individual hosts, which can indicate execution of unauthorized services, malware, or persistence mechanisms. Processes are considered rare when they only run occasionally as compared with other processes running on the host.", + "false_positives": [ + "A newly installed program or one that runs rarely as part of a monthly or quarterly workflow could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "rare_process_by_host_linux_ecs", + "name": "Unusual Process For a Linux Host", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "46f804f5-b289-43d6-a881-9387cf594f75", + "severity": "low", + "tags": [ + "Elastic", + "Linux", + "ML" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/rare_process_by_host_windows.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/rare_process_by_host_windows.json new file mode 100644 index 0000000000000..7bc46cdc04dd2 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/rare_process_by_host_windows.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "Identifies rare processes that do not usually run on individual hosts, which can indicate execution of unauthorized services, malware, or persistence mechanisms. Processes are considered rare when they only run occasionally as compared with other processes running on the host.", + "false_positives": [ + "A newly installed program or one that runs rarely as part of a monthly or quarterly workflow could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "rare_process_by_host_windows_ecs", + "name": "Unusual Process For a Windows Host", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "6d448b96-c922-4adb-b51c-b767f1ea5b76", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Windows" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json index 5c03c3a76e4a7..fd240262d021f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json @@ -7,7 +7,6 @@ "apm-*-transaction*" ], "language": "kuery", - "max_signals": 100, "name": "Web Application Suspicious Activity: sqlmap User Agent", "query": "user_agent.original:\"sqlmap/1.3.11#stable (http://sqlmap.org)\"", "references": [ @@ -17,9 +16,9 @@ "rule_id": "d49cc73f-7a16-4def-89ce-9fc7127d7820", "severity": "medium", "tags": [ - "Elastic", - "APM" + "APM", + "Elastic" ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suspicious_login_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suspicious_login_activity.json new file mode 100644 index 0000000000000..915bc1bcfc051 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suspicious_login_activity.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "Identifies an unusually high number of authentication attempts.", + "false_positives": [ + "Security audits may trigger this signal. Conditions that generate bursts of failed logins, such as misconfigured applications or account lockouts could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "suspicious_login_activity_ecs", + "name": "Unusual Login Activity", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "4330272b-9724-4bc6-a3ca-f1532b81e5c2", + "severity": "low", + "tags": [ + "Elastic", + "Linux", + "ML" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_network_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_network_activity.json new file mode 100644 index 0000000000000..72671760c9c8d --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_network_activity.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "Identifies Windows processes that do not usually use the network but have unexpected network activity, which can indicate command-and-control, lateral movement, persistence, or data exfiltration activity. A process with unusual network activity can denote process exploitation or injection, where the process is used to run persistence mechanisms that allow a malicious actor remote access or control of the host, data exfiltration, and execution of unauthorized network applications.", + "false_positives": [ + "A newly installed program or one that rarely uses the network could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "windows_anomalous_network_activity_ecs", + "name": "Unusual Windows Network Activity", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "ba342eb2-583c-439f-b04d-1fdd7c1417cc", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Windows" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_path_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_path_activity.json new file mode 100644 index 0000000000000..082fce438ca9e --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_path_activity.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "Identifies processes started from atypical folders in the file system, which might indicate malware execution or persistence mechanisms. In corporate Windows environments, software installation is centrally managed and it is unusual for programs to be executed from user or temporary directories. Processes executed from these locations can denote that a user downloaded software directly from the Internet or a malicious script or macro executed malware.", + "false_positives": [ + "A new and unusual program or artifact download in the course of software upgrades, debugging, or troubleshooting could trigger this signal. Users downloading and running programs from unusual locations, such as temporary directories, browser caches, or profile paths could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "windows_anomalous_path_activity_ecs", + "name": "Unusual Windows Path Activity", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "445a342e-03fb-42d0-8656-0367eb2dead5", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Windows" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_process_all_hosts.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_process_all_hosts.json new file mode 100644 index 0000000000000..93469b5a06223 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_process_all_hosts.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "Searches for rare processes running on multiple hosts in an entire fleet or network. This reduces the detection of false positives since automated maintenance processes usually only run occasionally on a single machine but are common to all or many hosts in a fleet.", + "false_positives": [ + "A newly installed program or one that runs rarely as part of a monthly or quarterly workflow could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "windows_anomalous_process_all_hosts_ecs", + "name": "Anomalous Process For a Windows Population", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "6e40d56f-5c0e-4ac6-aece-bee96645b172", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Windows" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_process_creation.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_process_creation.json new file mode 100644 index 0000000000000..1b80e443baae6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_process_creation.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "Identifies unusual parent-child process relationships that can indicate malware execution or persistence mechanisms. Malicious scripts often call on other applications and processes as part of their exploit payload. For example, when a malicious Office document runs scripts as part of an exploit payload, Excel or Word may start a script interpreter process, which, in turn, runs a script that downloads and executes malware. Another common scenario is Outlook running an unusual process when malware is downloaded in an email. Monitoring and identifying anomalous process relationships is a method of detecting new and emerging malware that is not yet recognized by anti-virus scanners.", + "false_positives": [ + "Users running scripts in the course of technical support operations of software upgrades could trigger this signal. A newly installed program or one that runs rarely as part of a monthly or quarterly workflow could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "windows_anomalous_process_creation", + "name": "Anomalous Windows Process Creation", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "0b29cab4-dbbd-4a3f-9e8e-1287c7c11ae5", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Windows" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_script.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_script.json new file mode 100644 index 0000000000000..4de5443bcaf3f --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_script.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected a PowerShell script with unusual data characteristics, such as obfuscation, that may be a characteristic of malicious PowerShell script text blocks.", + "false_positives": [ + "Certain kinds of security testing may trigger this signal. PowerShell scripts that use high levels of obfuscation or have unusual script block payloads may trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "windows_anomalous_script", + "name": "Suspicious Powershell Script", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "1781d055-5c66-4adf-9d60-fc0fa58337b6", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Windows" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_service.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_service.json new file mode 100644 index 0000000000000..7e0641fee68c2 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_service.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected an unusual Windows service, This can indicate execution of unauthorized services, malware, or persistence mechanisms. In corporate Windows environments, hosts do not generally run many rare or unique services. This job helps detect malware and persistence mechanisms that have been installed and run as a service.", + "false_positives": [ + "A newly installed program or one that runs rarely as part of a monthly or quarterly workflow could trigger this signal." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "windows_anomalous_service", + "name": "Unusual Windows Service", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "1781d055-5c66-4adf-9c71-fc0fa58338c7", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Windows" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_user_name.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_user_name.json new file mode 100644 index 0000000000000..217404b6eb474 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_anomalous_user_name.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected activity for a username that is not normally active, which can indicate unauthorized changes, activity by unauthorized users, lateral movement, or compromised credentials. In many organizations, new usernames are not often created apart from specific types of system activities, such as creating new accounts for new employees. These user accounts quickly become active and routine. Events from rarely used usernames can point to suspicious activity. Additionally, automated Linux fleets tend to see activity from rarely used usernames only when personnel log in to make authorized or unauthorized changes, or threat actors have acquired credentials and log in for malicious purposes. Unusual usernames can also indicate pivoting, where compromised credentials are used to try and move laterally from one host to another.", + "false_positives": [ + "Uncommon user activity can be due to an administrator or help desk technician logging onto a workstation or server in order to perform manual troubleshooting or reconfiguration." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "windows_anomalous_user_name_ecs", + "name": "Unusual Windows Username", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "1781d055-5c66-4adf-9c59-fc0fa58336a5", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Windows" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_network_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_network_connection.json new file mode 100644 index 0000000000000..2cda21cf7d5ef --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_network_connection.json @@ -0,0 +1,36 @@ +{ + "description": "Identifies certutil.exe making a network connection. Adversaries could abuse certutil.exe to download a certificate, or malware, from a remote URL.", + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "max_signals": 33, + "name": "Network Connection via Certutil", + "query": "process.name:certutil.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)", + "risk_score": 21, + "rule_id": "3838e0e3-1850-4850-a411-2e8c5ba40ba8", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1105", + "name": "Remote File Copy", + "reference": "https://attack.mitre.org/techniques/T1105/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file 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 index 7ab8034ef4083..2427ab4d7cc55 100644 --- 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 @@ -7,9 +7,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Command Prompt Network Connection", - "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", + "query": "process.name:cmd.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)", "risk_score": 21, "rule_id": "89f9a4b0-9f8f-4ee0-8823-c4751a6d6696", "severity": "low", @@ -51,4 +50,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json index d914fd2e91a07..f8e5bd22576a4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "PowerShell spawning Cmd", "query": "process.parent.name:powershell.exe and process.name:cmd.exe", "risk_score": 21, @@ -48,4 +47,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json index b7f0c54fedf62..71aafa9984ecb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Svchost spawning Cmd", "query": "process.parent.name:svchost.exe and process.name:cmd.exe", "risk_score": 21, @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_msbuild.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_msbuild.json new file mode 100644 index 0000000000000..4ff7891438554 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_msbuild.json @@ -0,0 +1,38 @@ +{ + "description": "An instance of MSBuild, the Microsoft Build Engine, loaded DLLs (dynamically linked libraries) responsible for Windows credential management. This technique is sometimes used for credential dumping.", + "false_positives": [ + "The Build Engine is commonly used by Windows developers but use by non-engineers is unusual." + ], + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "name": "Microsoft Build Engine Loading Windows Credential Libraries", + "query": "(winlog.event_data.OriginalFileName: (vaultcli.dll or SAMLib.DLL) or dll.name: (vaultcli.dll or SAMLib.DLL)) and process.name: MSBuild.exe and event.action: \"Image loaded (rule: ImageLoad)\"", + "risk_score": 73, + "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae5", + "severity": "high", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0006", + "name": "Credential Access", + "reference": "https://attack.mitre.org/tactics/TA0006/" + }, + "technique": [ + { + "id": "T1003", + "name": "Credential Dumping", + "reference": "https://attack.mitre.org/techniques/T1003/" + } + ] + } + ], + "type": "query", + "version": 1 +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_cve_2020_0601.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_cve_2020_0601.json new file mode 100644 index 0000000000000..c08bb7b3315f5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_cve_2020_0601.json @@ -0,0 +1,36 @@ +{ + "description": "A spoofing vulnerability exists in the way Windows CryptoAPI (Crypt32.dll) validates Elliptic Curve Cryptography (ECC) certificates. An attacker could exploit the vulnerability by using a spoofed code-signing certificate to sign a malicious executable, making it appear the file was from a trusted, legitimate source.", + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "max_signals": 33, + "name": "Windows CryptoAPI Spoofing Vulnerability (CVE-2020-0601 - CurveBall)", + "query": "event.provider:\"Microsoft-Windows-Audit-CVE\" and message:\"[CVE-2020-0601]\"", + "risk_score": 21, + "rule_id": "56557cde-d923-4b88-adee-c61b3f3b5dc3", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0005", + "name": "Defense Evasion", + "reference": "https://attack.mitre.org/tactics/TA0005/" + }, + "technique": [ + { + "id": "T1116", + "name": "Code Signing", + "reference": "https://attack.mitre.org/techniques/T1116/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file 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 index 86242fd1081a1..3f97f7aca74f6 100644 --- 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 @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Potential Evasion via Filter Manager", "query": "event.code:1 and process.name:fltMC.exe", "risk_score": 21, @@ -33,4 +32,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_by_office_app.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_by_office_app.json new file mode 100644 index 0000000000000..72e02f8718d03 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_by_office_app.json @@ -0,0 +1,56 @@ +{ + "description": "An instance of MSBuild, the Microsoft Build Engine, was started by Excel or Word. This is unusual behavior for the Build Engine and could have been caused by an Excel or Word document executing a malicious script payload.", + "false_positives": [ + "The Build Engine is commonly used by Windows developers but use by non-engineers is unusual. It is quite unusual for this program to be started by an Office application like Word or Excel." + ], + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "name": "Microsoft Build Engine Started by an Office Application", + "query": "process.name:MSBuild.exe and process.parent.name:(eqnedt32.exe or excel.exe or fltldr.exe or msaccess.exe or mspub.exe or outlook.exe or powerpnt.exe or winword.exe) and event.action: \"Process Create (rule: ProcessCreate)\"", + "references": [ + "https://blog.talosintelligence.com/2020/02/building-bypass-with-msbuild.html" + ], + "risk_score": 73, + "rule_id": "c5dc3223-13a2-44a2-946c-e9dc0aa0449c", + "severity": "high", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0005", + "name": "Defense Evasion", + "reference": "https://attack.mitre.org/tactics/TA0005/" + }, + "technique": [ + { + "id": "T1127", + "name": "Trusted Developer Utilities", + "reference": "https://attack.mitre.org/techniques/T1127/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0002", + "name": "Execution", + "reference": "https://attack.mitre.org/tactics/TA0002/" + }, + "technique": [ + { + "id": "T1127", + "name": "Trusted Developer Utilities", + "reference": "https://attack.mitre.org/techniques/T1127/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_by_script.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_by_script.json new file mode 100644 index 0000000000000..ad519f1516aa6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_by_script.json @@ -0,0 +1,53 @@ +{ + "description": "An instance of MSBuild, the Microsoft Build Engine, was started by a script or the Windows command interpreter. This behavior is unusual and is sometimes used by malicious payloads.", + "false_positives": [ + "The Build Engine is commonly used by Windows developers but use by non-engineers is unusual." + ], + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "name": "Microsoft Build Engine Started by a Script Process", + "query": "process.name:MSBuild.exe and process.parent.name:(cmd.exe or powershell.exe or cscript.exe or wscript.exe) and event.action:\"Process Create (rule: ProcessCreate)\"", + "risk_score": 21, + "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae2", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0005", + "name": "Defense Evasion", + "reference": "https://attack.mitre.org/tactics/TA0005/" + }, + "technique": [ + { + "id": "T1127", + "name": "Trusted Developer Utilities", + "reference": "https://attack.mitre.org/techniques/T1127/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0002", + "name": "Execution", + "reference": "https://attack.mitre.org/tactics/TA0002/" + }, + "technique": [ + { + "id": "T1127", + "name": "Trusted Developer Utilities", + "reference": "https://attack.mitre.org/techniques/T1127/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_by_system_process.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_by_system_process.json new file mode 100644 index 0000000000000..1bbce904f2518 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_by_system_process.json @@ -0,0 +1,53 @@ +{ + "description": "An instance of MSBuild, the Microsoft Build Engine, was started by Explorer or the WMI (Windows Management Instrumentation) subsystem. This behavior is unusual and is sometimes used by malicious payloads.", + "false_positives": [ + "The Build Engine is commonly used by Windows developers but use by non-engineers is unusual." + ], + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "name": "Microsoft Build Engine Started by a System Process", + "query": "process.name:MSBuild.exe and process.parent.name:(explorer.exe or wmiprvse.exe) and event.action:\"Process Create (rule: ProcessCreate)\"", + "risk_score": 47, + "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae3", + "severity": "medium", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0005", + "name": "Defense Evasion", + "reference": "https://attack.mitre.org/tactics/TA0005/" + }, + "technique": [ + { + "id": "T1127", + "name": "Trusted Developer Utilities", + "reference": "https://attack.mitre.org/techniques/T1127/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0002", + "name": "Execution", + "reference": "https://attack.mitre.org/tactics/TA0002/" + }, + "technique": [ + { + "id": "T1127", + "name": "Trusted Developer Utilities", + "reference": "https://attack.mitre.org/techniques/T1127/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_renamed.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_renamed.json new file mode 100644 index 0000000000000..eea4b3b4efe10 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_renamed.json @@ -0,0 +1,38 @@ +{ + "description": "An instance of MSBuild, the Microsoft Build Engine, was started after being renamed. This is uncommon behavior and may indicate an attempt to run unnoticed or undetected.", + "false_positives": [ + "The Build Engine is commonly used by Windows developers but use by non-engineers is unusual." + ], + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "name": "Microsoft Build Engine Using an Alternate Name", + "query": "(pe.original_file_name:MSBuild.exe or winlog.event_data.OriginalFileName: MSBuild.exe) and not process.name: MSBuild.exe and event.action: \"Process Create (rule: ProcessCreate)\"", + "risk_score": 21, + "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae4", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0005", + "name": "Defense Evasion", + "reference": "https://attack.mitre.org/tactics/TA0005/" + }, + "technique": [ + { + "id": "T1036", + "name": "Masquerading", + "reference": "https://attack.mitre.org/techniques/T1036/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_unusal_process.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_unusal_process.json new file mode 100644 index 0000000000000..81ea14e265388 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_msbuild_started_unusal_process.json @@ -0,0 +1,41 @@ +{ + "description": "An instance of MSBuild, the Microsoft Build Engine, started a PowerShell script or the Visual C# Command Line Compiler. This technique is sometimes used to deploy a malicious payload using the Build Engine.", + "false_positives": [ + "The Build Engine is commonly used by Windows developers but use by non-engineers is unusual. If a build system triggers this rule it can be exempted by process, user or host name." + ], + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "name": "Microsoft Build Engine Started an Unusual Process", + "query": "process.parent.name:MSBuild.exe and process.name:(csc.exe or iexplore.exe or powershell.exe)", + "references": [ + "https://blog.talosintelligence.com/2020/02/building-bypass-with-msbuild.html" + ], + "risk_score": 21, + "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae6", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0005", + "name": "Defense Evasion", + "reference": "https://attack.mitre.org/tactics/TA0005/" + }, + "technique": [ + { + "id": "T1500", + "name": "Compile After Delivery", + "reference": "https://attack.mitre.org/techniques/T1500/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file 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 index 7789b0723b3fc..2b6e1fb3daaec 100644 --- 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 @@ -7,7 +7,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Process Activity via Compiled HTML File", "query": "event.code:1 and process.name:hh.exe", "risk_score": 21, @@ -51,4 +50,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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..c397c955fe64f --- /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,51 @@ +{ + "description": "RegSvcs.exe and RegAsm.exe are Windows command line utilities that are used to register .NET Component Object Model (COM) assemblies. Adversaries can use RegSvcs.exe and RegAsm.exe to proxy execution of code through a trusted Windows utility.", + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "max_signals": 33, + "name": "Execution via Regsvcs/Regasm", + "query": "process.name:(RegAsm.exe or RegSvcs.exe) and event.action:\"Process Create (rule: ProcessCreate)\"", + "risk_score": 21, + "rule_id": "47f09343-8d1f-4bb5-8bb0-00c9d18f5010", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0002", + "name": "Execution", + "reference": "https://attack.mitre.org/tactics/TA0002/" + }, + "technique": [ + { + "id": "T1121", + "name": "Regsvcs/Regasm", + "reference": "https://attack.mitre.org/techniques/T1121/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0005", + "name": "Defense Evasion", + "reference": "https://attack.mitre.org/tactics/TA0005/" + }, + "technique": [ + { + "id": "T1121", + "name": "Regsvcs/Regasm", + "reference": "https://attack.mitre.org/techniques/T1121/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file 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 index bd2376a0897f4..f60a986996d6f 100644 --- 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 @@ -7,9 +7,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Trusted Developer Application Usage", - "query": "event.code:1 and (process.name:MSBuild.exe or process.name:msxsl.exe)", + "query": "event.code:1 and process.name:(MSBuild.exe or msxsl.exe)", "risk_score": 21, "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae1", "severity": "low", @@ -51,4 +50,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index 32fa953388be3..4b3efead776d2 100644 --- 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 @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Network Connection via Compiled HTML File", - "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", + "query": "process.name:hh.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)", "risk_score": 21, "rule_id": "b29ee2be-bf99-446c-ab1a-2dc0183394b8", "severity": "low", @@ -48,4 +47,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_injection_msbuild.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_injection_msbuild.json new file mode 100644 index 0000000000000..c6310c12ed974 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_injection_msbuild.json @@ -0,0 +1,53 @@ +{ + "description": "An instance of MSBuild, the Microsoft Build Engine, created a thread in another process. This technique is sometimes used to evade detection or elevate privileges.", + "false_positives": [ + "The Build Engine is commonly used by Windows developers but use by non-engineers is unusual." + ], + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "name": "Process Injection by the Microsoft Build Engine", + "query": "process.name:MSBuild.exe and event.action:\"CreateRemoteThread detected (rule: CreateRemoteThread)\"", + "risk_score": 21, + "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae9", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0005", + "name": "Defense Evasion", + "reference": "https://attack.mitre.org/tactics/TA0005/" + }, + "technique": [ + { + "id": "T1055", + "name": "Process Injection", + "reference": "https://attack.mitre.org/techniques/T1055/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0004", + "name": "Privilege Escalation", + "reference": "https://attack.mitre.org/tactics/TA0004/" + }, + "technique": [ + { + "id": "T1055", + "name": "Process Injection", + "reference": "https://attack.mitre.org/techniques/T1055/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file 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 index 0015371f03067..0cd68ba5c1ed8 100644 --- 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 @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Network Connection via Signed Binary", - "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", + "query": "process.name:(expand.exe or extrac.exe or ieexec.exe or makecab.exe) and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)", "risk_score": 21, "rule_id": "63e65ec3-43b1-45b0-8f2d-45b34291dc44", "severity": "low", @@ -48,4 +47,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_modification_of_boot_config.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_modification_of_boot_config.json new file mode 100644 index 0000000000000..d761226276496 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_modification_of_boot_config.json @@ -0,0 +1,36 @@ +{ + "description": "Identifies use of bcdedit.exe to delete boot configuration data. This tactic is sometimes used as by malware or an attacker as a destructive technique.", + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "max_signals": 33, + "name": "Modification of Boot Configuration", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:bcdedit.exe and process.args:(/set and (bootstatuspolicy and ignoreallfailures or no and recoveryenabled))", + "risk_score": 21, + "rule_id": "69c251fb-a5d6-4035-b5ec-40438bd829ff", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0005", + "name": "Defense Evasion", + "reference": "https://attack.mitre.org/tactics/TA0005/" + }, + "technique": [ + { + "id": "T1107", + "name": "File Deletion", + "reference": "https://attack.mitre.org/techniques/T1107/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_msxsl_network.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_msxsl_network.json new file mode 100644 index 0000000000000..9b45d03aae375 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_msxsl_network.json @@ -0,0 +1,36 @@ +{ + "description": "Identifies msxsl.exe making a network connection. This may indicate adversarial activity as msxsl.exe is often leveraged by adversaries to execute malicious scripts and evade detection.", + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "max_signals": 33, + "name": "Network Connection via MsXsl", + "query": "process.name:msxsl.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)", + "risk_score": 21, + "rule_id": "b86afe07-0d98-4738-b15d-8d7465f95ff5", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0002", + "name": "Execution", + "reference": "https://attack.mitre.org/tactics/TA0002/" + }, + "technique": [ + { + "id": "T1220", + "name": "XSL Script Processing", + "reference": "https://attack.mitre.org/techniques/T1220/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_system_account.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_system_account.json new file mode 100644 index 0000000000000..390c9c278905c --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_system_account.json @@ -0,0 +1,36 @@ +{ + "description": "Identifies the SYSTEM account using the Net utility. The Net utility is a component of the Windows operating system. It is used in command line operations for control of users, groups, services, and network connections.", + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "max_signals": 33, + "name": "Net command via SYSTEM account", + "query": "(process.name:net.exe or process.name:net1.exe and not process.parent.name:net.exe) and user.name:SYSTEM and event.action:\"Process Create (rule: ProcessCreate)\"", + "risk_score": 21, + "rule_id": "2856446a-34e6-435b-9fb5-f8f040bfa7ed", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0007", + "name": "Discovery", + "reference": "https://attack.mitre.org/tactics/TA0007/" + }, + "technique": [ + { + "id": "T1087", + "name": "Account Discovery", + "reference": "https://attack.mitre.org/techniques/T1087/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file 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 index 6eaac7b9e6cab..0488667d06c82 100644 --- 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 @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Potential Application Shimming via Sdbinst", "query": "event.code:1 and process.name:sdbinst.exe", "risk_score": 21, @@ -48,4 +47,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index b2463633b0c5d..26f0a0bcc245c 100644 --- 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 @@ -4,9 +4,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Potential Modification of Accessibility Binaries", - "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)", + "query": "event.code:1 and process.parent.name:winlogon.exe and process.name:(atbroker.exe or displayswitch.exe or magnify.exe or narrator.exe or osk.exe or sethc.exe or utilman.exe)", "risk_score": 21, "rule_id": "7405ddf1-6c8e-41ce-818f-48bea6bcaed8", "severity": "low", @@ -48,4 +47,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file 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 index a0542ef59d8cd..28ebdb44fddd2 100644 --- 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 @@ -7,7 +7,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Process Discovery via Tasklist", "query": "event.code:1 and process.name:tasklist.exe", "risk_score": 21, @@ -36,4 +35,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_rare_user_runas_event.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_rare_user_runas_event.json new file mode 100644 index 0000000000000..3dca119b5a28e --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_rare_user_runas_event.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected an unusual user context switch, using the runas command or similar techniques, which can indicate account takeover or privilege escalation using compromised accounts. Privilege elevation using tools like runas are more commonly used by domain and network administrators than by regular Windows users.", + "false_positives": [ + "Uncommon user privilege elevation activity can be due to an administrator, help desk technician, or a user performing manual troubleshooting or reconfiguration." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "windows_rare_user_runas_event", + "name": "Unusual Windows User Privilege Elevation Activity", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "1781d055-5c66-4adf-9d82-fc0fa58449c8", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Windows" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_rare_user_type10_remote_login.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_rare_user_type10_remote_login.json new file mode 100644 index 0000000000000..09ff2a0cedf41 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_rare_user_type10_remote_login.json @@ -0,0 +1,24 @@ +{ + "anomaly_threshold": 50, + "description": "A machine learning job detected an unusual remote desktop protocol (RDP) username, which can indicate account takeover or credentialed persistence using compromised accounts. RDP attacks, such as BlueKeep, also tend to use unusual usernames.", + "false_positives": [ + "Uncommon username activity can be due to an engineer logging onto a server instance in order to perform manual troubleshooting or reconfiguration." + ], + "from": "now-16m", + "interval": "15m", + "machine_learning_job_id": "windows_rare_user_type10_remote_login", + "name": "Unusual Windows Remote User", + "references": [ + "https://www.elastic.co/guide/en/siem/guide/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "1781d055-5c66-4adf-9e93-fc0fa69550c9", + "severity": "low", + "tags": [ + "Elastic", + "ML", + "Windows" + ], + "type": "machine_learning", + "version": 1 +} \ No newline at end of file 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 index d0f2e809c1aa7..920ff28a9a9cd 100644 --- 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 @@ -1,5 +1,5 @@ { - "description": "Identifies the native Windows tools regsvr32.exe and regsvr64.exe making a network connection. This may be indicative of an attacker bypassing whitelisting or running arbitrary scripts via a signed Microsoft binary.", + "description": "Identifies the native Windows tools regsvr32.exe and regsvr64.exe making a network connection. This may be indicative of an attacker bypassing whitelisting or running arbitrary scripts via a signed Microsoft binary.", "false_positives": [ "Security testing may produce events like this. Activity of this kind performed by non-engineers and ordinary users is unusual." ], @@ -7,9 +7,8 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Network Connection via Regsvr", - "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", + "query": "process.name:(regsvr32.exe or regsvr64.exe) and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:(10.0.0.0/8 or 169.254.169.254 or 172.16.0.0/12 or 192.168.0.0/16)", "risk_score": 21, "rule_id": "fb02b8d3-71ee-4af1-bacd-215d23f17efa", "severity": "low", @@ -51,4 +50,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_suspicious_pdf_reader.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_suspicious_pdf_reader.json new file mode 100644 index 0000000000000..9d4c2438acfb9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_suspicious_pdf_reader.json @@ -0,0 +1,35 @@ +{ + "description": "Identifies suspicious child processes of PDF reader applications. These child processes are often launched via exploitation of PDF applications or social engineering.", + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "name": "Suspicious PDF Reader Child Process", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:(AcroRd32.exe or Acrobat.exe or FoxitPhantomPDF.exe or FoxitReader.exe) and process.name:(arp.exe or dsquery.exe or dsget.exe or gpresult.exe or hostname.exe or ipconfig.exe or nbtstat.exe or net.exe or net1.exe or netsh.exe or netstat.exe or nltest.exe or ping.exe or qprocess.exe or quser.exe or qwinsta.exe or reg.exe or sc.exe or systeminfo.exe or tasklist.exe or tracert.exe or whoami.exe or bginfo.exe or cdb.exe or cmstp.exe or csi.exe or dnx.exe or fsi.exe or ieexec.exe or iexpress.exe or installutil.exe or Microsoft.Workflow.Compiler.exe or msbuild.exe or mshta.exe or msxsl.exe or odbcconf.exe or rcsi.exe or regsvr32.exe or xwizard.exe or atbroker.exe or forfiles.exe or schtasks.exe or regasm.exe or regsvcs.exe or cmd.exe or cscript.exe or powershell.exe or pwsh.exe or wmic.exe or wscript.exe or bitsadmin.exe or certutil.exe or ftp.exe)", + "risk_score": 21, + "rule_id": "53a26770-9cbd-40c5-8b57-61d01a325e14", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0002", + "name": "Execution", + "reference": "https://attack.mitre.org/tactics/TA0002/" + }, + "technique": [ + { + "id": "T1204", + "name": "User Execution", + "reference": "https://attack.mitre.org/techniques/T1204/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_uac_bypass_event_viewer.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_uac_bypass_event_viewer.json new file mode 100644 index 0000000000000..0d4168640bc60 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_uac_bypass_event_viewer.json @@ -0,0 +1,36 @@ +{ + "description": "Identifies User Account Control (UAC) bypass via eventvwr.exe. Attackers bypass UAC to stealthily execute code with elevated permissions.", + "index": [ + "winlogbeat-*" + ], + "language": "kuery", + "max_signals": 33, + "name": "Bypass UAC via Event Viewer", + "query": "process.parent.name:eventvwr.exe and event.action:\"Process Create (rule: ProcessCreate)\" and not process.executable:(\"C:\\Windows\\SysWOW64\\mmc.exe\" or \"C:\\Windows\\System32\\mmc.exe\")", + "risk_score": 21, + "rule_id": "31b4c719-f2b4-41f6-a9bd-fce93c2eaf62", + "severity": "low", + "tags": [ + "Elastic", + "Windows" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0004", + "name": "Privilege Escalation", + "reference": "https://attack.mitre.org/tactics/TA0004/" + }, + "technique": [ + { + "id": "T1088", + "name": "Bypass User Account Control", + "reference": "https://attack.mitre.org/techniques/T1088/" + } + ] + } + ], + "type": "query", + "version": 1 +} \ No newline at end of file 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 index 678160f945ba0..46af0c5b586a5 100644 --- 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 @@ -7,7 +7,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 100, "name": "Whoami Process Activity", "query": "process.name:whoami.exe and event.code:1", "risk_score": 21, @@ -36,4 +35,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts index ada11174c5340..38b1097a845f8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts @@ -16,7 +16,6 @@ import { import { AlertsClient, PartialAlert } from '../../../../../../../plugins/alerting/server'; import { Alert } from '../../../../../../../plugins/alerting/common'; import { SIGNALS_ID } from '../../../../common/constants'; -import { LegacyRequest } from '../../../types'; import { ActionsClient } from '../../../../../../../plugins/actions/server'; import { RuleAlertParams, RuleTypeParams, RuleAlertParamsRest } from '../types'; @@ -39,14 +38,6 @@ export interface FindParamsRest { filter: string; } -export interface PatchRulesRequest extends LegacyRequest { - payload: PatchRuleAlertParamsRest; -} - -export interface UpdateRulesRequest extends LegacyRequest { - payload: UpdateRuleAlertParamsRest; -} - export interface RuleAlertType extends Alert { params: RuleTypeParams; } @@ -93,7 +84,7 @@ export interface IRuleStatusFindType { saved_objects: IRuleStatusSavedObject[]; } -export type RuleStatusString = 'succeeded' | 'failed' | 'going to run' | 'executing'; +export type RuleStatusString = 'succeeded' | 'failed' | 'going to run'; export interface HapiReadableStream extends Readable { hapi: { @@ -151,12 +142,12 @@ export interface Clients { actionsClient: ActionsClient; } -export type PatchRuleParams = Partial & { +export type PatchRuleParams = Partial> & { id: string | undefined | null; savedObjectsClient: SavedObjectsClientContract; } & Clients; -export type UpdateRuleParams = RuleAlertParams & { +export type UpdateRuleParams = Omit & { id: string | undefined | null; savedObjectsClient: SavedObjectsClientContract; } & Clients; @@ -166,7 +157,9 @@ export type DeleteRuleParams = Clients & { ruleId: string | undefined | null; }; -export type CreateRuleParams = Omit & { ruleId: string } & Clients; +export type CreateRuleParams = Omit & { + ruleId: string; +} & Clients; export interface ReadRuleParams { alertsClient: AlertsClient; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.test.ts new file mode 100644 index 0000000000000..7a3f233475117 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.test.ts @@ -0,0 +1,62 @@ +/* + * 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 { savedObjectsClientMock } from '../../../../../../../../src/core/server/mocks'; +import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks'; +import { actionsClientMock } from '../../../../../../../plugins/actions/server/mocks'; +import { mockPrepackagedRule } from '../routes/__mocks__/request_responses'; +import { updatePrepackagedRules } from './update_prepacked_rules'; +import { patchRules } from './patch_rules'; +jest.mock('./patch_rules'); + +describe('updatePrepackagedRules', () => { + let actionsClient: ReturnType; + let alertsClient: ReturnType; + let savedObjectsClient: ReturnType; + + beforeEach(() => { + actionsClient = actionsClientMock.create(); + alertsClient = alertsClientMock.create(); + savedObjectsClient = savedObjectsClientMock.create(); + }); + + it('should omit actions and enabled when calling patchRules', async () => { + const actions = [ + { + group: 'group', + id: 'id', + action_type_id: 'action_type_id', + params: {}, + }, + ]; + const outputIndex = 'outputIndex'; + const prepackagedRule = mockPrepackagedRule(); + + await updatePrepackagedRules( + alertsClient, + actionsClient, + savedObjectsClient, + [{ ...prepackagedRule, actions }], + outputIndex + ); + + expect(patchRules).toHaveBeenCalledWith( + expect.objectContaining({ + ruleId: 'rule-1', + }) + ); + expect(patchRules).not.toHaveBeenCalledWith( + expect.objectContaining({ + enabled: true, + }) + ); + expect(patchRules).not.toHaveBeenCalledWith( + expect.objectContaining({ + actions, + }) + ); + }); +}); 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 index cc67622176a04..7eb0d8d1399be 100644 --- 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 @@ -19,7 +19,6 @@ export const updatePrepackagedRules = async ( ): Promise => { await rules.forEach(async rule => { const { - actions, description, false_positives: falsePositives, from, @@ -40,7 +39,6 @@ export const updatePrepackagedRules = async ( to, type, threat, - throttle, references, version, note, @@ -51,7 +49,6 @@ export const updatePrepackagedRules = async ( return patchRules({ alertsClient, actionsClient, - actions, description, falsePositives, from, @@ -75,7 +72,6 @@ export const updatePrepackagedRules = async ( to, type, threat, - throttle, references, version, note, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rule_actions.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rule_actions.ts new file mode 100644 index 0000000000000..ac10143c1d8d0 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rule_actions.ts @@ -0,0 +1,50 @@ +/* + * 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 { AlertsClient, AlertServices } from '../../../../../../../plugins/alerting/server'; +import { getRuleActionsSavedObject } from '../rule_actions/get_rule_actions_saved_object'; +import { readRules } from './read_rules'; +import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; + +interface UpdateRuleActions { + alertsClient: AlertsClient; + savedObjectsClient: AlertServices['savedObjectsClient']; + ruleAlertId: string; +} + +export const updateRuleActions = async ({ + alertsClient, + savedObjectsClient, + ruleAlertId, +}: UpdateRuleActions) => { + const rule = await readRules({ alertsClient, id: ruleAlertId }); + if (rule == null) { + return null; + } + + const ruleActions = await getRuleActionsSavedObject({ + savedObjectsClient, + ruleAlertId, + }); + + if (!ruleActions) { + return null; + } + + return alertsClient.update({ + id: ruleAlertId, + data: { + actions: !ruleActions.alertThrottle + ? ruleActions.actions.map(transformRuleToAlertAction) + : [], + throttle: null, + name: rule.name, + tags: rule.tags, + schedule: rule.schedule, + params: rule.params, + }, + }); +}; 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 af00816abfc3d..ca299db6ace50 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 @@ -28,12 +28,10 @@ describe('updateRules', () => { await updateRules({ alertsClient, actionsClient, - actions: [], savedObjectsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ...rule.params, enabled: false, - throttle: null, interval: '', name: '', tags: [], @@ -56,12 +54,10 @@ describe('updateRules', () => { await updateRules({ alertsClient, actionsClient, - actions: [], savedObjectsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ...rule.params, enabled: true, - throttle: null, interval: '', name: '', tags: [], @@ -86,12 +82,10 @@ describe('updateRules', () => { await updateRules({ alertsClient, actionsClient, - actions: [], savedObjectsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ...params, enabled: true, - throttle: null, interval: '', name: '', tags: [], 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 72cbc959c0105..0e70e05f4de78 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 @@ -5,7 +5,6 @@ */ import { PartialAlert } from '../../../../../../../plugins/alerting/server'; -import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; import { readRules } from './read_rules'; import { IRuleSavedAttributesSavedObjectAttributes, UpdateRuleParams } from './types'; import { addTags } from './add_tags'; @@ -16,7 +15,6 @@ import { hasListsFeature } from '../feature_flags'; export const updateRules = async ({ alertsClient, actionsClient, // TODO: Use this whenever we add feature support for different action types - actions, savedObjectsClient, description, falsePositives, @@ -30,7 +28,6 @@ export const updateRules = async ({ meta, filters, from, - immutable, id, ruleId, index, @@ -41,7 +38,6 @@ export const updateRules = async ({ severity, tags, threat, - throttle, to, type, references, @@ -57,7 +53,6 @@ export const updateRules = async ({ } const calculatedVersion = calculateVersion(rule.params.immutable, rule.params.version, { - actions, description, falsePositives, query, @@ -77,7 +72,6 @@ export const updateRules = async ({ severity, tags, threat, - throttle, to, type, references, @@ -93,17 +87,17 @@ export const updateRules = async ({ const update = await alertsClient.update({ id: rule.id, data: { - tags: addTags(tags, rule.params.ruleId, immutable), + tags: addTags(tags, rule.params.ruleId, rule.params.immutable), name, schedule: { interval }, - actions: actions?.map(transformRuleToAlertAction) ?? rule.actions, - throttle: throttle !== undefined ? throttle : rule.throttle, + actions: rule.actions, + throttle: rule.throttle, params: { description, ruleId: rule.params.ruleId, falsePositives, from, - immutable, + immutable: rule.params.immutable, query, language, outputIndex, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules_notifications.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules_notifications.ts new file mode 100644 index 0000000000000..f70c591243a76 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules_notifications.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RuleAlertAction } from '../../../../common/detection_engine/types'; +import { AlertsClient, AlertServices } from '../../../../../../../plugins/alerting/server'; +import { updateOrCreateRuleActionsSavedObject } from '../rule_actions/update_or_create_rule_actions_saved_object'; +import { updateNotifications } from '../notifications/update_notifications'; +import { updateRuleActions } from './update_rule_actions'; + +interface UpdateRulesNotifications { + alertsClient: AlertsClient; + savedObjectsClient: AlertServices['savedObjectsClient']; + ruleAlertId: string; + actions: RuleAlertAction[] | undefined; + throttle: string | undefined; + enabled: boolean; + name: string; +} + +export const updateRulesNotifications = async ({ + alertsClient, + savedObjectsClient, + ruleAlertId, + actions, + enabled, + name, + throttle, +}: UpdateRulesNotifications) => { + const ruleActions = await updateOrCreateRuleActionsSavedObject({ + savedObjectsClient, + ruleAlertId, + actions, + throttle, + }); + + await updateRuleActions({ + alertsClient, + savedObjectsClient, + ruleAlertId, + }); + + await updateNotifications({ + alertsClient, + ruleAlertId, + enabled, + name, + actions: ruleActions.actions, + interval: ruleActions?.alertThrottle, + }); + + return ruleActions; +}; 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 31b922e0067cd..6d7d7e93d7e6e 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 @@ -5,9 +5,15 @@ */ import { SignalSourceHit, SignalSearchResponse } from '../types'; -import { Logger } from 'kibana/server'; +import { + Logger, + SavedObject, + SavedObjectsFindResponse, +} from '../../../../../../../../../src/core/server'; import { loggingServiceMock } from '../../../../../../../../../src/core/server/mocks'; import { RuleTypeParams, OutputRuleAlertRest } from '../../types'; +import { IRuleStatusAttributes } from '../../rules/types'; +import { ruleStatusSavedObjectType } from '../../../../saved_objects'; export const sampleRuleAlertParams = ( maxSignals?: number | undefined, @@ -373,4 +379,34 @@ export const sampleRule = (): Partial => { }; }; +export const exampleRuleStatus: () => SavedObject = () => ({ + type: ruleStatusSavedObjectType, + id: '042e6d90-7069-11ea-af8b-0f8ae4fa817e', + attributes: { + alertId: 'f4b8e31d-cf93-4bde-a265-298bde885cd7', + statusDate: '2020-03-27T22:55:59.517Z', + status: 'succeeded', + lastFailureAt: null, + lastSuccessAt: '2020-03-27T22:55:59.517Z', + lastFailureMessage: null, + lastSuccessMessage: 'succeeded', + gap: null, + bulkCreateTimeDurations: [], + searchAfterTimeDurations: [], + lastLookBackDate: null, + }, + references: [], + updated_at: '2020-03-27T22:55:59.577Z', + version: 'WzgyMiwxXQ==', +}); + +export const exampleFindRuleStatusResponse: ( + mockStatuses: Array> +) => SavedObjectsFindResponse = (mockStatuses = [exampleRuleStatus()]) => ({ + total: 1, + per_page: 6, + page: 1, + saved_objects: mockStatuses, +}); + export const mockLogger: Logger = loggingServiceMock.createLogger(); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/rule_status_saved_objects_client.mock.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/rule_status_saved_objects_client.mock.ts new file mode 100644 index 0000000000000..7528dc8b656ec --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/rule_status_saved_objects_client.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RuleStatusSavedObjectsClient } from '../rule_status_saved_objects_client'; + +const createMockRuleStatusSavedObjectsClient = (): jest.Mocked => ({ + find: jest.fn(), + create: jest.fn(), + update: jest.fn(), + delete: jest.fn(), +}); + +export const ruleStatusSavedObjectsClientMock = { + create: createMockRuleStatusSavedObjectsClient, +}; 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 c86696d6ec5eb..f2c2b99bdac8c 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 @@ -33,7 +33,7 @@ describe('buildBulkBody', () => { interval: '5m', enabled: true, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); // Timestamp will potentially always be different so remove it for the test delete fakeSignalSourceHit['@timestamp']; @@ -80,6 +80,7 @@ describe('buildBulkBody', () => { references: ['http://google.com'], severity: 'high', tags: ['some fake tag 1', 'some fake tag 2'], + throttle: 'no_actions', type: 'query', to: 'now', note: '', @@ -143,7 +144,7 @@ describe('buildBulkBody', () => { interval: '5m', enabled: true, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); // Timestamp will potentially always be different so remove it for the test delete fakeSignalSourceHit['@timestamp']; @@ -208,6 +209,7 @@ describe('buildBulkBody', () => { version: 1, created_at: fakeSignalSourceHit.signal.rule?.created_at, updated_at: fakeSignalSourceHit.signal.rule?.updated_at, + throttle: 'no_actions', lists: [ { field: 'source.ip', @@ -261,7 +263,7 @@ describe('buildBulkBody', () => { interval: '5m', enabled: true, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); // Timestamp will potentially always be different so remove it for the test delete fakeSignalSourceHit['@timestamp']; @@ -325,6 +327,7 @@ describe('buildBulkBody', () => { version: 1, created_at: fakeSignalSourceHit.signal.rule?.created_at, updated_at: fakeSignalSourceHit.signal.rule?.updated_at, + throttle: 'no_actions', lists: [ { field: 'source.ip', @@ -376,7 +379,7 @@ describe('buildBulkBody', () => { interval: '5m', enabled: true, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); // Timestamp will potentially always be different so remove it for the test delete fakeSignalSourceHit['@timestamp']; @@ -435,6 +438,7 @@ describe('buildBulkBody', () => { version: 1, updated_at: fakeSignalSourceHit.signal.rule?.updated_at, created_at: fakeSignalSourceHit.signal.rule?.created_at, + throttle: 'no_actions', lists: [ { field: 'source.ip', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.ts index f485769dffabc..75c4d75cedf1d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.ts @@ -24,7 +24,7 @@ interface BuildBulkBodyParams { interval: string; enabled: boolean; tags: string[]; - throttle: string | null; + throttle: string; } // format search_after result for signals index. 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 37d7ed8a51082..e360ceaf02f4d 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 @@ -38,7 +38,7 @@ describe('buildRule', () => { updatedBy: 'elastic', interval: 'some interval', tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); const expected: Partial = { actions: [], @@ -67,6 +67,7 @@ describe('buildRule', () => { updated_by: 'elastic', updated_at: rule.updated_at, created_at: rule.created_at, + throttle: 'no_actions', filters: [ { query: 'host.name: Rebecca', @@ -124,7 +125,7 @@ describe('buildRule', () => { updatedBy: 'elastic', interval: 'some interval', tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); const expected: Partial = { actions: [], @@ -154,6 +155,7 @@ describe('buildRule', () => { version: 1, updated_at: rule.updated_at, created_at: rule.created_at, + throttle: 'no_actions', lists: [ { field: 'source.ip', @@ -199,7 +201,7 @@ describe('buildRule', () => { updatedBy: 'elastic', interval: 'some interval', tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); const expected: Partial = { actions: [], @@ -229,80 +231,7 @@ describe('buildRule', () => { version: 1, updated_at: rule.updated_at, created_at: rule.created_at, - lists: [ - { - field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], - }, - { - field: 'host.name', - boolean_operator: 'and not', - values: [ - { - name: 'rock01', - type: 'value', - }, - { - name: 'mothra', - type: 'value', - }, - ], - }, - ], - }; - expect(rule).toEqual(expected); - }); - - test('it omits a null value such as if "throttle" is undefined if is present', () => { - const ruleParams = sampleRuleAlertParams(); - const rule = buildRule({ - actions: [], - ruleParams, - name: 'some-name', - id: sampleRuleGuid, - enabled: true, - createdAt: '2020-01-28T15:58:34.810Z', - updatedAt: '2020-01-28T15:59:14.004Z', - createdBy: 'elastic', - updatedBy: 'elastic', - interval: 'some interval', - tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, - }); - const expected: Partial = { - actions: [], - created_by: 'elastic', - description: 'Detecting root and admin users', - enabled: true, - false_positives: [], - from: 'now-6m', - id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', - immutable: false, - index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - interval: 'some interval', - language: 'kuery', - max_signals: 10000, - name: 'some-name', - output_index: '.siem-signals', - query: 'user.name: root or user.name: admin', - references: ['http://google.com'], - risk_score: 50, - rule_id: 'rule-1', - severity: 'high', - tags: ['some fake tag 1', 'some fake tag 2'], - to: 'now', - type: 'query', - note: '', - updated_by: 'elastic', - version: 1, - updated_at: rule.updated_at, - created_at: rule.created_at, + throttle: 'no_actions', lists: [ { field: 'source.ip', 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 1de80ca0b7eaf..9c375d7d45d5e 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 @@ -20,7 +20,7 @@ interface BuildRuleParams { updatedBy: string; interval: string; tags: string[]; - throttle: string | null; + throttle: string; } export const buildRule = ({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts index c1b61ef24462d..355041d9efbdb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts @@ -30,7 +30,7 @@ interface BulkCreateMlSignalsParams { interval: string; enabled: boolean; tags: string[]; - throttle: string | null; + throttle: string; } interface EcsAnomaly extends Anomaly { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_current_status_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_current_status_saved_object.ts deleted file mode 100644 index 1fee8bcd6c2f0..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_current_status_saved_object.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { SavedObjectsFindResponse, SavedObject } from 'src/core/server'; - -import { AlertServices } from '../../../../../../../plugins/alerting/server'; -import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types'; -import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; - -interface CurrentStatusSavedObjectParams { - alertId: string; - services: AlertServices; - ruleStatusSavedObjects: SavedObjectsFindResponse; -} - -export const getCurrentStatusSavedObject = async ({ - alertId, - services, - ruleStatusSavedObjects, -}: CurrentStatusSavedObjectParams): Promise> => { - if (ruleStatusSavedObjects.saved_objects.length === 0) { - // create - const date = new Date().toISOString(); - const currentStatusSavedObject = await services.savedObjectsClient.create< - IRuleSavedAttributesSavedObjectAttributes - >(ruleStatusSavedObjectType, { - alertId, // do a search for this id. - statusDate: date, - status: 'going to run', - lastFailureAt: null, - lastSuccessAt: null, - lastFailureMessage: null, - lastSuccessMessage: null, - gap: null, - bulkCreateTimeDurations: [], - searchAfterTimeDurations: [], - lastLookBackDate: null, - }); - return currentStatusSavedObject; - } else { - // update 0th to executing. - const currentStatusSavedObject = ruleStatusSavedObjects.saved_objects[0]; - const sDate = new Date().toISOString(); - currentStatusSavedObject.attributes.status = 'going to run'; - currentStatusSavedObject.attributes.statusDate = sDate; - await services.savedObjectsClient.update( - ruleStatusSavedObjectType, - currentStatusSavedObject.id, - { - ...currentStatusSavedObject.attributes, - } - ); - return currentStatusSavedObject; - } -}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_or_create_rule_statuses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_or_create_rule_statuses.ts new file mode 100644 index 0000000000000..913efbe04aa16 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_or_create_rule_statuses.ts @@ -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 { SavedObject } from 'src/core/server'; + +import { IRuleStatusAttributes } from '../rules/types'; +import { RuleStatusSavedObjectsClient } from './rule_status_saved_objects_client'; +import { getRuleStatusSavedObjects } from './get_rule_status_saved_objects'; + +interface RuleStatusParams { + alertId: string; + ruleStatusClient: RuleStatusSavedObjectsClient; +} + +export const createNewRuleStatus = async ({ + alertId, + ruleStatusClient, +}: RuleStatusParams): Promise> => { + const now = new Date().toISOString(); + return ruleStatusClient.create({ + alertId, + statusDate: now, + status: 'going to run', + lastFailureAt: null, + lastSuccessAt: null, + lastFailureMessage: null, + lastSuccessMessage: null, + gap: null, + bulkCreateTimeDurations: [], + searchAfterTimeDurations: [], + lastLookBackDate: null, + }); +}; + +export const getOrCreateRuleStatuses = async ({ + alertId, + ruleStatusClient, +}: RuleStatusParams): Promise>> => { + const ruleStatuses = await getRuleStatusSavedObjects({ + alertId, + ruleStatusClient, + }); + if (ruleStatuses.saved_objects.length > 0) { + return ruleStatuses.saved_objects; + } + const newStatus = await createNewRuleStatus({ alertId, ruleStatusClient }); + + return [newStatus]; +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_rule_status_saved_objects.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_rule_status_saved_objects.ts index 5a59d0413cfb9..828b4ea41096e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_rule_status_saved_objects.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_rule_status_saved_objects.ts @@ -5,24 +5,21 @@ */ import { SavedObjectsFindResponse } from 'kibana/server'; -import { AlertServices } from '../../../../../../../plugins/alerting/server'; -import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; -import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types'; +import { IRuleStatusAttributes } from '../rules/types'; +import { MAX_RULE_STATUSES } from './rule_status_service'; +import { RuleStatusSavedObjectsClient } from './rule_status_saved_objects_client'; interface GetRuleStatusSavedObject { alertId: string; - services: AlertServices; + ruleStatusClient: RuleStatusSavedObjectsClient; } export const getRuleStatusSavedObjects = async ({ alertId, - services, -}: GetRuleStatusSavedObject): Promise> => { - return services.savedObjectsClient.find({ - type: ruleStatusSavedObjectType, - perPage: 6, // 0th element is current status, 1-5 is last 5 failures. + ruleStatusClient, +}: GetRuleStatusSavedObject): Promise> => { + return ruleStatusClient.find({ + perPage: MAX_RULE_STATUSES, sortField: 'statusDate', sortOrder: 'desc', search: `${alertId}`, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_messages.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_messages.test.ts new file mode 100644 index 0000000000000..8e4b5ce3c9924 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_messages.test.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { BuildRuleMessageFactoryParams, buildRuleMessageFactory } from './rule_messages'; + +describe('buildRuleMessageFactory', () => { + let factoryParams: BuildRuleMessageFactoryParams; + beforeEach(() => { + factoryParams = { + name: 'name', + id: 'id', + ruleId: 'ruleId', + index: 'index', + }; + }); + + it('appends rule attributes to the provided message', () => { + const buildMessage = buildRuleMessageFactory(factoryParams); + + const message = buildMessage('my message'); + expect(message).toEqual(expect.stringContaining('my message')); + expect(message).toEqual(expect.stringContaining('name: "name"')); + expect(message).toEqual(expect.stringContaining('id: "id"')); + expect(message).toEqual(expect.stringContaining('rule id: "ruleId"')); + expect(message).toEqual(expect.stringContaining('signals index: "index"')); + }); + + it('joins message parts with newlines', () => { + const buildMessage = buildRuleMessageFactory(factoryParams); + + const message = buildMessage('my message'); + const messageParts = message.split('\n'); + expect(messageParts).toContain('my message'); + expect(messageParts).toContain('name: "name"'); + expect(messageParts).toContain('id: "id"'); + expect(messageParts).toContain('rule id: "ruleId"'); + expect(messageParts).toContain('signals index: "index"'); + }); + + it('joins multiple arguments with newlines', () => { + const buildMessage = buildRuleMessageFactory(factoryParams); + + const message = buildMessage('my message', 'here is more'); + const messageParts = message.split('\n'); + expect(messageParts).toContain('my message'); + expect(messageParts).toContain('here is more'); + }); + + it('defaults the rule ID if not provided ', () => { + const buildMessage = buildRuleMessageFactory({ + ...factoryParams, + ruleId: undefined, + }); + + const message = buildMessage('my message', 'here is more'); + expect(message).toEqual(expect.stringContaining('rule id: "(unknown rule id)"')); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_messages.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_messages.ts new file mode 100644 index 0000000000000..d5f9d332bbcdd --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_messages.ts @@ -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. + */ + +export type BuildRuleMessage = (...messages: string[]) => string; +export interface BuildRuleMessageFactoryParams { + name: string; + id: string; + ruleId: string | null | undefined; + index: string; +} + +export const buildRuleMessageFactory = ({ + id, + ruleId, + index, + name, +}: BuildRuleMessageFactoryParams): BuildRuleMessage => (...messages) => + [ + ...messages, + `name: "${name}"`, + `id: "${id}"`, + `rule id: "${ruleId ?? '(unknown rule id)'}"`, + `signals index: "${index}"`, + ].join('\n'); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_status_saved_objects_client.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_status_saved_objects_client.ts new file mode 100644 index 0000000000000..11cbf67304409 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_status_saved_objects_client.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 { + SavedObjectsClientContract, + SavedObject, + SavedObjectsUpdateResponse, + SavedObjectsFindOptions, + SavedObjectsFindResponse, +} from '../../../../../../../../src/core/server'; +import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; +import { IRuleStatusAttributes } from '../rules/types'; + +export interface RuleStatusSavedObjectsClient { + find: ( + options?: Omit + ) => Promise>; + create: (attributes: IRuleStatusAttributes) => Promise>; + update: ( + id: string, + attributes: Partial + ) => Promise>; + delete: (id: string) => Promise<{}>; +} + +export const ruleStatusSavedObjectsClientFactory = ( + savedObjectsClient: SavedObjectsClientContract +): RuleStatusSavedObjectsClient => ({ + find: options => + savedObjectsClient.find({ ...options, type: ruleStatusSavedObjectType }), + create: attributes => savedObjectsClient.create(ruleStatusSavedObjectType, attributes), + update: (id, attributes) => savedObjectsClient.update(ruleStatusSavedObjectType, id, attributes), + delete: id => savedObjectsClient.delete(ruleStatusSavedObjectType, id), +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_status_service.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_status_service.test.ts new file mode 100644 index 0000000000000..ea9534710d418 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_status_service.test.ts @@ -0,0 +1,195 @@ +/* + * 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 { ruleStatusSavedObjectsClientMock } from './__mocks__/rule_status_saved_objects_client.mock'; +import { + buildRuleStatusAttributes, + RuleStatusService, + ruleStatusServiceFactory, + MAX_RULE_STATUSES, +} from './rule_status_service'; +import { exampleRuleStatus, exampleFindRuleStatusResponse } from './__mocks__/es_results'; + +const expectIsoDateString = expect.stringMatching(/Z$/); +const buildStatuses = (n: number) => + Array(n) + .fill(exampleRuleStatus()) + .map((status, index) => ({ + ...status, + id: `status-index-${index}`, + })); + +describe('buildRuleStatusAttributes', () => { + it('generates a new date on each call', async () => { + const { statusDate } = buildRuleStatusAttributes('going to run'); + await new Promise(resolve => setTimeout(resolve, 10)); // ensure time has passed + const { statusDate: statusDate2 } = buildRuleStatusAttributes('going to run'); + + expect(statusDate).toEqual(expectIsoDateString); + expect(statusDate2).toEqual(expectIsoDateString); + expect(statusDate).not.toEqual(statusDate2); + }); + + it('returns a status and statusDate if "going to run"', () => { + const result = buildRuleStatusAttributes('going to run'); + expect(result).toEqual({ + status: 'going to run', + statusDate: expectIsoDateString, + }); + }); + + it('returns success fields if "success"', () => { + const result = buildRuleStatusAttributes('succeeded', 'success message'); + expect(result).toEqual({ + status: 'succeeded', + statusDate: expectIsoDateString, + lastSuccessAt: expectIsoDateString, + lastSuccessMessage: 'success message', + }); + + expect(result.statusDate).toEqual(result.lastSuccessAt); + }); + + it('returns failure fields if "failed"', () => { + const result = buildRuleStatusAttributes('failed', 'failure message'); + expect(result).toEqual({ + status: 'failed', + statusDate: expectIsoDateString, + lastFailureAt: expectIsoDateString, + lastFailureMessage: 'failure message', + }); + + expect(result.statusDate).toEqual(result.lastFailureAt); + }); +}); + +describe('ruleStatusService', () => { + let currentStatus: ReturnType; + let ruleStatusClient: ReturnType; + let service: RuleStatusService; + + beforeEach(async () => { + currentStatus = exampleRuleStatus(); + ruleStatusClient = ruleStatusSavedObjectsClientMock.create(); + ruleStatusClient.find.mockResolvedValue(exampleFindRuleStatusResponse([currentStatus])); + service = await ruleStatusServiceFactory({ alertId: 'mock-alert-id', ruleStatusClient }); + }); + + describe('goingToRun', () => { + it('updates the current status to "going to run"', async () => { + await service.goingToRun(); + + expect(ruleStatusClient.update).toHaveBeenCalledWith( + currentStatus.id, + expect.objectContaining({ + status: 'going to run', + statusDate: expectIsoDateString, + }) + ); + }); + }); + + describe('success', () => { + it('updates the current status to "succeeded"', async () => { + await service.success('hey, it worked'); + + expect(ruleStatusClient.update).toHaveBeenCalledWith( + currentStatus.id, + expect.objectContaining({ + status: 'succeeded', + statusDate: expectIsoDateString, + lastSuccessAt: expectIsoDateString, + lastSuccessMessage: 'hey, it worked', + }) + ); + }); + }); + + describe('error', () => { + beforeEach(() => { + // mock the creation of our new status + ruleStatusClient.create.mockResolvedValue(exampleRuleStatus()); + }); + + it('updates the current status to "failed"', async () => { + await service.error('oh no, it broke'); + + expect(ruleStatusClient.update).toHaveBeenCalledWith( + currentStatus.id, + expect.objectContaining({ + status: 'failed', + statusDate: expectIsoDateString, + lastFailureAt: expectIsoDateString, + lastFailureMessage: 'oh no, it broke', + }) + ); + }); + + it('does not delete statuses if we have less than the max number of statuses', async () => { + await service.error('oh no, it broke'); + + expect(ruleStatusClient.delete).not.toHaveBeenCalled(); + }); + + it('does not delete rule statuses when we just hit the limit', async () => { + // max - 1 in store, meaning our new error will put us at max + ruleStatusClient.find.mockResolvedValue( + exampleFindRuleStatusResponse(buildStatuses(MAX_RULE_STATUSES - 1)) + ); + service = await ruleStatusServiceFactory({ alertId: 'mock-alert-id', ruleStatusClient }); + + await service.error('oh no, it broke'); + + expect(ruleStatusClient.delete).not.toHaveBeenCalled(); + }); + + it('deletes stale rule status when we already have max statuses', async () => { + // max in store, meaning our new error will push one off the end + ruleStatusClient.find.mockResolvedValue( + exampleFindRuleStatusResponse(buildStatuses(MAX_RULE_STATUSES)) + ); + service = await ruleStatusServiceFactory({ alertId: 'mock-alert-id', ruleStatusClient }); + + await service.error('oh no, it broke'); + + expect(ruleStatusClient.delete).toHaveBeenCalledTimes(1); + // we should delete the 6th (index 5) + expect(ruleStatusClient.delete).toHaveBeenCalledWith('status-index-5'); + }); + + it('deletes any number of rule statuses in excess of the max', async () => { + // max + 1 in store, meaning our new error will put us two over + ruleStatusClient.find.mockResolvedValue( + exampleFindRuleStatusResponse(buildStatuses(MAX_RULE_STATUSES + 1)) + ); + service = await ruleStatusServiceFactory({ alertId: 'mock-alert-id', ruleStatusClient }); + + await service.error('oh no, it broke'); + + expect(ruleStatusClient.delete).toHaveBeenCalledTimes(2); + // we should delete the 6th (index 5) + expect(ruleStatusClient.delete).toHaveBeenCalledWith('status-index-5'); + // we should delete the 7th (index 6) + expect(ruleStatusClient.delete).toHaveBeenCalledWith('status-index-6'); + }); + + it('handles multiple error calls', async () => { + // max in store, meaning our new error will push one off the end + ruleStatusClient.find.mockResolvedValue( + exampleFindRuleStatusResponse(buildStatuses(MAX_RULE_STATUSES)) + ); + service = await ruleStatusServiceFactory({ alertId: 'mock-alert-id', ruleStatusClient }); + + await service.error('oh no, it broke'); + await service.error('oh no, it broke'); + + expect(ruleStatusClient.delete).toHaveBeenCalledTimes(2); + // we should delete the 6th (index 5) + expect(ruleStatusClient.delete).toHaveBeenCalledWith('status-index-5'); + expect(ruleStatusClient.delete).toHaveBeenCalledWith('status-index-5'); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_status_service.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_status_service.ts new file mode 100644 index 0000000000000..5bfef134b0bae --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/rule_status_service.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { assertUnreachable } from '../../../utils/build_query'; +import { IRuleStatusAttributes, RuleStatusString } from '../rules/types'; +import { getOrCreateRuleStatuses } from './get_or_create_rule_statuses'; +import { RuleStatusSavedObjectsClient } from './rule_status_saved_objects_client'; + +// 1st is mutable status, followed by 5 most recent failures +export const MAX_RULE_STATUSES = 6; + +interface Attributes { + searchAfterTimeDurations?: string[]; + bulkCreateTimeDurations?: string[]; + lastLookBackDate?: string; + gap?: string; +} + +export interface RuleStatusService { + goingToRun: () => Promise; + success: (message: string, attributes?: Attributes) => Promise; + error: (message: string, attributes?: Attributes) => Promise; +} + +export const buildRuleStatusAttributes: ( + status: RuleStatusString, + message?: string, + attributes?: Attributes +) => Partial = (status, message, attributes = {}) => { + const now = new Date().toISOString(); + const baseAttributes: Partial = { + ...attributes, + status, + statusDate: now, + }; + + switch (status) { + case 'succeeded': { + return { + ...baseAttributes, + lastSuccessAt: now, + lastSuccessMessage: message, + }; + } + case 'failed': { + return { + ...baseAttributes, + lastFailureAt: now, + lastFailureMessage: message, + }; + } + case 'going to run': { + return baseAttributes; + } + } + + assertUnreachable(status); +}; + +export const ruleStatusServiceFactory = async ({ + alertId, + ruleStatusClient, +}: { + alertId: string; + ruleStatusClient: RuleStatusSavedObjectsClient; +}): Promise => { + return { + goingToRun: async () => { + const [currentStatus] = await getOrCreateRuleStatuses({ + alertId, + ruleStatusClient, + }); + + await ruleStatusClient.update(currentStatus.id, { + ...currentStatus.attributes, + ...buildRuleStatusAttributes('going to run'), + }); + }, + + success: async (message, attributes) => { + const [currentStatus] = await getOrCreateRuleStatuses({ + alertId, + ruleStatusClient, + }); + + await ruleStatusClient.update(currentStatus.id, { + ...currentStatus.attributes, + ...buildRuleStatusAttributes('succeeded', message, attributes), + }); + }, + + error: async (message, attributes) => { + const ruleStatuses = await getOrCreateRuleStatuses({ + alertId, + ruleStatusClient, + }); + const [currentStatus] = ruleStatuses; + + const failureAttributes = { + ...currentStatus.attributes, + ...buildRuleStatusAttributes('failed', message, attributes), + }; + + // We always update the newest status, so to 'persist' a failure we push a copy to the head of the list + await ruleStatusClient.update(currentStatus.id, failureAttributes); + const newStatus = await ruleStatusClient.create(failureAttributes); + + // drop oldest failures + const oldStatuses = [newStatus, ...ruleStatuses].slice(MAX_RULE_STATUSES); + await Promise.all(oldStatuses.map(status => ruleStatusClient.delete(status.id))); + }, + }; +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index b12c21b7a5b56..06652028b3741 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -53,7 +53,7 @@ describe('searchAfterAndBulkCreate', () => { pageSize: 1, filter: undefined, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(mockService.callCluster).toHaveBeenCalledTimes(0); expect(success).toEqual(true); @@ -111,7 +111,7 @@ describe('searchAfterAndBulkCreate', () => { pageSize: 1, filter: undefined, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(mockService.callCluster).toHaveBeenCalledTimes(5); expect(success).toEqual(true); @@ -140,7 +140,7 @@ describe('searchAfterAndBulkCreate', () => { pageSize: 1, filter: undefined, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(mockLogger.error).toHaveBeenCalled(); expect(success).toEqual(false); @@ -176,7 +176,7 @@ describe('searchAfterAndBulkCreate', () => { pageSize: 1, filter: undefined, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(mockLogger.error).toHaveBeenCalled(); expect(success).toEqual(false); @@ -212,7 +212,7 @@ describe('searchAfterAndBulkCreate', () => { pageSize: 1, filter: undefined, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(success).toEqual(true); }); @@ -250,7 +250,7 @@ describe('searchAfterAndBulkCreate', () => { pageSize: 1, filter: undefined, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(success).toEqual(true); }); @@ -288,7 +288,7 @@ describe('searchAfterAndBulkCreate', () => { pageSize: 1, filter: undefined, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(success).toEqual(true); }); @@ -328,7 +328,7 @@ describe('searchAfterAndBulkCreate', () => { pageSize: 1, filter: undefined, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(success).toEqual(false); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts index ff263333fb798..a5d5dd0a7b710 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -31,7 +31,7 @@ interface SearchAfterAndBulkCreateParams { pageSize: number; filter: unknown; tags: string[]; - throttle: string | null; + throttle: string; } export interface SearchAfterAndBulkCreateReturnType { 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 ab9def14bef65..78b0cd84eeda3 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 @@ -6,11 +6,10 @@ import { performance } from 'perf_hooks'; import { Logger } from 'src/core/server'; -import { - SIGNALS_ID, - DEFAULT_SEARCH_AFTER_PAGE_SIZE, - NOTIFICATION_THROTTLE_RULE, -} from '../../../../common/constants'; + +import { SIGNALS_ID, DEFAULT_SEARCH_AFTER_PAGE_SIZE } from '../../../../common/constants'; +import { isJobStarted, isMlRule } from '../../../../common/detection_engine/ml_helpers'; +import { SetupPlugins } from '../../../plugin'; import { buildEventsSearchQuery } from './build_events_query'; import { getInputIndex } from './get_input_output_index'; @@ -21,24 +20,24 @@ import { import { getFilter } from './get_filter'; import { SignalRuleAlertTypeDefinition, RuleAlertAttributes } from './types'; import { getGapBetweenRuns, makeFloatString } from './utils'; -import { writeSignalRuleExceptionToSavedObject } from './write_signal_rule_exception_to_saved_object'; import { signalParamsSchema } from './signal_params_schema'; import { siemRuleActionGroups } from './siem_rule_action_groups'; -import { writeGapErrorToSavedObject } from './write_gap_error_to_saved_object'; -import { getRuleStatusSavedObjects } from './get_rule_status_saved_objects'; -import { getCurrentStatusSavedObject } from './get_current_status_saved_object'; -import { writeCurrentStatusSucceeded } from './write_current_status_succeeded'; import { findMlSignals } from './find_ml_signals'; import { bulkCreateMlSignals } from './bulk_create_ml_signals'; import { getSignalsCount } from '../notifications/get_signals_count'; import { scheduleNotificationActions } from '../notifications/schedule_notification_actions'; +import { ruleStatusServiceFactory } from './rule_status_service'; +import { buildRuleMessageFactory } from './rule_messages'; +import { ruleStatusSavedObjectsClientFactory } from './rule_status_saved_objects_client'; export const signalRulesAlertType = ({ logger, version, + ml, }: { logger: Logger; version: string; + ml: SetupPlugins['ml']; }): SignalRuleAlertTypeDefinition => { return { id: SIGNALS_ID, @@ -64,22 +63,15 @@ export const signalRulesAlertType = ({ to, type, } = params; + const ruleStatusClient = ruleStatusSavedObjectsClientFactory(services.savedObjectsClient); + const ruleStatusService = await ruleStatusServiceFactory({ + alertId, + ruleStatusClient, + }); const savedObject = await services.savedObjectsClient.get( 'alert', alertId ); - - const ruleStatusSavedObjects = await getRuleStatusSavedObjects({ - alertId, - services, - }); - - const currentStatusSavedObject = await getCurrentStatusSavedObject({ - alertId, - services, - ruleStatusSavedObjects, - }); - const { actions, name, @@ -92,23 +84,31 @@ export const signalRulesAlertType = ({ throttle, params: ruleParams, } = savedObject.attributes; - const updatedAt = savedObject.updated_at ?? ''; - - const gap = getGapBetweenRuns({ previousStartedAt, interval, from, to }); - await writeGapErrorToSavedObject({ - alertId, - logger, - ruleId: ruleId ?? '(unknown rule id)', - currentStatusSavedObject, - services, - gap, - ruleStatusSavedObjects, + const buildRuleMessage = buildRuleMessageFactory({ + id: alertId, + ruleId, name, + index: outputIndex, }); + logger.debug(buildRuleMessage('[+] Starting Signal Rule execution')); + await ruleStatusService.goingToRun(); + + const gap = getGapBetweenRuns({ previousStartedAt, interval, from, to }); + if (gap != null && gap.asMilliseconds() > 0) { + const gapString = gap.humanize(); + const gapMessage = buildRuleMessage( + `${gapString} (${gap.asMilliseconds()}ms) has passed since last rule execution, and signals may have been missed.`, + 'Consider increasing your look behind time or adding more Kibana instances.' + ); + logger.warn(gapMessage); + + await ruleStatusService.error(gapMessage, { gap: gapString }); + } + const searchAfterSize = Math.min(params.maxSignals, DEFAULT_SEARCH_AFTER_PAGE_SIZE); - let creationSucceeded: SearchAfterAndBulkCreateReturnType = { + let result: SearchAfterAndBulkCreateReturnType = { success: false, bulkCreateTimes: [], searchAfterTimes: [], @@ -116,11 +116,34 @@ export const signalRulesAlertType = ({ }; try { - if (type === 'machine_learning') { + if (isMlRule(type)) { + if (ml == null) { + throw new Error('ML plugin unavailable during rule execution'); + } if (machineLearningJobId == null || anomalyThreshold == null) { throw new Error( - `Attempted to execute machine learning rule, but it is missing job id and/or anomaly threshold for rule id: "${ruleId}", name: "${name}", signals index: "${outputIndex}", job id: "${machineLearningJobId}", anomaly threshold: "${anomalyThreshold}"` + [ + 'Machine learning rule is missing job id and/or anomaly threshold:', + `job id: "${machineLearningJobId}"`, + `anomaly threshold: "${anomalyThreshold}"`, + ].join('\n') + ); + } + + const summaryJobs = await ml + .jobServiceProvider(ml.mlClient.callAsInternalUser) + .jobsSummary([machineLearningJobId]); + const jobSummary = summaryJobs.find(job => job.id === machineLearningJobId); + + if (jobSummary == null || !isJobStarted(jobSummary.jobState, jobSummary.datafeedState)) { + const errorMessage = buildRuleMessage( + 'Machine learning job is not started:', + `job id: "${machineLearningJobId}"`, + `job status: "${jobSummary?.jobState}"`, + `datafeed status: "${jobSummary?.datafeedState}"` ); + logger.warn(errorMessage); + await ruleStatusService.error(errorMessage); } const anomalyResults = await findMlSignals( @@ -130,12 +153,9 @@ export const signalRulesAlertType = ({ to, services.callCluster ); - const anomalyCount = anomalyResults.hits.hits.length; if (anomalyCount) { - logger.info( - `Found ${anomalyCount} signals from ML anomalies for signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}", pushing signals to index "${outputIndex}"` - ); + logger.info(buildRuleMessage(`Found ${anomalyCount} signals from ML anomalies.`)); } const { success, bulkCreateDuration } = await bulkCreateMlSignals({ @@ -156,9 +176,9 @@ export const signalRulesAlertType = ({ enabled, tags, }); - creationSucceeded.success = success; + result.success = success; if (bulkCreateDuration) { - creationSucceeded.bulkCreateTimes.push(bulkCreateDuration); + result.bulkCreateTimes.push(bulkCreateDuration); } } else { const inputIndex = await getInputIndex(services, version, index); @@ -181,27 +201,21 @@ export const signalRulesAlertType = ({ searchAfterSortId: undefined, }); - logger.debug( - `Starting signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}"` - ); - logger.debug( - `[+] Initial search call of signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}"` - ); + logger.debug(buildRuleMessage('[+] Initial search call')); const start = performance.now(); const noReIndexResult = await services.callCluster('search', noReIndex); const end = performance.now(); - if (noReIndexResult.hits.total.value !== 0) { + const signalCount = noReIndexResult.hits.total.value; + if (signalCount !== 0) { logger.info( - `Found ${ - noReIndexResult.hits.total.value - } signals from the indexes of "[${inputIndex.join( - ', ' - )}]" using signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}", pushing signals to index "${outputIndex}"` + buildRuleMessage( + `Found ${signalCount} signals from the indexes of "[${inputIndex.join(', ')}]"` + ) ); } - creationSucceeded = await searchAfterAndBulkCreate({ + result = await searchAfterAndBulkCreate({ someResult: noReIndexResult, ruleParams: params, services, @@ -222,11 +236,11 @@ export const signalRulesAlertType = ({ tags, throttle, }); - creationSucceeded.searchAfterTimes.push(makeFloatString(end - start)); + result.searchAfterTimes.push(makeFloatString(end - start)); } - if (creationSucceeded.success) { - if (meta?.throttle === NOTIFICATION_THROTTLE_RULE && actions.length) { + if (result.success) { + if (actions.length) { const notificationRuleParams = { ...ruleParams, name, @@ -237,14 +251,12 @@ export const signalRulesAlertType = ({ to: 'now', index: ruleParams.outputIndex, ruleId: ruleParams.ruleId!, - kibanaSiemAppUrl: meta.kibanaSiemAppUrl as string, + kibanaSiemAppUrl: meta?.kibanaSiemAppUrl as string, ruleAlertId: savedObject.id, callCluster: services.callCluster, }); - logger.info( - `Found ${signalsCount} signals using signal rule name: "${notificationRuleParams.name}", id: "${notificationRuleParams.ruleId}", rule_id: "${notificationRuleParams.ruleId}" in "${notificationRuleParams.outputIndex}" index` - ); + logger.info(buildRuleMessage(`Found ${signalsCount} signals for notification.`)); if (signalsCount) { const alertInstance = services.alertInstanceFactory(alertId); @@ -257,44 +269,35 @@ export const signalRulesAlertType = ({ } } - logger.debug( - `Finished signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}"` - ); - await writeCurrentStatusSucceeded({ - services, - currentStatusSavedObject, - bulkCreateTimes: creationSucceeded.bulkCreateTimes, - searchAfterTimes: creationSucceeded.searchAfterTimes, - lastLookBackDate: creationSucceeded.lastLookBackDate?.toISOString() ?? null, + logger.debug(buildRuleMessage('[+] Signal Rule execution completed.')); + await ruleStatusService.success('succeeded', { + bulkCreateTimeDurations: result.bulkCreateTimes, + searchAfterTimeDurations: result.searchAfterTimes, + lastLookBackDate: result.lastLookBackDate?.toISOString(), }); } else { - await writeSignalRuleExceptionToSavedObject({ - name, - alertId, - currentStatusSavedObject, - logger, - message: `Bulk Indexing signals failed. Check logs for further details \nRule name: "${name}"\nid: "${alertId}"\nrule_id: "${ruleId}"\n`, - services, - ruleStatusSavedObjects, - ruleId: ruleId ?? '(unknown rule id)', - bulkCreateTimes: creationSucceeded.bulkCreateTimes, - searchAfterTimes: creationSucceeded.searchAfterTimes, - lastLookBackDate: creationSucceeded.lastLookBackDate?.toISOString() ?? null, + const errorMessage = buildRuleMessage( + 'Bulk Indexing of signals failed. Check logs for further details.' + ); + logger.error(errorMessage); + await ruleStatusService.error(errorMessage, { + bulkCreateTimeDurations: result.bulkCreateTimes, + searchAfterTimeDurations: result.searchAfterTimes, + lastLookBackDate: result.lastLookBackDate?.toISOString(), }); } - } catch (err) { - await writeSignalRuleExceptionToSavedObject({ - name, - alertId, - currentStatusSavedObject, - logger, - message: `Bulk Indexing signals failed. Check logs for further details \nRule name: "${name}"\nid: "${alertId}"\nrule_id: "${ruleId}"\n`, - services, - ruleStatusSavedObjects, - ruleId: ruleId ?? '(unknown rule id)', - bulkCreateTimes: creationSucceeded.bulkCreateTimes, - searchAfterTimes: creationSucceeded.searchAfterTimes, - lastLookBackDate: creationSucceeded.lastLookBackDate?.toISOString() ?? null, + } catch (error) { + const errorMessage = error.message ?? '(no error message given)'; + const message = buildRuleMessage( + 'An error occurred during rule execution:', + `message: "${errorMessage}"` + ); + + logger.error(message); + await ruleStatusService.error(message, { + bulkCreateTimeDurations: result.bulkCreateTimes, + searchAfterTimeDurations: result.searchAfterTimes, + lastLookBackDate: result.lastLookBackDate?.toISOString(), }); } }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts index 93f9c24a057f2..45b5610e2d3c3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts @@ -160,7 +160,7 @@ describe('singleBulkCreate', () => { interval: '5m', enabled: true, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(success).toEqual(true); }); @@ -192,7 +192,7 @@ describe('singleBulkCreate', () => { interval: '5m', enabled: true, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(success).toEqual(true); }); @@ -216,7 +216,7 @@ describe('singleBulkCreate', () => { interval: '5m', enabled: true, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(success).toEqual(true); }); @@ -241,7 +241,7 @@ describe('singleBulkCreate', () => { interval: '5m', enabled: true, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(mockLogger.error).not.toHaveBeenCalled(); @@ -268,7 +268,7 @@ describe('singleBulkCreate', () => { interval: '5m', enabled: true, tags: ['some fake tag 1', 'some fake tag 2'], - throttle: null, + throttle: 'no_actions', }); expect(mockLogger.error).toHaveBeenCalled(); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts index 0192ff76efa54..ffec40b839bf6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts @@ -30,7 +30,7 @@ interface SingleBulkCreateParams { interval: string; enabled: boolean; tags: string[]; - throttle: string | null; + throttle: string; } /** diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts index 93c48ed38c7c4..543e8bf0619b0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts @@ -158,7 +158,7 @@ export interface AlertAttributes { schedule: { interval: string; }; - throttle: string | null; + throttle: string; } export interface RuleAlertAttributes extends AlertAttributes { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_current_status_succeeded.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_current_status_succeeded.ts deleted file mode 100644 index 50136790c3479..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_current_status_succeeded.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 { SavedObject } from 'src/core/server'; -import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; - -import { AlertServices } from '../../../../../../../plugins/alerting/server'; -import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types'; - -interface GetRuleStatusSavedObject { - services: AlertServices; - currentStatusSavedObject: SavedObject; - lastLookBackDate: string | null | undefined; - bulkCreateTimes: string[] | null | undefined; - searchAfterTimes: string[] | null | undefined; -} - -export const writeCurrentStatusSucceeded = async ({ - services, - currentStatusSavedObject, - lastLookBackDate, - bulkCreateTimes, - searchAfterTimes, -}: GetRuleStatusSavedObject): Promise => { - const sDate = new Date().toISOString(); - currentStatusSavedObject.attributes.status = 'succeeded'; - currentStatusSavedObject.attributes.statusDate = sDate; - currentStatusSavedObject.attributes.lastSuccessAt = sDate; - currentStatusSavedObject.attributes.lastSuccessMessage = 'succeeded'; - if (lastLookBackDate != null) { - currentStatusSavedObject.attributes.lastLookBackDate = lastLookBackDate; - } - if (bulkCreateTimes != null) { - currentStatusSavedObject.attributes.bulkCreateTimeDurations = bulkCreateTimes; - } - if (searchAfterTimes != null) { - currentStatusSavedObject.attributes.searchAfterTimeDurations = searchAfterTimes; - } - await services.savedObjectsClient.update(ruleStatusSavedObjectType, currentStatusSavedObject.id, { - ...currentStatusSavedObject.attributes, - }); -}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_gap_error_to_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_gap_error_to_saved_object.ts deleted file mode 100644 index e47e5388527da..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_gap_error_to_saved_object.ts +++ /dev/null @@ -1,62 +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 moment from 'moment'; -import { Logger, SavedObject, SavedObjectsFindResponse } from 'src/core/server'; - -import { AlertServices } from '../../../../../../../plugins/alerting/server'; -import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types'; -import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; - -interface WriteGapErrorToSavedObjectParams { - logger: Logger; - alertId: string; - ruleId: string; - currentStatusSavedObject: SavedObject; - ruleStatusSavedObjects: SavedObjectsFindResponse; - services: AlertServices; - gap: moment.Duration | null | undefined; - name: string; -} - -export const writeGapErrorToSavedObject = async ({ - alertId, - currentStatusSavedObject, - logger, - services, - ruleStatusSavedObjects, - ruleId, - gap, - name, -}: WriteGapErrorToSavedObjectParams): Promise => { - if (gap != null && gap.asMilliseconds() > 0) { - logger.warn( - `Signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}" has a time gap of ${gap.humanize()} (${gap.asMilliseconds()}ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.` - ); - // write a failure status whenever we have a time gap - // this is a temporary solution until general activity - // monitoring is developed as a feature - const gapDate = new Date().toISOString(); - await services.savedObjectsClient.create(ruleStatusSavedObjectType, { - alertId, - statusDate: gapDate, - status: 'failed', - lastFailureAt: gapDate, - lastSuccessAt: currentStatusSavedObject.attributes.lastSuccessAt, - lastFailureMessage: `Signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}" has a time gap of ${gap.humanize()} (${gap.asMilliseconds()}ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.`, - lastSuccessMessage: currentStatusSavedObject.attributes.lastSuccessMessage, - gap: gap.humanize(), - }); - - if (ruleStatusSavedObjects.saved_objects.length >= 6) { - // delete fifth status and prepare to insert a newer one. - const toDelete = ruleStatusSavedObjects.saved_objects.slice(5); - await toDelete.forEach(async item => - services.savedObjectsClient.delete(ruleStatusSavedObjectType, item.id) - ); - } - } -}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_signal_rule_exception_to_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_signal_rule_exception_to_saved_object.ts deleted file mode 100644 index 2a14184859591..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_signal_rule_exception_to_saved_object.ts +++ /dev/null @@ -1,73 +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 { Logger, SavedObject, SavedObjectsFindResponse } from 'src/core/server'; - -import { AlertServices } from '../../../../../../../plugins/alerting/server'; -import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types'; -import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; - -interface SignalRuleExceptionParams { - logger: Logger; - alertId: string; - ruleId: string; - currentStatusSavedObject: SavedObject; - ruleStatusSavedObjects: SavedObjectsFindResponse; - message: string; - services: AlertServices; - name: string; - lastLookBackDate?: string | null | undefined; - bulkCreateTimes?: string[] | null | undefined; - searchAfterTimes?: string[] | null | undefined; -} - -export const writeSignalRuleExceptionToSavedObject = async ({ - alertId, - currentStatusSavedObject, - logger, - message, - services, - ruleStatusSavedObjects, - ruleId, - name, - lastLookBackDate, - bulkCreateTimes, - searchAfterTimes, -}: SignalRuleExceptionParams): Promise => { - logger.error( - `Error from signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}" message: ${message}` - ); - const sDate = new Date().toISOString(); - currentStatusSavedObject.attributes.status = 'failed'; - currentStatusSavedObject.attributes.statusDate = sDate; - currentStatusSavedObject.attributes.lastFailureAt = sDate; - currentStatusSavedObject.attributes.lastFailureMessage = message; - if (lastLookBackDate) { - currentStatusSavedObject.attributes.lastLookBackDate = lastLookBackDate; - } - if (bulkCreateTimes) { - currentStatusSavedObject.attributes.bulkCreateTimeDurations = bulkCreateTimes; - } - if (searchAfterTimes) { - currentStatusSavedObject.attributes.searchAfterTimeDurations = searchAfterTimes; - } - // current status is failing - await services.savedObjectsClient.update(ruleStatusSavedObjectType, currentStatusSavedObject.id, { - ...currentStatusSavedObject.attributes, - }); - // create new status for historical purposes - await services.savedObjectsClient.create(ruleStatusSavedObjectType, { - ...currentStatusSavedObject.attributes, - }); - - if (ruleStatusSavedObjects.saved_objects.length >= 6) { - // delete fifth status and prepare to insert a newer one. - const toDelete = ruleStatusSavedObjects.saved_objects.slice(5); - await toDelete.forEach(async item => - services.savedObjectsClient.delete(ruleStatusSavedObjectType, item.id) - ); - } -}; 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 aae8763a7ea39..efa0a92cc573b 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 @@ -8,7 +8,7 @@ import { CallAPIOptions } from '../../../../../../../src/core/server'; import { Filter } from '../../../../../../../src/plugins/data/server'; import { IRuleStatusAttributes } from './rules/types'; import { ListsDefaultArraySchema } from './routes/schemas/types/lists_default_array'; -import { RuleAlertAction } from '../../../common/detection_engine/types'; +import { RuleAlertAction, RuleType } from '../../../common/detection_engine/types'; export type PartialFilter = Partial; @@ -28,7 +28,6 @@ export interface ThreatParams { // TODO: Eventually this whole RuleAlertParams will be replaced with io-ts. For now we can slowly strangle it out and reduce duplicate types // We don't have the input types defined through io-ts just yet but as we being introducing types from there we will more and more remove // types and share them between input and output schema but have an input Rule Schema and an output Rule Schema. -export type RuleType = 'query' | 'saved_query' | 'machine_learning'; export interface RuleAlertParams { actions: RuleAlertAction[]; @@ -61,7 +60,7 @@ export interface RuleAlertParams { threat: ThreatParams[] | undefined | null; type: RuleType; version: number; - throttle: string | null; + throttle: string; lists: ListsDefaultArraySchema | null | undefined; } @@ -119,7 +118,6 @@ export type OutputRuleAlertRest = RuleAlertParamsRest & { created_by: string | undefined | null; updated_by: string | undefined | null; immutable: boolean; - throttle: string | undefined | null; }; export type ImportRuleAlertRest = Omit & { diff --git a/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts b/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts index eb483de000915..f2662c79d3393 100644 --- a/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts @@ -316,6 +316,7 @@ export const signalFieldsMap: Readonly> = { 'signal.rule.created_by': 'signal.rule.created_by', 'signal.rule.updated_by': 'signal.rule.updated_by', 'signal.rule.version': 'signal.rule.version', + 'signal.rule.note': 'signal.rule.note', }; export const ruleFieldsMap: Readonly> = { diff --git a/x-pack/legacy/plugins/siem/server/lib/timeline/routes/schemas/schemas.ts b/x-pack/legacy/plugins/siem/server/lib/timeline/routes/schemas/schemas.ts index 63aee97729141..6552f973a66fa 100644 --- a/x-pack/legacy/plugins/siem/server/lib/timeline/routes/schemas/schemas.ts +++ b/x-pack/legacy/plugins/siem/server/lib/timeline/routes/schemas/schemas.ts @@ -6,14 +6,14 @@ import Joi from 'joi'; const allowEmptyString = Joi.string().allow([null, '']); -const columnHeaderType = Joi.string(); +const columnHeaderType = allowEmptyString; export const created = Joi.number().allow(null); -export const createdBy = Joi.string(); +export const createdBy = allowEmptyString; export const description = allowEmptyString; export const end = Joi.number(); export const eventId = allowEmptyString; -export const eventType = Joi.string(); +export const eventType = allowEmptyString; export const filters = Joi.array() .items( @@ -24,19 +24,11 @@ export const filters = Joi.array() disabled: Joi.boolean().allow(null), field: allowEmptyString, formattedValue: allowEmptyString, - index: { - type: 'keyword', - }, - key: { - type: 'keyword', - }, - negate: { - type: 'boolean', - }, + index: allowEmptyString, + key: allowEmptyString, + negate: Joi.boolean().allow(null), params: allowEmptyString, - type: { - type: 'keyword', - }, + type: allowEmptyString, value: allowEmptyString, }), exists: allowEmptyString, @@ -68,22 +60,22 @@ export const version = allowEmptyString; export const columns = Joi.array().items( Joi.object({ aggregatable: Joi.boolean().allow(null), - category: Joi.string(), + category: allowEmptyString, columnHeaderType, description, example: allowEmptyString, indexes: allowEmptyString, - id: Joi.string(), + id: allowEmptyString, name, placeholder: allowEmptyString, searchable: Joi.boolean().allow(null), - type: Joi.string(), + type: allowEmptyString, }).required() ); export const dataProviders = Joi.array() .items( Joi.object({ - id: Joi.string(), + id: allowEmptyString, name: allowEmptyString, enabled: Joi.boolean().allow(null), excluded: Joi.boolean().allow(null), @@ -98,7 +90,7 @@ export const dataProviders = Joi.array() and: Joi.array() .items( Joi.object({ - id: Joi.string(), + id: allowEmptyString, name, enabled: Joi.boolean().allow(null), excluded: Joi.boolean().allow(null), @@ -122,9 +114,9 @@ export const dateRange = Joi.object({ }); export const favorite = Joi.array().items( Joi.object({ - keySearch: Joi.string(), - fullName: Joi.string(), - userName: Joi.string(), + keySearch: allowEmptyString, + fullName: allowEmptyString, + userName: allowEmptyString, favoriteDate: Joi.number(), }).allow(null) ); @@ -141,26 +133,26 @@ const noteItem = Joi.object({ }); export const eventNotes = Joi.array().items(noteItem); export const globalNotes = Joi.array().items(noteItem); -export const kqlMode = Joi.string(); +export const kqlMode = allowEmptyString; export const kqlQuery = Joi.object({ filterQuery: Joi.object({ kuery: Joi.object({ - kind: Joi.string(), + kind: allowEmptyString, expression: allowEmptyString, }), serializedQuery: allowEmptyString, }), }); export const pinnedEventIds = Joi.array() - .items(Joi.string()) + .items(allowEmptyString) .allow(null); export const sort = Joi.object({ - columnId: Joi.string(), - sortDirection: Joi.string(), + columnId: allowEmptyString, + sortDirection: allowEmptyString, }); /* eslint-disable @typescript-eslint/camelcase */ -export const ids = Joi.array().items(Joi.string()); +export const ids = Joi.array().items(allowEmptyString); export const exclude_export_details = Joi.boolean(); export const file_name = allowEmptyString; diff --git a/x-pack/legacy/plugins/siem/server/plugin.ts b/x-pack/legacy/plugins/siem/server/plugin.ts index 98631ea220a54..13b58fa1d57eb 100644 --- a/x-pack/legacy/plugins/siem/server/plugin.ts +++ b/x-pack/legacy/plugins/siem/server/plugin.ts @@ -18,6 +18,7 @@ import { } from '../../../../../src/core/server'; import { SecurityPluginSetup as SecuritySetup } from '../../../../plugins/security/server'; import { PluginSetupContract as FeaturesSetup } from '../../../../plugins/features/server'; +import { MlPluginSetup as MlSetup } from '../../../../plugins/ml/server'; import { EncryptedSavedObjectsPluginSetup as EncryptedSavedObjectsSetup } from '../../../../plugins/encrypted_saved_objects/server'; import { SpacesPluginSetup as SpacesSetup } from '../../../../plugins/spaces/server'; import { PluginStartContract as ActionsStart } from '../../../../plugins/actions/server'; @@ -35,6 +36,7 @@ import { pinnedEventSavedObjectType, timelineSavedObjectType, ruleStatusSavedObjectType, + ruleActionsSavedObjectType, } from './saved_objects'; import { SiemClientFactory } from './client'; import { hasListsFeature, listsEnvFeatureFlagName } from './lib/detection_engine/feature_flags'; @@ -48,6 +50,7 @@ export interface SetupPlugins { licensing: LicensingPluginSetup; security?: SecuritySetup; spaces?: SpacesSetup; + ml?: MlSetup; } export interface StartPlugins { @@ -119,6 +122,11 @@ export class Plugin { pinnedEventSavedObjectType, timelineSavedObjectType, ruleStatusSavedObjectType, + ruleActionsSavedObjectType, + 'cases', + 'cases-comments', + 'cases-configure', + 'cases-user-actions', ], read: ['config'], }, @@ -145,6 +153,11 @@ export class Plugin { pinnedEventSavedObjectType, timelineSavedObjectType, ruleStatusSavedObjectType, + ruleActionsSavedObjectType, + 'cases', + 'cases-comments', + 'cases-configure', + 'cases-user-actions', ], }, ui: [ @@ -164,6 +177,7 @@ export class Plugin { const signalRuleType = signalRulesAlertType({ logger: this.logger, version: this.context.env.packageInfo.version, + ml: plugins.ml, }); const ruleNotificationType = rulesNotificationAlertType({ logger: this.logger, diff --git a/x-pack/legacy/plugins/siem/server/saved_objects.ts b/x-pack/legacy/plugins/siem/server/saved_objects.ts index 76d8837883b8b..7b097eefedb46 100644 --- a/x-pack/legacy/plugins/siem/server/saved_objects.ts +++ b/x-pack/legacy/plugins/siem/server/saved_objects.ts @@ -17,11 +17,16 @@ import { ruleStatusSavedObjectMappings, ruleStatusSavedObjectType, } from './lib/detection_engine/rules/saved_object_mappings'; +import { + ruleActionsSavedObjectMappings, + ruleActionsSavedObjectType, +} from './lib/detection_engine/rule_actions/saved_object_mappings'; export { noteSavedObjectType, pinnedEventSavedObjectType, ruleStatusSavedObjectType, + ruleActionsSavedObjectType, timelineSavedObjectType, }; export const savedObjectMappings = { @@ -29,4 +34,5 @@ export const savedObjectMappings = { ...noteSavedObjectMappings, ...pinnedEventSavedObjectMappings, ...ruleStatusSavedObjectMappings, + ...ruleActionsSavedObjectMappings, }; diff --git a/x-pack/legacy/plugins/siem/server/types.ts b/x-pack/legacy/plugins/siem/server/types.ts index 4119645a5af47..a52322f5f830c 100644 --- a/x-pack/legacy/plugins/siem/server/types.ts +++ b/x-pack/legacy/plugins/siem/server/types.ts @@ -7,12 +7,8 @@ import { Legacy } from 'kibana'; import { SiemClient } from './client'; -export { LegacyRequest } from '../../../../../src/core/server'; - export interface LegacyServices { - alerting?: Legacy.Server['plugins']['alerting']; config: Legacy.Server['config']; - route: Legacy.Server['route']; } export { SiemClient }; @@ -23,6 +19,6 @@ export interface SiemRequestContext { declare module 'src/core/server' { interface RequestHandlerContext { - siem: SiemRequestContext; + siem?: SiemRequestContext; } } diff --git a/x-pack/legacy/plugins/uptime/common/graphql/types.ts b/x-pack/legacy/plugins/uptime/common/graphql/types.ts index bd017e6cfaf4c..c8beb91d807d5 100644 --- a/x-pack/legacy/plugins/uptime/common/graphql/types.ts +++ b/x-pack/legacy/plugins/uptime/common/graphql/types.ts @@ -8,7 +8,6 @@ // Scalars // ==================================================== - export type UnsignedInteger = any; // ==================================================== @@ -367,7 +366,6 @@ export interface DocCount { count: UnsignedInteger; } - export interface Snapshot { counts: SnapshotCount; } @@ -380,7 +378,6 @@ export interface SnapshotCount { total: number; } - /** The primary object returned for monitor states. */ export interface MonitorSummaryResult { /** Used to go to the next page of results */ @@ -524,7 +521,6 @@ export interface SummaryHistogramPoint { down: number; } - export interface AllPingsQueryArgs { /** Optional: the direction to sort by. Accepts 'asc' and 'desc'. Defaults to 'desc'. */ sort?: string | null; @@ -540,6 +536,7 @@ export interface AllPingsQueryArgs { dateRangeEnd: string; /** Optional: agent location to filter by. */ location?: string | null; + page?: number; } export interface GetMonitorStatesQueryArgs { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/index.js b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/index.js index 9b8bdb50441c9..0ea5e1e2b8372 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/index.js +++ b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/index.js @@ -27,6 +27,7 @@ export class Typeahead extends Component { index: null, value: '', inputIsPristine: true, + lastSubmitted: '', }; static getDerivedStateFromProps(props, state) { @@ -151,7 +152,10 @@ export class Typeahead extends Component { }; onSubmit = () => { - this.props.onSubmit(this.state.value); + if (this.state.lastSubmitted !== this.state.value) { + this.props.onSubmit(this.state.value); + this.setState({ lastSubmitted: this.state.value }); + } this.setState({ isSuggestionsVisible: false }); }; @@ -177,6 +181,7 @@ export class Typeahead extends Component { value={this.state.value} onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} + onBlur={this.onSubmit} onChange={this.onChangeInputValue} onClick={this.onClickInput} autoComplete="off" diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx index 11f6565734782..168d71a31dd45 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx @@ -7,14 +7,14 @@ import React, { useEffect, useState, useContext, useRef } from 'react'; import uuid from 'uuid'; import styled from 'styled-components'; +import { npStart } from 'ui/new_platform'; -import { start } from '../../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; +import { ViewMode } from '../../../../../../../../../src/plugins/embeddable/public'; import * as i18n from './translations'; -// @ts-ignore -import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../maps/common/constants'; +import { MapEmbeddable, MapEmbeddableInput } from '../../../../../../maps/public'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../../../plugins/maps/public'; import { Location } from '../../../../../common/runtime_types'; -import { MapEmbeddable } from './types'; import { getLayerList } from './map_config'; import { UptimeThemeContext } from '../../../../contexts'; @@ -47,9 +47,9 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp const { colors } = useContext(UptimeThemeContext); const [embeddable, setEmbeddable] = useState(); const embeddableRoot: React.RefObject = useRef(null); - const factory = start.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); + const factory = npStart.plugins.embeddable.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); - const input = { + const input: MapEmbeddableInput = { id: uuid.v4(), filters: [], hidePanelTitles: true, @@ -57,7 +57,7 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp value: 0, pause: false, }, - viewMode: 'view', + viewMode: ViewMode.VIEW, isLayerTOCOpen: false, hideFilterActions: true, // Zoom Lat/Lon values are set to make sure map is in center in the panel diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/types.ts b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/types.ts deleted file mode 100644 index 03cb33c3459d2..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/types.ts +++ /dev/null @@ -1,31 +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 { Query } from 'src/plugins/data/common'; -import { TimeRange } from 'src/plugins/data/public'; -import { - EmbeddableInput, - EmbeddableOutput, - IEmbeddable, -} from '../../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; - -import { Filter } from '../../../../../../../../../src/plugins/data/public'; - -export interface MapEmbeddableInput extends EmbeddableInput { - filters: Filter[]; - query: Query; - refreshConfig: { - isPaused: boolean; - interval: number; - }; - timeRange?: TimeRange; -} - -export interface CustomProps { - setLayerList: Function; -} - -export type MapEmbeddable = IEmbeddable & CustomProps; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap index d19de73c16c5f..2e59ec5e57337 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap @@ -364,17 +364,16 @@ exports[`PingList component renders sorted list without errors 1`] = ` onChange={[Function]} pagination={ Object { - "initialPageSize": 20, + "initialPageSize": 25, "pageIndex": 0, - "pageSize": 30, + "pageSize": 10, "pageSizeOptions": Array [ - 5, 10, - 20, + 25, 50, 100, ], - "totalItemCount": 30, + "totalItemCount": 9231, } } responsive={true} diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/ping_list.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/ping_list.test.tsx index 36586b182de43..68d285bd0baf1 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/ping_list.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/ping_list.test.tsx @@ -204,9 +204,11 @@ describe('PingList component', () => { loading={false} data={{ allPings }} onPageCountChange={jest.fn()} + onPageIndexChange={jest.fn()} onSelectedLocationChange={(_loc: any[]) => {}} onSelectedStatusChange={jest.fn()} - pageSize={30} + pageIndex={0} + pageSize={10} selectedOption="down" selectedLocation={AllLocationOption.value} /> diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/ping_list.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/ping_list.tsx index d245bc1456e6a..19768c7104e91 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/ping_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/ping_list.tsx @@ -41,7 +41,9 @@ interface PingListProps { onSelectedStatusChange: (status: string | undefined) => void; onSelectedLocationChange: (location: any) => void; onPageCountChange: (itemCount: number) => void; + onPageIndexChange: (index: number) => void; pageSize: number; + pageIndex: number; selectedOption: string; selectedLocation: string | undefined; } @@ -78,8 +80,10 @@ export const PingListComponent = ({ data, loading, onPageCountChange, + onPageIndexChange, onSelectedLocationChange, onSelectedStatusChange, + pageIndex, pageSize, selectedOption, selectedLocation, @@ -232,15 +236,11 @@ export const PingListComponent = ({ ]; const pagination: Pagination = { - initialPageSize: 20, - pageIndex: 0, + initialPageSize: 25, + pageIndex, pageSize, - pageSizeOptions: [5, 10, 20, 50, 100], - /** - * we're not currently supporting pagination in this component - * so the first page is the only page - */ - totalItemCount: pageSize, + pageSizeOptions: [10, 25, 50, 100], + totalItemCount: data?.allPings?.total ?? pageSize, }; return ( @@ -323,9 +323,10 @@ export const PingListComponent = ({ itemId="id" itemIdToExpandedRowMap={itemIdToExpandedRowMap} pagination={pagination} - onChange={(criteria: CriteriaWithPagination) => - onPageCountChange(criteria.page!.size) - } + onChange={(criteria: CriteriaWithPagination) => { + onPageCountChange(criteria.page!.size); + onPageIndexChange(criteria.page!.index); + }} /> diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap index 354521e7c55b9..ead27425c26f3 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap @@ -53,9 +53,18 @@ exports[`ML Flyout component renders without errors 1`] = ` + + + Cancel + + @@ -206,8 +215,26 @@ exports[`ML Flyout component shows license info if no ml available 1`] = ` class="euiFlyoutFooter" >
+
+ +
diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_flyout.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_flyout.tsx index 917367f3e8dad..fdecfbf20810c 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_flyout.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_flyout.tsx @@ -7,6 +7,7 @@ import React, { useContext } from 'react'; import { EuiButton, + EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiFlyout, @@ -64,11 +65,15 @@ export function MLFlyoutView({ isCreatingJob, onClickCreate, onClose, canCreateM {labels.TAKE_SOME_TIME_TEXT}

- - + + + onClose()} disabled={isCreatingJob || isLoadingMLJob}> + {labels.CANCEL_LABEL} + + onClickCreate()} diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/translations.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/translations.tsx index 570dd9d1bfa26..32374674771e8 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/translations.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/translations.tsx @@ -124,6 +124,13 @@ export const CREATE_NEW_JOB = i18n.translate( } ); +export const CANCEL_LABEL = i18n.translate( + 'xpack.uptime.ml.enableAnomalyDetectionPanel.cancelLabel', + { + defaultMessage: 'Cancel', + } +); + export const CREAT_ML_JOB_DESC = i18n.translate( 'xpack.uptime.ml.enableAnomalyDetectionPanel.createMLJobDescription', { diff --git a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx index 5871783dffdeb..3de636cac6ecd 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx @@ -51,6 +51,7 @@ export const MonitorPageComponent: React.FC = ({ const { dateRangeStart, dateRangeEnd, selectedPingStatus } = params; const [selectedLocation, setSelectedLocation] = useState(undefined); + const [pingListIndex, setPingListIndex] = useState(0); const sharedVariables = { dateRangeStart, @@ -81,11 +82,14 @@ export const MonitorPageComponent: React.FC = ({ updateUrlParams({ selectedPingStatus: selectedStatus || '' }); refreshApp(); }} + onPageIndexChange={(index: number) => setPingListIndex(index)} + pageIndex={pingListIndex} pageSize={pingListPageCount} selectedOption={selectedPingStatus} selectedLocation={selectedLocation} variables={{ ...sharedVariables, + page: pingListIndex, size: pingListPageCount, status: selectedPingStatus, }} diff --git a/x-pack/legacy/plugins/uptime/public/queries/pings_query.ts b/x-pack/legacy/plugins/uptime/public/queries/pings_query.ts index 203c7ef426c19..ed20fe8eb2931 100644 --- a/x-pack/legacy/plugins/uptime/public/queries/pings_query.ts +++ b/x-pack/legacy/plugins/uptime/public/queries/pings_query.ts @@ -15,6 +15,7 @@ query PingList( $sort: String $size: Int $location: String + $page: Int ) { allPings( dateRangeStart: $dateRangeStart @@ -24,6 +25,7 @@ query PingList( sort: $sort size: $size location: $location + page: $page ) { total locations diff --git a/x-pack/package.json b/x-pack/package.json index 35a8714dcbd0c..394d6e85e368e 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -232,7 +232,7 @@ "dedent": "^0.7.0", "del": "^5.1.0", "dragselect": "1.13.1", - "elasticsearch": "^16.5.0", + "elasticsearch": "^16.7.0", "extract-zip": "^1.7.0", "file-saver": "^1.3.8", "file-type": "^10.9.0", diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.test.ts index 2712b8f6ea9b5..aa9b1dcfcf239 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.test.ts @@ -226,15 +226,19 @@ describe('handleUpdateIncident', () => { const res = await handleUpdateIncident({ incidentId: '123', serviceNow, - params, + params: { + ...params, + updatedAt: '2020-03-15T08:34:53.450Z', + updatedBy: { fullName: 'Another User', username: 'anotherUser' }, + }, comments: [], mapping: finalMapping, }); expect(serviceNow.updateIncident).toHaveBeenCalled(); expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - short_description: 'a title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - description: 'a description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + short_description: 'a title (updated at 2020-03-15T08:34:53.450Z by Another User)', + description: 'a description (updated at 2020-03-15T08:34:53.450Z by Another User)', }); expect(serviceNow.updateIncident).toHaveReturned(); expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); @@ -256,7 +260,11 @@ describe('handleUpdateIncident', () => { const res = await handleUpdateIncident({ incidentId: '123', serviceNow, - params, + params: { + ...params, + updatedAt: '2020-03-15T08:34:53.450Z', + updatedBy: { fullName: 'Another User', username: 'anotherUser' }, + }, comments: [ { comment: 'first comment', @@ -278,10 +286,10 @@ describe('handleUpdateIncident', () => { fullName: 'Elastic User', username: 'elastic', }, - updatedAt: '2020-03-13T08:34:53.450Z', + updatedAt: '2020-03-16T08:34:53.450Z', updatedBy: { - fullName: 'Elastic User', - username: 'elastic', + fullName: 'Another User', + username: 'anotherUser', }, version: 'WzU3LDFd', }, @@ -291,8 +299,8 @@ describe('handleUpdateIncident', () => { expect(serviceNow.updateIncident).toHaveBeenCalled(); expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - description: 'a description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - short_description: 'a title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + description: 'a description (updated at 2020-03-15T08:34:53.450Z by Another User)', + short_description: 'a title (updated at 2020-03-15T08:34:53.450Z by Another User)', }); expect(serviceNow.updateIncident).toHaveReturned(); expect(serviceNow.batchCreateComments).toHaveBeenCalled(); @@ -312,17 +320,17 @@ describe('handleUpdateIncident', () => { version: 'WzU3LDFd', }, { - comment: 'second comment (added at 2020-03-13T08:34:53.450Z by Elastic User)', + comment: 'second comment (added at 2020-03-16T08:34:53.450Z by Another User)', commentId: '789', createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic', }, - updatedAt: '2020-03-13T08:34:53.450Z', + updatedAt: '2020-03-16T08:34:53.450Z', updatedBy: { - fullName: 'Elastic User', - username: 'elastic', + fullName: 'Another User', + username: 'anotherUser', }, version: 'WzU3LDFd', }, diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.test.ts index ce8c3542ab69f..cbcefe6364e8f 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.test.ts @@ -191,13 +191,21 @@ describe('transformFields', () => { test('transform fields for update correctly', () => { const fields = prepareFieldsForTransformation({ - params: fullParams, + params: { + ...fullParams, + updatedAt: '2020-03-15T08:34:53.450Z', + updatedBy: { username: 'anotherUser', fullName: 'Another User' }, + }, mapping: finalMapping, defaultPipes: ['informationUpdated'], }); const res = transformFields({ - params: fullParams, + params: { + ...fullParams, + updatedAt: '2020-03-15T08:34:53.450Z', + updatedBy: { username: 'anotherUser', fullName: 'Another User' }, + }, fields, currentIncident: { short_description: 'first title (created at 2020-03-13T08:34:53.450Z by Elastic User)', @@ -205,9 +213,9 @@ describe('transformFields', () => { }, }); expect(res).toEqual({ - short_description: 'a title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + short_description: 'a title (updated at 2020-03-15T08:34:53.450Z by Another User)', description: - 'first description (created at 2020-03-13T08:34:53.450Z by Elastic User) \r\na description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + 'first description (created at 2020-03-13T08:34:53.450Z by Elastic User) \r\na description (updated at 2020-03-15T08:34:53.450Z by Another User)', }); }); @@ -229,7 +237,7 @@ describe('transformFields', () => { expect(res.description?.includes('\r\n')).toBe(true); }); - test('append username if fullname is undefined', () => { + test('append username if fullname is undefined when create', () => { const fields = prepareFieldsForTransformation({ params: fullParams, mapping: finalMapping, @@ -245,6 +253,32 @@ describe('transformFields', () => { description: 'a description (created at 2020-03-13T08:34:53.450Z by elastic)', }); }); + + test('append username if fullname is undefined when update', () => { + const fields = prepareFieldsForTransformation({ + params: { + ...fullParams, + updatedAt: '2020-03-15T08:34:53.450Z', + updatedBy: { username: 'anotherUser', fullName: 'Another User' }, + }, + mapping: finalMapping, + defaultPipes: ['informationUpdated'], + }); + + const res = transformFields({ + params: { + ...fullParams, + updatedAt: '2020-03-15T08:34:53.450Z', + updatedBy: { username: 'anotherUser', fullName: null }, + }, + fields, + }); + + expect(res).toEqual({ + short_description: 'a title (updated at 2020-03-15T08:34:53.450Z by anotherUser)', + description: 'a description (updated at 2020-03-15T08:34:53.450Z by anotherUser)', + }); + }); }); describe('appendField', () => { @@ -330,8 +364,8 @@ describe('transformComments', () => { comment: 'first comment', createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, + updatedAt: '2020-03-15T08:34:53.450Z', + updatedBy: { fullName: 'Another User', username: 'anotherUser' }, }, ]; const res = transformComments(comments, fullParams, ['informationUpdated']); @@ -339,11 +373,11 @@ describe('transformComments', () => { { commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', version: 'WzU3LDFd', - comment: 'first comment (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + comment: 'first comment (updated at 2020-03-15T08:34:53.450Z by Another User)', createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, + updatedAt: '2020-03-15T08:34:53.450Z', + updatedBy: { fullName: 'Another User', username: 'anotherUser' }, }, ]); }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.ts index 46d4789e0bd53..750fda93b60d6 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.ts @@ -83,8 +83,11 @@ export const transformFields = ({ const transform = flow(...cur.pipes.map(p => t[p])); prev[cur.key] = transform({ value: cur.value, - date: params.createdAt, - user: params.createdBy.fullName ?? params.createdBy.username, + date: params.updatedAt ?? params.createdAt, + user: + params.updatedBy != null + ? params.updatedBy.fullName ?? params.updatedBy.username + : params.createdBy.fullName ?? params.createdBy.username, previousValue: currentIncident ? currentIncident[cur.key] : '', }).value; return prev; @@ -112,8 +115,11 @@ export const transformComments = ( ...c, comment: flow(...pipes.map(p => t[p]))({ value: c.comment, - date: params.createdAt, - user: params.createdBy.fullName ?? '', + date: c.updatedAt ?? c.createdAt, + user: + c.updatedBy != null + ? c.updatedBy.fullName ?? c.updatedBy.username + : c.createdBy.fullName ?? c.createdBy.username, }).value, })); }; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts index 7eda7060df846..1a23354e6490d 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts @@ -226,7 +226,11 @@ describe('execute()', () => { const executorOptions: ActionTypeExecutorOptions = { actionId, config: mockOptions.config, - params: { ...mockOptions.params, executorAction: 'updateIncident' }, + params: { + ...mockOptions.params, + updatedAt: '2020-03-15T08:34:53.450Z', + updatedBy: { fullName: 'Another User', username: 'anotherUser' }, + }, secrets: mockOptions.secrets, services, }; @@ -244,7 +248,11 @@ describe('execute()', () => { const executorOptions: ActionTypeExecutorOptions = { actionId, config: mockOptions.config, - params: { ...mockOptions.params, executorAction: 'updateIncident' }, + params: { + ...mockOptions.params, + updatedAt: '2020-03-15T08:34:53.450Z', + updatedBy: { fullName: 'Another User', username: 'anotherUser' }, + }, secrets: mockOptions.secrets, services, }; diff --git a/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts b/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts new file mode 100644 index 0000000000000..1b0fe03633531 --- /dev/null +++ b/x-pack/plugins/actions/server/usage/actions_telemetry.test.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 { getTotalCount } from './actions_telemetry'; + +describe('actions telemetry', () => { + test('getTotalCount should replace action types names with . to __', async () => { + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue({ + aggregations: { + byActionTypeId: { + value: { + types: { '.index': 1, '.server-log': 1 }, + }, + }, + }, + hits: { + hits: [ + { + _id: 'action:541efb3d-f82a-4d2c-a5c3-636d1ce49b53', + _index: '.kibana_1', + _score: 0, + _source: { + action: { + actionTypeId: '.index', + config: { + index: 'kibana_sample_data_ecommerce', + refresh: true, + executionTimeField: null, + }, + name: 'test', + secrets: + 'UPyn6cit6zBTPMmldfKh/8S2JWypwaLhhEQWBXp+OyTc6TtLHOnW92wehCqTq1FhIY3vA8hwVsggj+tbIoCcfPArpzP5SO7hh8vd6pY13x5TkiM083UgjjaAxbPvKQ==', + }, + references: [], + type: 'action', + updated_at: '2020-03-26T18:46:44.449Z', + }, + }, + { + _id: 'action:00000000-f82a-4d2c-a5c3-636d1ce49b53', + _index: '.kibana_1', + _score: 0, + _source: { + action: { + actionTypeId: '.server-log', + config: {}, + name: 'test server log', + secrets: '', + }, + references: [], + type: 'action', + updated_at: '2020-03-26T18:46:44.449Z', + }, + }, + ], + }, + }); + + const telemetry = await getTotalCount(mockEsClient, 'test'); + + expect(mockEsClient).toHaveBeenCalledTimes(1); + + expect(telemetry).toMatchInlineSnapshot(` +Object { + "countByType": Object { + "__index": 1, + "__server-log": 1, + }, + "countTotal": 2, +} +`); + }); +}); diff --git a/x-pack/plugins/actions/server/usage/actions_telemetry.ts b/x-pack/plugins/actions/server/usage/actions_telemetry.ts index ccdb4ecec2012..eabb38e61d17d 100644 --- a/x-pack/plugins/actions/server/usage/actions_telemetry.ts +++ b/x-pack/plugins/actions/server/usage/actions_telemetry.ts @@ -54,7 +54,13 @@ export async function getTotalCount(callCluster: APICaller, kibanaIndex: string) parseInt(searchResult.aggregations.byActionTypeId.value.types[key], 0) + total, 0 ), - countByType: searchResult.aggregations.byActionTypeId.value.types, + countByType: Object.keys(searchResult.aggregations.byActionTypeId.value.types).reduce( + (obj: any, key: string) => ({ + ...obj, + [key.replace('.', '__')]: searchResult.aggregations.byActionTypeId.value.types[key], + }), + {} + ), }; } diff --git a/x-pack/plugins/alerting/server/usage/alerts_telemetry.test.ts b/x-pack/plugins/alerting/server/usage/alerts_telemetry.test.ts new file mode 100644 index 0000000000000..171f80cf11cb8 --- /dev/null +++ b/x-pack/plugins/alerting/server/usage/alerts_telemetry.test.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 { getTotalCountInUse } from './alerts_telemetry'; + +describe('alerts telemetry', () => { + test('getTotalCountInUse should replace action types names with . to __', async () => { + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue({ + aggregations: { + byAlertTypeId: { + value: { + types: { '.index-threshold': 2 }, + }, + }, + }, + hits: { + hits: [], + }, + }); + + const telemetry = await getTotalCountInUse(mockEsClient, 'test'); + + expect(mockEsClient).toHaveBeenCalledTimes(1); + + expect(telemetry).toMatchInlineSnapshot(` +Object { + "countByType": Object { + "__index-threshold": 2, + }, + "countTotal": 2, +} +`); + }); +}); diff --git a/x-pack/plugins/alerting/server/usage/alerts_telemetry.ts b/x-pack/plugins/alerting/server/usage/alerts_telemetry.ts index 9ab63b7755500..9c710fa3b3b8e 100644 --- a/x-pack/plugins/alerting/server/usage/alerts_telemetry.ts +++ b/x-pack/plugins/alerting/server/usage/alerts_telemetry.ts @@ -245,7 +245,13 @@ export async function getTotalCountAggregations(callCluster: APICaller, kibanaIn return { count_total: totalAlertsCount, - count_by_type: results.aggregations.byAlertTypeId.value.types, + count_by_type: Object.keys(results.aggregations.byAlertTypeId.value.types).reduce( + (obj: any, key: string) => ({ + ...obj, + [key.replace('.', '__')]: results.aggregations.byAlertTypeId.value.types[key], + }), + {} + ), throttle_time: { min: `${results.aggregations.throttleTime.value.min}s`, avg: `${ @@ -298,7 +304,13 @@ export async function getTotalCountInUse(callCluster: APICaller, kibanaInex: str parseInt(searchResult.aggregations.byAlertTypeId.value.types[key], 0) + total, 0 ), - countByType: searchResult.aggregations.byAlertTypeId.value.types, + countByType: Object.keys(searchResult.aggregations.byAlertTypeId.value.types).reduce( + (obj: any, key: string) => ({ + ...obj, + [key.replace('.', '__')]: searchResult.aggregations.byAlertTypeId.value.types[key], + }), + {} + ), }; } diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap index 365d8838a24a6..0c585bec22f6c 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap @@ -2,11 +2,6 @@ exports[`settingDefinitions should have correct default values 1`] = ` Array [ - Object { - "key": "active", - "type": "boolean", - "validationName": "(\\"true\\" | \\"false\\")", - }, Object { "key": "api_request_size", "type": "bytes", @@ -105,6 +100,11 @@ Array [ "validationError": "Please specify an integer and a unit", "validationName": "durationRt", }, + Object { + "key": "recording", + "type": "boolean", + "validationName": "(\\"true\\" | \\"false\\")", + }, Object { "key": "server_timeout", "type": "duration", diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts index b83c03c543295..6a0e2d65d1949 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts @@ -13,21 +13,6 @@ import { RawSettingDefinition } from './types'; * Settings added here will show up in the UI and will be validated on the client and server */ export const generalSettings: RawSettingDefinition[] = [ - // Active - { - key: 'active', - type: 'boolean', - defaultValue: 'true', - label: i18n.translate('xpack.apm.agentConfig.active.label', { - defaultMessage: 'Active' - }), - description: i18n.translate('xpack.apm.agentConfig.active.description', { - defaultMessage: - 'A boolean specifying if the agent should be active or not.\nWhen active, the agent instruments incoming HTTP requests, tracks errors and collects and sends metrics.\nWhen inactive, the agent works as a noop, not collecting data and not communicating with the APM Server.\nAs this is a reversible switch, agent threads are not being killed when inactivated, but they will be \nmostly idle in this state, so the overhead should be negligible.\n\nYou can use this setting to dynamically disable Elastic APM at runtime.' - }), - excludeAgents: ['js-base', 'rum-js', 'python', 'dotnet'] - }, - // API Request Size { key: 'api_request_size', @@ -40,7 +25,7 @@ export const generalSettings: RawSettingDefinition[] = [ 'xpack.apm.agentConfig.apiRequestSize.description', { defaultMessage: - 'The maximum total compressed size of the request body which is sent to the APM server intake api via a chunked encoding (HTTP streaming).\nNote that a small overshoot is possible.\n\nAllowed byte units are `b`, `kb` and `mb`. `1kb` is equal to `1024b`.' + 'The maximum total compressed size of the request body which is sent to the APM Server intake api via a chunked encoding (HTTP streaming).\nNote that a small overshoot is possible.\n\nAllowed byte units are `b`, `kb` and `mb`. `1kb` is equal to `1024b`.' } ), excludeAgents: ['js-base', 'rum-js', 'dotnet'] @@ -121,6 +106,20 @@ export const generalSettings: RawSettingDefinition[] = [ excludeAgents: ['js-base', 'rum-js', 'python'] }, + // Recording + { + key: 'recording', + type: 'boolean', + defaultValue: 'true', + label: i18n.translate('xpack.apm.agentConfig.recording.label', { + defaultMessage: 'Recording' + }), + description: i18n.translate('xpack.apm.agentConfig.recording.description', { + defaultMessage: + 'When recording, the agent instruments incoming HTTP requests, tracks errors, and collects and sends metrics. When inactive, the agent works as a noop, not collecting data and not communicating with the APM Server except for polling for updated configuration. As this is a reversible switch, agent threads are not being killed when inactivated, but they will be mostly idle in this state, so the overhead should be negligible. You can use this setting to dynamically control whether Elastic APM is enabled or disabled.' + }) + }, + // SERVER_TIMEOUT { key: 'server_timeout', @@ -133,7 +132,7 @@ export const generalSettings: RawSettingDefinition[] = [ 'xpack.apm.agentConfig.serverTimeout.description', { defaultMessage: - 'If a request to the APM server takes longer than the configured timeout,\nthe request is cancelled and the event (exception or transaction) is discarded.\nSet to 0 to disable timeouts.\n\nWARNING: If timeouts are disabled or set to a high value, your app could experience memory issues if the APM server times out.' + 'If a request to the APM Server takes longer than the configured timeout,\nthe request is cancelled and the event (exception or transaction) is discarded.\nSet to 0 to disable timeouts.\n\nWARNING: If timeouts are disabled or set to a high value, your app could experience memory issues if the APM Server times out.' } ), includeAgents: ['nodejs', 'java', 'go'] diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.test.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.test.ts index fe55442324c92..b0255d2d828bb 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.test.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.test.ts @@ -43,12 +43,12 @@ describe('filterByAgent', () => { describe('options per agent', () => { it('go', () => { expect(getSettingKeysForAgent('go')).toEqual([ - 'active', 'api_request_size', 'api_request_time', 'capture_body', 'capture_headers', 'log_level', + 'recording', 'server_timeout', 'span_frames_min_duration', 'stack_trace_limit', @@ -59,7 +59,6 @@ describe('filterByAgent', () => { it('java', () => { expect(getSettingKeysForAgent('java')).toEqual([ - 'active', 'api_request_size', 'api_request_time', 'capture_body', @@ -72,6 +71,7 @@ describe('filterByAgent', () => { 'profiling_inferred_spans_included_classes', 'profiling_inferred_spans_min_duration', 'profiling_inferred_spans_sampling_interval', + 'recording', 'server_timeout', 'span_frames_min_duration', 'stack_trace_limit', @@ -88,24 +88,26 @@ describe('filterByAgent', () => { it('js-base', () => { expect(getSettingKeysForAgent('js-base')).toEqual([ + 'recording', 'transaction_sample_rate' ]); }); it('rum-js', () => { expect(getSettingKeysForAgent('rum-js')).toEqual([ + 'recording', 'transaction_sample_rate' ]); }); it('nodejs', () => { expect(getSettingKeysForAgent('nodejs')).toEqual([ - 'active', 'api_request_size', 'api_request_time', 'capture_body', 'capture_headers', 'log_level', + 'recording', 'server_timeout', 'stack_trace_limit', 'transaction_max_spans', @@ -119,6 +121,7 @@ describe('filterByAgent', () => { 'api_request_time', 'capture_body', 'capture_headers', + 'recording', 'span_frames_min_duration', 'transaction_max_spans', 'transaction_sample_rate' @@ -130,6 +133,7 @@ describe('filterByAgent', () => { 'capture_body', 'capture_headers', 'log_level', + 'recording', 'span_frames_min_duration', 'stack_trace_limit', 'transaction_max_spans', @@ -139,12 +143,12 @@ describe('filterByAgent', () => { it('ruby', () => { expect(getSettingKeysForAgent('ruby')).toEqual([ - 'active', 'api_request_size', 'api_request_time', 'capture_body', 'capture_headers', 'log_level', + 'recording', 'span_frames_min_duration', 'transaction_max_spans', 'transaction_sample_rate' @@ -155,6 +159,7 @@ describe('filterByAgent', () => { expect(getSettingKeysForAgent(undefined)).toEqual([ 'capture_body', 'capture_headers', + 'recording', 'transaction_max_spans', 'transaction_sample_rate' ]); diff --git a/x-pack/plugins/apm/server/lib/service_map/dedupe_connections/index.test.ts b/x-pack/plugins/apm/server/lib/service_map/dedupe_connections/index.test.ts index 01d6a2e2e81bc..572d73e368c7a 100644 --- a/x-pack/plugins/apm/server/lib/service_map/dedupe_connections/index.test.ts +++ b/x-pack/plugins/apm/server/lib/service_map/dedupe_connections/index.test.ts @@ -140,4 +140,21 @@ describe('dedupeConnections', () => { // @ts-ignore expect(nodejsNode?.data[SPAN_SUBTYPE]).toBe('aa'); }); + + it('processes connections without a matching "service" aggregation', () => { + const response: ServiceMapResponse = { + services: [javaService], + discoveredServices: [], + connections: [ + { + source: javaService, + destination: nodejsService + } + ] + }; + + const { elements } = dedupeConnections(response); + + expect(elements.length).toBe(3); + }); }); diff --git a/x-pack/plugins/apm/server/lib/service_map/dedupe_connections/index.ts b/x-pack/plugins/apm/server/lib/service_map/dedupe_connections/index.ts index 6a433367d8217..e5d7c0b2de10c 100644 --- a/x-pack/plugins/apm/server/lib/service_map/dedupe_connections/index.ts +++ b/x-pack/plugins/apm/server/lib/service_map/dedupe_connections/index.ts @@ -88,7 +88,7 @@ export function dedupeConnections(response: ServiceMapResponse) { serviceName = node[SERVICE_NAME]; } - const matchedServiceNodes = services.filter( + const matchedServiceNodes = serviceNodes.filter( serviceNode => serviceNode[SERVICE_NAME] === serviceName ); diff --git a/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts b/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts index d076008da9d8e..9695c331e0504 100644 --- a/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts +++ b/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts @@ -437,7 +437,7 @@ export const createJsAgentInstructions = (apmServerUrl = '') => [ title: i18n.translate( 'xpack.apm.tutorial.jsClient.enableRealUserMonitoring.title', { - defaultMessage: 'Enable Real User Monitoring support in APM server' + defaultMessage: 'Enable Real User Monitoring support in APM Server' } ), textPre: i18n.translate( diff --git a/x-pack/plugins/case/common/api/cases/case.ts b/x-pack/plugins/case/common/api/cases/case.ts index ee244dd205113..3c5d3405f395e 100644 --- a/x-pack/plugins/case/common/api/cases/case.ts +++ b/x-pack/plugins/case/common/api/cases/case.ts @@ -54,7 +54,11 @@ export const CaseAttributesRt = rt.intersection([ }), ]); -export const CaseRequestRt = CaseBasicRt; +export const CasePostRequestRt = rt.type({ + description: rt.string, + tags: rt.array(rt.string), + title: rt.string, +}); export const CaseExternalServiceRequestRt = CaseExternalServiceBasicRt; @@ -95,7 +99,7 @@ export const CasesFindResponseRt = rt.intersection([ ]); export const CasePatchRequestRt = rt.intersection([ - rt.partial(CaseRequestRt.props), + rt.partial(CaseBasicRt.props), rt.type({ id: rt.string, version: rt.string }), ]); @@ -157,7 +161,7 @@ export const ServiceConnectorCaseResponseRt = rt.intersection([ ]); export type CaseAttributes = rt.TypeOf; -export type CaseRequest = rt.TypeOf; +export type CasePostRequest = rt.TypeOf; export type CaseResponse = rt.TypeOf; export type CasesResponse = rt.TypeOf; export type CasesFindResponse = rt.TypeOf; diff --git a/x-pack/plugins/case/common/api/runtime_types.ts b/x-pack/plugins/case/common/api/runtime_types.ts index d5b858df38def..4717d7c3c48c4 100644 --- a/x-pack/plugins/case/common/api/runtime_types.ts +++ b/x-pack/plugins/case/common/api/runtime_types.ts @@ -4,22 +4,55 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; +import { either, fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; -import { Errors, Type } from 'io-ts'; +import * as rt from 'io-ts'; import { failure } from 'io-ts/lib/PathReporter'; type ErrorFactory = (message: string) => Error; export const createPlainError = (message: string) => new Error(message); -export const throwErrors = (createError: ErrorFactory) => (errors: Errors) => { +export const throwErrors = (createError: ErrorFactory) => (errors: rt.Errors) => { throw createError(failure(errors).join('\n')); }; export const decodeOrThrow = ( - runtimeType: Type, + runtimeType: rt.Type, createError: ErrorFactory = createPlainError ) => (inputValue: I) => pipe(runtimeType.decode(inputValue), fold(throwErrors(createError), identity)); + +const getExcessProps = (props: rt.Props, r: Record): string[] => { + const ex: string[] = []; + for (const k of Object.keys(r)) { + if (!props.hasOwnProperty(k)) { + ex.push(k); + } + } + return ex; +}; + +export function excess>(codec: C): C { + const r = new rt.InterfaceType( + codec.name, + codec.is, + (i, c) => + either.chain(rt.UnknownRecord.validate(i, c), (s: Record) => { + const ex = getExcessProps(codec.props, s); + return ex.length > 0 + ? rt.failure( + i, + c, + `Invalid value ${JSON.stringify(i)} supplied to : ${ + codec.name + }, excess properties: ${JSON.stringify(ex)}` + ) + : codec.validate(i, c); + }), + codec.encode, + codec.props + ); + return r as any; +} diff --git a/x-pack/plugins/case/common/api/user.ts b/x-pack/plugins/case/common/api/user.ts index 651cd08f08a02..af198470737cf 100644 --- a/x-pack/plugins/case/common/api/user.ts +++ b/x-pack/plugins/case/common/api/user.ts @@ -7,9 +7,9 @@ import * as rt from 'io-ts'; export const UserRT = rt.type({ - email: rt.union([rt.undefined, rt.string]), - full_name: rt.union([rt.undefined, rt.string]), - username: rt.string, + email: rt.union([rt.undefined, rt.null, rt.string]), + full_name: rt.union([rt.undefined, rt.null, rt.string]), + username: rt.union([rt.undefined, rt.null, rt.string]), }); export const UsersRt = rt.array(UserRT); diff --git a/x-pack/plugins/case/kibana.json b/x-pack/plugins/case/kibana.json index f565dc1b6924e..55416ee28c7df 100644 --- a/x-pack/plugins/case/kibana.json +++ b/x-pack/plugins/case/kibana.json @@ -2,7 +2,7 @@ "configPath": ["xpack", "case"], "id": "case", "kibanaVersion": "kibana", - "requiredPlugins": ["security", "actions"], + "requiredPlugins": ["actions"], "optionalPlugins": [ "spaces", "security" diff --git a/x-pack/plugins/case/server/plugin.ts b/x-pack/plugins/case/server/plugin.ts index a6a459373b0ed..670e6ec797a9f 100644 --- a/x-pack/plugins/case/server/plugin.ts +++ b/x-pack/plugins/case/server/plugin.ts @@ -60,7 +60,7 @@ export class CasePlugin { ); const caseService = await caseServicePlugin.setup({ - authentication: plugins.security.authc, + authentication: plugins.security != null ? plugins.security.authc : null, }); const caseConfigureService = await caseConfigureServicePlugin.setup(); const userActionService = await userActionServicePlugin.setup(); diff --git a/x-pack/plugins/case/server/routes/api/__fixtures__/create_mock_so_repository.ts b/x-pack/plugins/case/server/routes/api/__fixtures__/create_mock_so_repository.ts index 5051f78a47cce..95cd66a9c51a2 100644 --- a/x-pack/plugins/case/server/routes/api/__fixtures__/create_mock_so_repository.ts +++ b/x-pack/plugins/case/server/routes/api/__fixtures__/create_mock_so_repository.ts @@ -29,7 +29,7 @@ export const createMockSavedObjectsRepository = ({ if (!result.length) { throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); } - return result[0]; + return result; } const result = caseSavedObject.filter(s => s.id === id); if (!result.length) { @@ -100,8 +100,9 @@ export const createMockSavedObjectsRepository = ({ if (attributes.description === 'Throw an error' || attributes.comment === 'Throw an error') { throw SavedObjectsErrorHelpers.createBadRequestError('Error thrown for testing'); } + if (type === CASE_COMMENT_SAVED_OBJECT) { - return { + const newCommentObj = { type, id: 'mock-comment', attributes, @@ -109,6 +110,8 @@ export const createMockSavedObjectsRepository = ({ updated_at: '2019-12-02T22:48:08.327Z', version: 'WzksMV0=', }; + caseCommentSavedObject = [...caseCommentSavedObject, newCommentObj]; + return newCommentObj; } return { type, @@ -124,6 +127,16 @@ export const createMockSavedObjectsRepository = ({ if (!caseCommentSavedObject.find(s => s.id === id)) { throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); } + caseCommentSavedObject = [ + ...caseCommentSavedObject, + { + id, + type, + updated_at: '2019-11-22T22:50:55.191Z', + version: 'WzE3LDFd', + attributes, + }, + ]; } else if (type === CASE_SAVED_OBJECT) { if (!caseSavedObject.find(s => s.id === id)) { throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/delete_all_comments.ts b/x-pack/plugins/case/server/routes/api/cases/comments/delete_all_comments.ts index 941ac90f2e90e..1dfab165eccd7 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/delete_all_comments.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/delete_all_comments.ts @@ -53,7 +53,7 @@ export function initDeleteAllCommentsApi({ caseService, router, userActionServic ), }); - return response.ok({ body: 'true' }); + return response.noContent(); } catch (error) { return response.customError(wrapError(error)); } diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.test.ts b/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.test.ts index 8f05fbce391f8..b2022e6dec26d 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.test.ts @@ -21,7 +21,7 @@ describe('DELETE comment', () => { beforeAll(async () => { routeHandler = await createRoute(initDeleteCommentApi, 'delete'); }); - it(`deletes the comment. responds with 200`, async () => { + it(`deletes the comment. responds with 204`, async () => { const request = httpServerMock.createKibanaRequest({ path: '/api/cases/{case_id}/comments/{comment_id}', method: 'delete', @@ -39,7 +39,7 @@ describe('DELETE comment', () => { ); const response = await routeHandler(theContext, request, kibanaResponseFactory); - expect(response.status).toEqual(200); + expect(response.status).toEqual(204); }); it(`returns an error when thrown from deleteComment service`, async () => { const request = httpServerMock.createKibanaRequest({ diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.ts b/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.ts index 44e57fc809e04..ff0729afed96a 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.ts @@ -64,7 +64,7 @@ export function initDeleteCommentApi({ caseService, router, userActionService }: ], }); - return response.ok({ body: 'true' }); + return response.noContent(); } catch (error) { return response.customError(wrapError(error)); } diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.test.ts b/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.test.ts index 4e7e266f326a2..8d9906c2abe7f 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.test.ts @@ -43,7 +43,9 @@ describe('PATCH comment', () => { const response = await routeHandler(theContext, request, kibanaResponseFactory); expect(response.status).toEqual(200); - expect(response.payload.comment).toEqual('Update my comment'); + expect(response.payload.comments[response.payload.comments.length - 1].comment).toEqual( + 'Update my comment' + ); }); it(`Fails with 409 if version does not match`, async () => { diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.ts b/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.ts index c67ad1bdaea71..3b38afc02ed81 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.ts @@ -10,11 +10,11 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; -import { CommentPatchRequestRt, CommentResponseRt, throwErrors } from '../../../../../common/api'; +import { CommentPatchRequestRt, CaseResponseRt, throwErrors } from '../../../../../common/api'; import { CASE_SAVED_OBJECT } from '../../../../saved_object_types'; import { buildCommentUserActionItem } from '../../../../services/user_actions/helpers'; import { RouteDeps } from '../../types'; -import { escapeHatch, wrapError, flattenCommentSavedObject } from '../../utils'; +import { escapeHatch, wrapError, flattenCaseSavedObject } from '../../utils'; export function initPatchCommentApi({ caseService, router, userActionService }: RouteDeps) { router.patch( @@ -30,11 +30,17 @@ export function initPatchCommentApi({ caseService, router, userActionService }: async (context, request, response) => { try { const client = context.core.savedObjects.client; + const caseId = request.params.case_id; const query = pipe( CommentPatchRequestRt.decode(request.body), fold(throwErrors(Boom.badRequest), identity) ); + const myCase = await caseService.getCase({ + client, + caseId, + }); + const myComment = await caseService.getComment({ client, commentId: query.id, @@ -45,10 +51,8 @@ export function initPatchCommentApi({ caseService, router, userActionService }: } const caseRef = myComment.references.find(c => c.type === CASE_SAVED_OBJECT); - if (caseRef == null || (caseRef != null && caseRef.id !== request.params.case_id)) { - throw Boom.notFound( - `This comment ${query.id} does not exist in ${request.params.case_id}).` - ); + if (caseRef == null || (caseRef != null && caseRef.id !== caseId)) { + throw Boom.notFound(`This comment ${query.id} does not exist in ${caseId}).`); } if (query.version !== myComment.version) { @@ -59,40 +63,77 @@ export function initPatchCommentApi({ caseService, router, userActionService }: const { username, full_name, email } = await caseService.getUser({ request, response }); const updatedDate = new Date().toISOString(); - const updatedComment = await caseService.patchComment({ + const [updatedComment, updatedCase] = await Promise.all([ + caseService.patchComment({ + client, + commentId: query.id, + updatedAttributes: { + comment: query.comment, + updated_at: updatedDate, + updated_by: { email, full_name, username }, + }, + version: query.version, + }), + caseService.patchCase({ + client, + caseId, + updatedAttributes: { + updated_at: updatedDate, + updated_by: { username, full_name, email }, + }, + version: myCase.version, + }), + ]); + + const totalCommentsFindByCases = await caseService.getAllCaseComments({ client, - commentId: query.id, - updatedAttributes: { - comment: query.comment, - updated_at: updatedDate, - updated_by: { email, full_name, username }, + caseId, + options: { + fields: [], + page: 1, + perPage: 1, }, - version: query.version, }); - await userActionService.postUserActions({ - client, - actions: [ - buildCommentUserActionItem({ - action: 'update', - actionAt: updatedDate, - actionBy: { username, full_name, email }, - caseId: request.params.case_id, - commentId: updatedComment.id, - fields: ['comment'], - newValue: query.comment, - oldValue: myComment.attributes.comment, - }), - ], - }); + const [comments] = await Promise.all([ + caseService.getAllCaseComments({ + client, + caseId: request.params.case_id, + options: { + fields: [], + page: 1, + perPage: totalCommentsFindByCases.total, + }, + }), + userActionService.postUserActions({ + client, + actions: [ + buildCommentUserActionItem({ + action: 'update', + actionAt: updatedDate, + actionBy: { username, full_name, email }, + caseId: request.params.case_id, + commentId: updatedComment.id, + fields: ['comment'], + newValue: query.comment, + oldValue: myComment.attributes.comment, + }), + ], + }), + ]); return response.ok({ - body: CommentResponseRt.encode( - flattenCommentSavedObject({ - ...updatedComment, - attributes: { ...myComment.attributes, ...updatedComment.attributes }, - references: myComment.references, - }) + body: CaseResponseRt.encode( + flattenCaseSavedObject( + { + ...myCase, + ...updatedCase, + attributes: { ...myCase.attributes, ...updatedCase.attributes }, + version: updatedCase.version ?? myCase.version, + references: myCase.references, + }, + comments.saved_objects + ) ), }); } catch (error) { diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.test.ts b/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.test.ts index e51ec7c894d08..23039da681ec6 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.test.ts @@ -20,6 +20,10 @@ describe('POST comment', () => { let routeHandler: RequestHandler; beforeAll(async () => { routeHandler = await createRoute(initPostCommentApi, 'post'); + const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; + spyOnDate.mockImplementation(() => ({ + toISOString: jest.fn().mockReturnValue('2019-11-25T21:54:48.952Z'), + })); }); it(`Posts a new comment`, async () => { const request = httpServerMock.createKibanaRequest({ @@ -42,7 +46,9 @@ describe('POST comment', () => { const response = await routeHandler(theContext, request, kibanaResponseFactory); expect(response.status).toEqual(200); - expect(response.payload.id).toEqual('mock-comment'); + expect(response.payload.comments[response.payload.comments.length - 1].id).toEqual( + 'mock-comment' + ); }); it(`Returns an error if the case does not exist`, async () => { const request = httpServerMock.createKibanaRequest({ @@ -90,7 +96,7 @@ describe('POST comment', () => { expect(response.status).toEqual(400); expect(response.payload.isBoom).toEqual(true); }); - it(`Returns an error if user authentication throws`, async () => { + it(`Allow user to create comments without authentications`, async () => { routeHandler = await createRoute(initPostCommentApi, 'post', true); const request = httpServerMock.createKibanaRequest({ @@ -112,7 +118,21 @@ describe('POST comment', () => { ); const response = await routeHandler(theContext, request, kibanaResponseFactory); - expect(response.status).toEqual(500); - expect(response.payload.isBoom).toEqual(true); + expect(response.status).toEqual(200); + expect(response.payload.comments[response.payload.comments.length - 1]).toEqual({ + comment: 'Wow, good luck catching that bad meanie!', + created_at: '2019-11-25T21:54:48.952Z', + created_by: { + email: null, + full_name: null, + username: null, + }, + id: 'mock-comment', + pushed_at: null, + pushed_by: null, + updated_at: null, + updated_by: null, + version: 'WzksMV0=', + }); }); }); diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.ts b/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.ts index 2410505872a3a..70405af26f576 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.ts @@ -10,15 +10,10 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; -import { CommentRequestRt, CommentResponseRt, throwErrors } from '../../../../../common/api'; +import { CaseResponseRt, CommentRequestRt, excess, throwErrors } from '../../../../../common/api'; import { CASE_SAVED_OBJECT } from '../../../../saved_object_types'; import { buildCommentUserActionItem } from '../../../../services/user_actions/helpers'; -import { - escapeHatch, - transformNewComment, - wrapError, - flattenCommentSavedObject, -} from '../../utils'; +import { escapeHatch, transformNewComment, wrapError, flattenCaseSavedObject } from '../../utils'; import { RouteDeps } from '../../types'; export function initPostCommentApi({ caseService, router, userActionService }: RouteDeps) { @@ -35,54 +30,98 @@ export function initPostCommentApi({ caseService, router, userActionService }: R async (context, request, response) => { try { const client = context.core.savedObjects.client; + const caseId = request.params.case_id; const query = pipe( - CommentRequestRt.decode(request.body), + excess(CommentRequestRt).decode(request.body), fold(throwErrors(Boom.badRequest), identity) ); const myCase = await caseService.getCase({ client, - caseId: request.params.case_id, + caseId, }); const { username, full_name, email } = await caseService.getUser({ request, response }); const createdDate = new Date().toISOString(); - const newComment = await caseService.postNewComment({ - client, - attributes: transformNewComment({ - createdDate, - ...query, - username, - full_name, - email, + const [newComment, updatedCase] = await Promise.all([ + caseService.postNewComment({ + client, + attributes: transformNewComment({ + createdDate, + ...query, + username, + full_name, + email, + }), + references: [ + { + type: CASE_SAVED_OBJECT, + name: `associated-${CASE_SAVED_OBJECT}`, + id: myCase.id, + }, + ], }), - references: [ - { - type: CASE_SAVED_OBJECT, - name: `associated-${CASE_SAVED_OBJECT}`, - id: myCase.id, + caseService.patchCase({ + client, + caseId, + updatedAttributes: { + updated_at: createdDate, + updated_by: { username, full_name, email }, }, - ], - }); + version: myCase.version, + }), + ]); - await userActionService.postUserActions({ + const totalCommentsFindByCases = await caseService.getAllCaseComments({ client, - actions: [ - buildCommentUserActionItem({ - action: 'create', - actionAt: createdDate, - actionBy: { username, full_name, email }, - caseId: myCase.id, - commentId: newComment.id, - fields: ['comment'], - newValue: query.comment, - }), - ], + caseId, + options: { + fields: [], + page: 1, + perPage: 1, + }, }); + const [comments] = await Promise.all([ + caseService.getAllCaseComments({ + client, + caseId, + options: { + fields: [], + page: 1, + perPage: totalCommentsFindByCases.total, + }, + }), + userActionService.postUserActions({ + client, + actions: [ + buildCommentUserActionItem({ + action: 'create', + actionAt: createdDate, + actionBy: { username, full_name, email }, + caseId: myCase.id, + commentId: newComment.id, + fields: ['comment'], + newValue: query.comment, + }), + ], + }), + ]); + return response.ok({ - body: CommentResponseRt.encode(flattenCommentSavedObject(newComment)), + body: CaseResponseRt.encode( + flattenCaseSavedObject( + { + ...myCase, + ...updatedCase, + attributes: { ...myCase.attributes, ...updatedCase.attributes }, + version: updatedCase.version ?? myCase.version, + references: myCase.references, + }, + comments.saved_objects + ) + ), }); } catch (error) { return response.customError(wrapError(error)); diff --git a/x-pack/plugins/case/server/routes/api/cases/delete_cases.test.ts b/x-pack/plugins/case/server/routes/api/cases/delete_cases.test.ts index cee705694f21d..c5be6f78a1570 100644 --- a/x-pack/plugins/case/server/routes/api/cases/delete_cases.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/delete_cases.test.ts @@ -22,7 +22,7 @@ describe('DELETE case', () => { beforeAll(async () => { routeHandler = await createRoute(initDeleteCasesApi, 'delete'); }); - it(`deletes the case. responds with 200`, async () => { + it(`deletes the case. responds with 204`, async () => { const request = httpServerMock.createKibanaRequest({ path: '/api/cases', method: 'delete', @@ -39,7 +39,7 @@ describe('DELETE case', () => { ); const response = await routeHandler(theContext, request, kibanaResponseFactory); - expect(response.status).toEqual(200); + expect(response.status).toEqual(204); }); it(`returns an error when thrown from deleteCase service`, async () => { const request = httpServerMock.createKibanaRequest({ diff --git a/x-pack/plugins/case/server/routes/api/cases/delete_cases.ts b/x-pack/plugins/case/server/routes/api/cases/delete_cases.ts index 8b0384c12edce..0214017ae5c29 100644 --- a/x-pack/plugins/case/server/routes/api/cases/delete_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/delete_cases.ts @@ -70,7 +70,7 @@ export function initDeleteCasesApi({ caseService, router, userActionService }: R ), }); - return response.ok({ body: 'true' }); + return response.noContent(); } catch (error) { return response.customError(wrapError(error)); } diff --git a/x-pack/plugins/case/server/routes/api/cases/helpers.ts b/x-pack/plugins/case/server/routes/api/cases/helpers.ts index 747b5195da7ec..46c2209d79f7d 100644 --- a/x-pack/plugins/case/server/routes/api/cases/helpers.ts +++ b/x-pack/plugins/case/server/routes/api/cases/helpers.ts @@ -62,11 +62,14 @@ export const getCaseToUpdate = ( Object.entries(queryCase).reduce( (acc, [key, value]) => { const currentValue = get(currentCase, key); - if (isTwoArraysDifference(value, currentValue)) { - return { - ...acc, - [key]: value, - }; + if (Array.isArray(currentValue) && Array.isArray(value)) { + if (isTwoArraysDifference(value, currentValue)) { + return { + ...acc, + [key]: value, + }; + } + return acc; } else if (currentValue != null && value !== currentValue) { return { ...acc, diff --git a/x-pack/plugins/case/server/routes/api/cases/patch_cases.ts b/x-pack/plugins/case/server/routes/api/cases/patch_cases.ts index 3d0b7bc79f88b..c36ea8964dc80 100644 --- a/x-pack/plugins/case/server/routes/api/cases/patch_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/patch_cases.ts @@ -11,9 +11,10 @@ import { identity } from 'fp-ts/lib/function'; import { CasesPatchRequestRt, - throwErrors, CasesResponseRt, CasePatchRequest, + excess, + throwErrors, } from '../../../../common/api'; import { escapeHatch, wrapError, flattenCaseSavedObject } from '../utils'; import { RouteDeps } from '../types'; @@ -32,7 +33,7 @@ export function initPatchCasesApi({ caseService, router, userActionService }: Ro try { const client = context.core.savedObjects.client; const query = pipe( - CasesPatchRequestRt.decode(request.body), + excess(CasesPatchRequestRt).decode(request.body), fold(throwErrors(Boom.badRequest), identity) ); const myCases = await caseService.getCases({ @@ -116,6 +117,7 @@ export function initPatchCasesApi({ caseService, router, userActionService }: Ro ...updatedCase, attributes: { ...myCase.attributes, ...updatedCase?.attributes }, references: myCase.references, + version: updatedCase?.version ?? myCase.version, }); }); diff --git a/x-pack/plugins/case/server/routes/api/cases/post_case.test.ts b/x-pack/plugins/case/server/routes/api/cases/post_case.test.ts index 5b716e5a2d490..5899102224774 100644 --- a/x-pack/plugins/case/server/routes/api/cases/post_case.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/post_case.test.ts @@ -19,6 +19,10 @@ describe('POST cases', () => { let routeHandler: RequestHandler; beforeAll(async () => { routeHandler = await createRoute(initPostCaseApi, 'post'); + const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; + spyOnDate.mockImplementation(() => ({ + toISOString: jest.fn().mockReturnValue('2019-11-25T21:54:48.952Z'), + })); }); it(`Posts a new case`, async () => { const request = httpServerMock.createKibanaRequest({ @@ -27,7 +31,6 @@ describe('POST cases', () => { body: { description: 'This is a brand new case of a bad meanie defacing data', title: 'Super Bad Security Issue', - status: 'open', tags: ['defacement'], }, }); @@ -43,6 +46,28 @@ describe('POST cases', () => { expect(response.payload.id).toEqual('mock-it'); expect(response.payload.created_by.username).toEqual('awesome'); }); + + it(`Error if you passing status for a new case`, async () => { + const request = httpServerMock.createKibanaRequest({ + path: '/api/cases', + method: 'post', + body: { + description: 'This is a brand new case of a bad meanie defacing data', + title: 'Super Bad Security Issue', + status: 'open', + tags: ['defacement'], + }, + }); + + const theContext = createRouteContext( + createMockSavedObjectsRepository({ + caseSavedObject: mockCases, + }) + ); + + const response = await routeHandler(theContext, request, kibanaResponseFactory); + expect(response.status).toEqual(400); + }); it(`Returns an error if postNewCase throws`, async () => { const request = httpServerMock.createKibanaRequest({ path: '/api/cases', @@ -50,7 +75,6 @@ describe('POST cases', () => { body: { description: 'Throw an error', title: 'Super Bad Security Issue', - status: 'open', tags: ['error'], }, }); @@ -65,7 +89,7 @@ describe('POST cases', () => { expect(response.status).toEqual(400); expect(response.payload.isBoom).toEqual(true); }); - it(`Returns an error if user authentication throws`, async () => { + it(`Allow user to create case without authentication`, async () => { routeHandler = await createRoute(initPostCaseApi, 'post', true); const request = httpServerMock.createKibanaRequest({ @@ -74,7 +98,6 @@ describe('POST cases', () => { body: { description: 'This is a brand new case of a bad meanie defacing data', title: 'Super Bad Security Issue', - status: 'open', tags: ['defacement'], }, }); @@ -86,7 +109,27 @@ describe('POST cases', () => { ); const response = await routeHandler(theContext, request, kibanaResponseFactory); - expect(response.status).toEqual(500); - expect(response.payload.isBoom).toEqual(true); + expect(response.status).toEqual(200); + expect(response.payload).toEqual({ + closed_at: null, + closed_by: null, + comments: [], + created_at: '2019-11-25T21:54:48.952Z', + created_by: { + email: null, + full_name: null, + username: null, + }, + description: 'This is a brand new case of a bad meanie defacing data', + external_service: null, + id: 'mock-it', + status: 'open', + tags: ['defacement'], + title: 'Super Bad Security Issue', + totalComment: 0, + updated_at: null, + updated_by: null, + version: 'WzksMV0=', + }); }); }); diff --git a/x-pack/plugins/case/server/routes/api/cases/post_case.ts b/x-pack/plugins/case/server/routes/api/cases/post_case.ts index 75be68013bcd4..239b8bfdf9b29 100644 --- a/x-pack/plugins/case/server/routes/api/cases/post_case.ts +++ b/x-pack/plugins/case/server/routes/api/cases/post_case.ts @@ -11,7 +11,7 @@ import { identity } from 'fp-ts/lib/function'; import { flattenCaseSavedObject, transformNewCase, wrapError, escapeHatch } from '../utils'; -import { CaseRequestRt, throwErrors, CaseResponseRt } from '../../../../common/api'; +import { CasePostRequestRt, throwErrors, excess, CaseResponseRt } from '../../../../common/api'; import { buildCaseUserActionItem } from '../../../services/user_actions/helpers'; import { RouteDeps } from '../types'; @@ -27,7 +27,7 @@ export function initPostCaseApi({ caseService, router, userActionService }: Rout try { const client = context.core.savedObjects.client; const query = pipe( - CaseRequestRt.decode(request.body), + excess(CasePostRequestRt).decode(request.body), fold(throwErrors(Boom.badRequest), identity) ); diff --git a/x-pack/plugins/case/server/routes/api/cases/push_case.ts b/x-pack/plugins/case/server/routes/api/cases/push_case.ts index 6ae3df180d9e4..aff057adea37f 100644 --- a/x-pack/plugins/case/server/routes/api/cases/push_case.ts +++ b/x-pack/plugins/case/server/routes/api/cases/push_case.ts @@ -102,16 +102,16 @@ export function initPushCaseUserActionApi({ }), caseService.patchComments({ client, - comments: comments.saved_objects.map(comment => ({ - commentId: comment.id, - updatedAttributes: { - pushed_at: pushedDate, - pushed_by: { username, full_name, email }, - updated_at: pushedDate, - updated_by: { username, full_name, email }, - }, - version: comment.version, - })), + comments: comments.saved_objects + .filter(comment => comment.attributes.pushed_at == null) + .map(comment => ({ + commentId: comment.id, + updatedAttributes: { + pushed_at: pushedDate, + pushed_by: { username, full_name, email }, + }, + version: comment.version, + })), }), userActionService.postUserActions({ client, @@ -140,7 +140,6 @@ export function initPushCaseUserActionApi({ ], }), ]); - return response.ok({ body: CaseResponseRt.encode( flattenCaseSavedObject( diff --git a/x-pack/plugins/case/server/routes/api/utils.ts b/x-pack/plugins/case/server/routes/api/utils.ts index 9d90eb8ef4a6d..a3df0fc93d2ac 100644 --- a/x-pack/plugins/case/server/routes/api/utils.ts +++ b/x-pack/plugins/case/server/routes/api/utils.ts @@ -14,7 +14,7 @@ import { } from 'kibana/server'; import { - CaseRequest, + CasePostRequest, CaseResponse, CasesFindResponse, CaseAttributes, @@ -33,27 +33,28 @@ export const transformNewCase = ({ username, }: { createdDate: string; - email?: string; - full_name?: string; - newCase: CaseRequest; - username: string; + email?: string | null; + full_name?: string | null; + newCase: CasePostRequest; + username?: string | null; }): CaseAttributes => ({ + ...newCase, closed_at: null, closed_by: null, created_at: createdDate, created_by: { email, full_name, username }, external_service: null, + status: 'open', updated_at: null, updated_by: null, - ...newCase, }); interface NewCommentArgs { comment: string; createdDate: string; - email?: string; - full_name?: string; - username: string; + email?: string | null; + full_name?: string | null; + username?: string | null; } export const transformNewComment = ({ comment, diff --git a/x-pack/plugins/case/server/saved_object_types/comments.ts b/x-pack/plugins/case/server/saved_object_types/comments.ts index 8776dd39b11fa..73b1852bafe58 100644 --- a/x-pack/plugins/case/server/saved_object_types/comments.ts +++ b/x-pack/plugins/case/server/saved_object_types/comments.ts @@ -44,6 +44,9 @@ export const caseCommentSavedObjectType: SavedObjectsType = { full_name: { type: 'keyword', }, + email: { + type: 'keyword', + }, }, }, updated_at: { diff --git a/x-pack/plugins/case/server/scripts/mock/case/post_case.json b/x-pack/plugins/case/server/scripts/mock/case/post_case.json index 25a9780596828..743fa396295ca 100644 --- a/x-pack/plugins/case/server/scripts/mock/case/post_case.json +++ b/x-pack/plugins/case/server/scripts/mock/case/post_case.json @@ -1,7 +1,6 @@ { "description": "This looks not so good", "title": "Bad meanie defacing data", - "status": "open", "tags": [ "defacement" ] diff --git a/x-pack/plugins/case/server/scripts/mock/case/post_case_v2.json b/x-pack/plugins/case/server/scripts/mock/case/post_case_v2.json index cf066d2c8a1e8..13efe436a640d 100644 --- a/x-pack/plugins/case/server/scripts/mock/case/post_case_v2.json +++ b/x-pack/plugins/case/server/scripts/mock/case/post_case_v2.json @@ -1,7 +1,6 @@ { "description": "I hope there are some good security engineers at this company...", "title": "Another bad dude", - "status": "open", "tags": [ "phishing" ] diff --git a/x-pack/plugins/case/server/services/index.ts b/x-pack/plugins/case/server/services/index.ts index 09d726228d309..cdc5fd21a8138 100644 --- a/x-pack/plugins/case/server/services/index.ts +++ b/x-pack/plugins/case/server/services/index.ts @@ -75,13 +75,13 @@ interface PatchCasesArgs extends ClientArgs { } interface UpdateCommentArgs extends ClientArgs { commentId: string; - updatedAttributes: Partial; + updatedAttributes: Partial; version?: string; } interface PatchComment { commentId: string; - updatedAttributes: Partial; + updatedAttributes: Partial; version?: string; } @@ -95,7 +95,7 @@ interface GetUserArgs { } interface CaseServiceDeps { - authentication: SecurityPluginSetup['authc']; + authentication: SecurityPluginSetup['authc'] | null; } export interface CaseServiceSetup { deleteCase(args: GetCaseArgs): Promise<{}>; @@ -107,7 +107,7 @@ export interface CaseServiceSetup { getComment(args: GetCommentArgs): Promise>; getTags(args: ClientArgs): Promise; getReporters(args: ClientArgs): Promise; - getUser(args: GetUserArgs): Promise; + getUser(args: GetUserArgs): Promise; postNewCase(args: PostCaseArgs): Promise>; postNewComment(args: PostCommentArgs): Promise>; patchCase(args: PatchCaseArgs): Promise>; @@ -207,13 +207,28 @@ export class CaseService { } }, getUser: async ({ request, response }: GetUserArgs) => { - this.log.debug(`Attempting to authenticate a user`); - const user = authentication!.getCurrentUser(request); - if (!user) { - this.log.debug(`Error on GET user: Bad User`); - throw new Error('Bad User - the user is not authenticated'); + try { + this.log.debug(`Attempting to authenticate a user`); + if (authentication != null) { + const user = authentication.getCurrentUser(request); + if (!user) { + return { + username: null, + full_name: null, + email: null, + }; + } + return user; + } + return { + username: null, + full_name: null, + email: null, + }; + } catch (error) { + this.log.debug(`Error on GET cases: ${error}`); + throw error; } - return user; }, postNewCase: async ({ client, attributes }: PostCaseArgs) => { try { diff --git a/x-pack/plugins/case/server/services/user_actions/helpers.ts b/x-pack/plugins/case/server/services/user_actions/helpers.ts index 59d193f0f30d5..e89700419b19d 100644 --- a/x-pack/plugins/case/server/services/user_actions/helpers.ts +++ b/x-pack/plugins/case/server/services/user_actions/helpers.ts @@ -31,11 +31,11 @@ export const transformNewUserAction = ({ actionField: UserActionField; action: UserAction; actionAt: string; - email?: string; - full_name?: string; + email?: string | null; + full_name?: string | null; newValue?: string | null; oldValue?: string | null; - username: string; + username?: string | null; }): CaseUserActionAttributes => ({ action_field: actionField, action, diff --git a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts index c493e8ce86781..70bdcdfd3cf1f 100644 --- a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts @@ -33,7 +33,7 @@ export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider { + const mockCoreSetup = coreMock.createSetup(); + const mockApiCaller = jest.fn(); + const mockSearch = jest.fn(); + const mockConfig$ = pluginInitializerContextConfigMock({}).legacy.globalConfig$; + + beforeEach(() => { + mockApiCaller.mockClear(); + mockSearch.mockClear(); + }); + + it('returns a strategy with `search`', () => { + const esSearch = enhancedEsSearchStrategyProvider( + { + core: mockCoreSetup, + config$: mockConfig$, + }, + mockApiCaller, + mockSearch + ); + + expect(typeof esSearch.search).toBe('function'); + }); + + it('makes a POST request to async search with params when no ID is provided', async () => { + mockApiCaller.mockResolvedValueOnce(mockAsyncResponse); + + const params = { index: 'logstash-*', body: { query: {} } }; + const esSearch = enhancedEsSearchStrategyProvider( + { + core: mockCoreSetup, + config$: mockConfig$, + }, + mockApiCaller, + mockSearch + ); + + await esSearch.search({ params }); + + expect(mockApiCaller).toBeCalled(); + expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); + const { method, path, body } = mockApiCaller.mock.calls[0][1]; + expect(method).toBe('POST'); + expect(path).toBe('logstash-*/_async_search'); + expect(body).toEqual({ query: {} }); + }); + + it('makes a GET request to async search with ID when ID is provided', async () => { + mockApiCaller.mockResolvedValueOnce(mockAsyncResponse); + + const params = { index: 'logstash-*', body: { query: {} } }; + const esSearch = enhancedEsSearchStrategyProvider( + { + core: mockCoreSetup, + config$: mockConfig$, + }, + mockApiCaller, + mockSearch + ); + + await esSearch.search({ id: 'foo', params }); + + expect(mockApiCaller).toBeCalled(); + expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); + const { method, path, body } = mockApiCaller.mock.calls[0][1]; + expect(method).toBe('GET'); + expect(path).toBe('_async_search/foo'); + expect(body).toEqual(undefined); + }); + + it('encodes special characters in the path', async () => { + mockApiCaller.mockResolvedValueOnce(mockAsyncResponse); + + const params = { index: 'foo-程', body: {} }; + const esSearch = enhancedEsSearchStrategyProvider( + { + core: mockCoreSetup, + config$: mockConfig$, + }, + mockApiCaller, + mockSearch + ); + + await esSearch.search({ params }); + + expect(mockApiCaller).toBeCalled(); + expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); + const { method, path } = mockApiCaller.mock.calls[0][1]; + expect(method).toBe('POST'); + expect(path).toBe('foo-%E7%A8%8B/_async_search'); + }); + + it('calls the rollup API if the index is a rollup type', async () => { + mockApiCaller.mockResolvedValueOnce(mockRollupResponse); + + const params = { index: 'foo-程', body: {} }; + const esSearch = enhancedEsSearchStrategyProvider( + { + core: mockCoreSetup, + config$: mockConfig$, + }, + mockApiCaller, + mockSearch + ); + + await esSearch.search({ indexType: 'rollup', params }); + + expect(mockApiCaller).toBeCalled(); + expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); + const { method, path } = mockApiCaller.mock.calls[0][1]; + expect(method).toBe('POST'); + expect(path).toBe('foo-%E7%A8%8B/_rollup_search'); + }); +}); diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts index 004e57f71464a..57d31553382bf 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -16,6 +16,7 @@ import { ISearchOptions, ISearchCancel, getDefaultSearchParams, + getTotalLoaded, } from '../../../../../src/plugins/data/server'; import { IEnhancedEsSearchRequest } from '../../common'; @@ -36,31 +37,21 @@ export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider) - : (response as AsyncSearchResponse).response; - - const id = (response as AsyncSearchResponse).id; - const { total, failed, successful } = rawResponse._shards; - const loaded = failed + successful; - return { id, total, loaded, rawResponse }; + : asyncSearch(caller, { ...request, params }, options); }; const cancel: ISearchCancel = async id => { const method = 'DELETE'; - const path = `_async_search/${id}`; + const path = encodeURI(`_async_search/${id}`); await caller('transport.request', { method, path }); }; return { search, cancel }; }; -function asyncSearch( +async function asyncSearch( caller: APICaller, request: IEnhancedEsSearchRequest, options?: ISearchOptions @@ -69,12 +60,18 @@ function asyncSearch( // If we have an ID, then just poll for that ID, otherwise send the entire request body const method = request.id ? 'GET' : 'POST'; - const path = request.id ? `_async_search/${request.id}` : `${index}/_async_search`; + const path = encodeURI(request.id ? `_async_search/${request.id}` : `${index}/_async_search`); // Wait up to 1s for the response to return - const query = toSnakeCase({ waitForCompletion: '1s', ...params }); + const query = toSnakeCase({ waitForCompletionTimeout: '1s', ...params }); + + const { response: rawResponse, id } = (await caller( + 'transport.request', + { method, path, body, query }, + options + )) as AsyncSearchResponse; - return caller('transport.request', { method, path, body, query }, options); + return { id, rawResponse, ...getTotalLoaded(rawResponse._shards) }; } async function rollupSearch( @@ -84,9 +81,16 @@ async function rollupSearch( ) { const { body, index, ...params } = request.params; const method = 'POST'; - const path = `${index}/_rollup_search`; + const path = encodeURI(`${index}/_rollup_search`); const query = toSnakeCase(params); - return caller('transport.request', { method, path, body, query }, options); + + const rawResponse = await ((caller( + 'transport.request', + { method, path, body, query }, + options + ) as unknown) as SearchResponse); + + return { rawResponse, ...getTotalLoaded(rawResponse._shards) }; } function toSnakeCase(obj: Record) { diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index 21a37e7f94d8c..b3eb518e35ae3 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -239,11 +239,19 @@ interface AlertMetadata { prev: string | null; } +interface AlertState { + state: { + host_metadata: HostMetadata; + }; +} + /** * Union of alert data and metadata. */ export type AlertData = AlertEvent & AlertMetadata; +export type AlertDetails = AlertData & AlertState; + export type HostMetadata = Immutable<{ '@timestamp': number; event: { diff --git a/x-pack/plugins/endpoint/package.json b/x-pack/plugins/endpoint/package.json index fc4f4bd586bef..9e65f23a38860 100644 --- a/x-pack/plugins/endpoint/package.json +++ b/x-pack/plugins/endpoint/package.json @@ -13,6 +13,7 @@ "devDependencies": { "@types/seedrandom": ">=2.0.0 <4.0.0", "@types/react-redux": "^7.1.0", - "redux-devtools-extension": "^2.13.8" + "redux-devtools-extension": "^2.13.8", + "ts-node": "^8.8.1" } } diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts index 42c24400d12d3..80b7fd87e13be 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts @@ -5,7 +5,7 @@ */ import { IIndexPattern } from 'src/plugins/data/public'; -import { Immutable, AlertData } from '../../../../../common/types'; +import { Immutable, AlertDetails } from '../../../../../common/types'; import { AlertListData } from '../../types'; interface ServerReturnedAlertsData { @@ -15,7 +15,7 @@ interface ServerReturnedAlertsData { interface ServerReturnedAlertDetailsData { readonly type: 'serverReturnedAlertDetailsData'; - readonly payload: Immutable; + readonly payload: Immutable; } interface ServerReturnedSearchBarIndexPatterns { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts index b37ba0c0983d3..2c6ebf52189f5 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts @@ -5,7 +5,7 @@ */ import { IIndexPattern } from 'src/plugins/data/public'; -import { AlertResultList, AlertData } from '../../../../../common/types'; +import { AlertResultList, AlertDetails } from '../../../../../common/types'; import { AppAction } from '../action'; import { MiddlewareFactory, AlertListState } from '../../types'; import { isOnAlertPage, apiQueryParams, hasSelectedAlert, uiQueryParams } from './selectors'; @@ -40,7 +40,7 @@ export const alertMiddlewareFactory: MiddlewareFactory = (coreSt if (action.type === 'userChangedUrl' && isOnAlertPage(state) && hasSelectedAlert(state)) { const uiParams = uiQueryParams(state); - const response: AlertData = await coreStart.http.get( + const response: AlertDetails = await coreStart.http.get( `/api/endpoint/alerts/${uiParams.selected_alert}` ); api.dispatch({ type: 'serverReturnedAlertDetailsData', payload: response }); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts index 3931723a55505..6a13e0f92471b 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertResultList } from '../../../../../common/types'; +import { AlertResultList, AlertDetails } from '../../../../../common/types'; import { EndpointDocGenerator } from '../../../../../common/generate_data'; export const mockAlertResultList: (options?: { @@ -47,3 +47,18 @@ export const mockAlertResultList: (options?: { }; return mock; }; + +export const mockAlertDetailsResult = (): AlertDetails => { + const generator = new EndpointDocGenerator(); + return { + ...generator.generateAlert(new Date().getTime()), + ...{ + id: 'xDUYMHABAKk0XnHd8rrd', + prev: null, + next: null, + state: { + host_metadata: generator.generateHostMetadata(), + }, + }, + }; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts index 6c5330bd78baf..7947a35068234 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts @@ -12,6 +12,7 @@ import { AlertResultList, Immutable, ImmutableArray, + AlertDetails, } from '../../../common/types'; import { EndpointPluginStartDependencies } from '../../plugin'; import { AppAction } from './store/action'; @@ -196,7 +197,7 @@ export interface AlertListState { readonly location?: Immutable; /** Specific Alert data to be shown in the details view */ - readonly alertDetails?: Immutable; + readonly alertDetails?: Immutable; /** Search bar state including indexPatterns */ readonly searchBar: AlertsSearchBarState; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.test.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.test.tsx index 0f5a9dd7fed17..e3639bf1cacbc 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.test.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.test.tsx @@ -9,7 +9,7 @@ import { appStoreFactory } from '../../store'; import { fireEvent } from '@testing-library/react'; import { MemoryHistory } from 'history'; import { AppAction } from '../../types'; -import { mockAlertResultList } from '../../store/alerts/mock_alert_result_list'; +import { mockAlertDetailsResult } from '../../store/alerts/mock_alert_result_list'; import { alertPageTestRender } from './test_helpers/render_alert_page'; describe('when the alert details flyout is open', () => { @@ -34,7 +34,7 @@ describe('when the alert details flyout is open', () => { reactTestingLibrary.act(() => { const action: AppAction = { type: 'serverReturnedAlertDetailsData', - payload: mockAlertResultList().alerts[0], + payload: mockAlertDetailsResult(), }; store.dispatch(action); }); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts index ceb5da2ca9098..0860c9c62aca4 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts @@ -43,6 +43,7 @@ interface UserChangedSelectedEvent { interface AppRequestedResolverData { readonly type: 'appRequestedResolverData'; } + /** * When the user switches the active descendent of the Resolver. */ @@ -50,7 +51,7 @@ interface UserFocusedOnResolverNode { readonly type: 'userFocusedOnResolverNode'; readonly payload: { /** - * Used to identify the process node that should be brought into view. + * Used to identify the process node that the user focused on (in the DOM) */ readonly nodeId: string; }; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/defs.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/defs.tsx index 3295ef0289235..911cda1be6517 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/defs.tsx +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/defs.tsx @@ -13,6 +13,7 @@ import { euiPaletteForStatus, colorPalette, } from '@elastic/eui'; +import styled from 'styled-components'; /** * Generating from `colorPalette` function: This could potentially @@ -396,17 +397,25 @@ const SymbolsAndShapes = memo(() => ( )); /** - * This element is used to define the reusable assets for the Resolver - * It confers sevral advantages, including but not limited to: - * 1) Freedom of form for creative assets (beyond box-model constraints) - * 2) Separation of concerns between creative assets and more functional areas of the app - * 3) elements can be handled by compositor (faster) + * This `` element is used to define the reusable assets for the Resolver + * It confers several advantages, including but not limited to: + * 1. Freedom of form for creative assets (beyond box-model constraints) + * 2. Separation of concerns between creative assets and more functional areas of the app + * 3. `` elements can be handled by compositor (faster) */ -export const SymbolDefinitions = memo(() => ( - - - - - - -)); +export const SymbolDefinitions = styled( + memo(({ className }: { className?: string }) => ( + + + + + + + )) +)` + position: absolute; + left: 100%; + top: 100%; + width: 0; + height: 0; +`; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx index 22e9d05ad98ff..58ce9b963de5d 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx @@ -88,15 +88,22 @@ export const Resolver = styled( projectionMatrix={projectionMatrix} /> ))} - {Array.from(processNodePositions).map(([processEvent, position], index) => ( - - ))} + {[...processNodePositions].map(([processEvent, position], index) => { + const adjacentNodeMap = processToAdjacencyMap.get(processEvent); + if (!adjacentNodeMap) { + // This should never happen + throw new Error('Issue calculating adjacency node map.'); + } + return ( + + ); + })} )} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx index 603521e2d9bb3..b5f8f49877853 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx @@ -82,7 +82,7 @@ export const ProcessEventDot = styled( /** * map of what nodes are "adjacent" to this one in "up, down, previous, next" directions */ - adjacentNodeMap?: AdjacentProcessMap; + adjacentNodeMap: AdjacentProcessMap; }) => { /** * Convert the position, which is in 'world' coordinates, to screen coordinates. @@ -91,7 +91,7 @@ export const ProcessEventDot = styled( const [magFactorX] = projectionMatrix; - const selfId = adjacentNodeMap?.self; + const selfId = adjacentNodeMap.self; const nodeViewportStyle = useMemo( () => ({ @@ -117,27 +117,31 @@ export const ProcessEventDot = styled( const labelYHeight = markerSize / 1.7647; - const levelAttribute = adjacentNodeMap?.level - ? { - 'aria-level': adjacentNodeMap.level, - } - : {}; - - const flowToAttribute = adjacentNodeMap?.nextSibling - ? { - 'aria-flowto': adjacentNodeMap.nextSibling, - } - : {}; + /** + * An element that should be animated when the node is clicked. + */ + const animationTarget: { + current: + | (SVGAnimationElement & { + /** + * `beginElement` is by [w3](https://www.w3.org/TR/SVG11/animate.html#__smil__ElementTimeControl__beginElement) + * but missing in [TSJS-lib-generator](https://github.com/microsoft/TSJS-lib-generator/blob/15a4678e0ef6de308e79451503e444e9949ee849/inputfiles/addedTypes.json#L1819) + */ + beginElement: () => void; + }) + | null; + } = React.createRef(); + const { cubeSymbol, labelFill, descriptionFill, descriptionText } = nodeAssets[ + nodeType(event) + ]; + const resolverNodeIdGenerator = useMemo(() => htmlIdGenerator('resolverNode'), []); - const nodeType = getNodeType(event); - const clickTargetRef: { current: SVGAnimationElement | null } = React.createRef(); - const { cubeSymbol, labelFill, descriptionFill, descriptionText } = nodeAssets[nodeType]; - const resolverNodeIdGenerator = htmlIdGenerator('resolverNode'); - const [nodeId, labelId, descriptionId] = [ - !!selfId ? resolverNodeIdGenerator(String(selfId)) : resolverNodeIdGenerator(), - resolverNodeIdGenerator(), - resolverNodeIdGenerator(), - ] as string[]; + const nodeId = useMemo(() => resolverNodeIdGenerator(selfId), [ + resolverNodeIdGenerator, + selfId, + ]); + const labelId = useMemo(() => resolverNodeIdGenerator(), [resolverNodeIdGenerator]); + const descriptionId = useMemo(() => resolverNodeIdGenerator(), [resolverNodeIdGenerator]); const dispatch = useResolverDispatch(); @@ -154,14 +158,11 @@ export const ProcessEventDot = styled( [dispatch, nodeId] ); - const handleClick = useCallback( - (clickEvent: React.MouseEvent) => { - if (clickTargetRef.current !== null) { - (clickTargetRef.current as any).beginElement(); - } - }, - [clickTargetRef] - ); + const handleClick = useCallback(() => { + if (animationTarget.current !== null) { + animationTarget.current.beginElement(); + } + }, [animationTarget]); return ( @@ -171,8 +172,10 @@ export const ProcessEventDot = styled( viewBox="-15 -15 90 30" preserveAspectRatio="xMidYMid meet" role="treeitem" - {...levelAttribute} - {...flowToAttribute} + aria-level={adjacentNodeMap.level} + aria-flowto={ + adjacentNodeMap.nextSibling === null ? undefined : adjacentNodeMap.nextSibling + } aria-labelledby={labelId} aria-describedby={descriptionId} aria-haspopup={'true'} @@ -202,7 +205,7 @@ export const ProcessEventDot = styled( begin="click" repeatCount="1" className="squish" - ref={clickTargetRef} + ref={animationTarget} /> = unknownEvent: 'runningProcessCube', }; -function getNodeType(processEvent: ResolverEvent): keyof typeof nodeAssets { +function nodeType(processEvent: ResolverEvent): keyof typeof nodeAssets { const processType = processModel.eventType(processEvent); if (processType in processTypeToCube) { diff --git a/x-pack/plugins/endpoint/scripts/README.md b/x-pack/plugins/endpoint/scripts/README.md index f0c8c5a9b0b66..34d0a1ecd8ede 100644 --- a/x-pack/plugins/endpoint/scripts/README.md +++ b/x-pack/plugins/endpoint/scripts/README.md @@ -3,11 +3,7 @@ The default behavior is to create 1 endpoint with 1 alert and a moderate number A seed value can be provided as a string for the random number generator for repeatable behavior, useful for demos etc. Use the `-d` option if you want to delete and remake the indices, otherwise it will add documents to existing indices. -The sample data generator script depends on ts-node, install with npm: - -```npm install -g ts-node``` - -Example command sequence to get ES and kibana running with sample data after installing ts-node: +Example command sequence to get ES and kibana running with sample data: ```yarn es snapshot``` -> starts ES diff --git a/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts b/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts index 3497573918fac..b95c1aaf87c14 100644 --- a/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts +++ b/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts @@ -9,6 +9,7 @@ import { AlertEvent, EndpointAppConstants } from '../../../../common/types'; import { EndpointAppContext } from '../../../types'; import { AlertDetailsRequestParams } from '../types'; import { AlertDetailsPagination } from './lib'; +import { getHostData } from '../../../routes/metadata'; export const alertDetailsHandlerWrapper = function( endpointAppContext: EndpointAppContext @@ -33,10 +34,15 @@ export const alertDetailsHandlerWrapper = function( response ); + const currentHostInfo = await getHostData(ctx, response._source.host.id); + return res.ok({ body: { id: response._id, ...response._source, + state: { + host_metadata: currentHostInfo, + }, next: await pagination.getNextUrl(), prev: await pagination.getPrevUrl(), }, diff --git a/x-pack/plugins/endpoint/server/routes/metadata.ts b/x-pack/plugins/endpoint/server/routes/metadata.ts index 463a071ab0c77..787ffe58a5372 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; +import { IRouter, RequestHandlerContext } from 'kibana/server'; import { SearchResponse } from 'elasticsearch'; import { schema } from '@kbn/config-schema'; import { kibanaRequestToMetadataListESQuery, - kibanaRequestToMetadataGetESQuery, + getESQueryHostMetadataByID, } from '../services/endpoint/metadata_query_builders'; import { HostMetadata, HostResultList } from '../../common/types'; import { EndpointAppContext } from '../types'; @@ -75,17 +75,11 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp }, async (context, req, res) => { try { - const query = kibanaRequestToMetadataGetESQuery(req, endpointAppContext); - const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser( - 'search', - query - )) as SearchResponse; - - if (response.hits.hits.length === 0) { - return res.notFound({ body: 'Endpoint Not Found' }); + const doc = await getHostData(context, req.params.id); + if (doc) { + return res.ok({ body: doc }); } - - return res.ok({ body: response.hits.hits[0]._source }); + return res.notFound({ body: 'Endpoint Not Found' }); } catch (err) { return res.internalError({ body: err }); } @@ -93,6 +87,23 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp ); } +export async function getHostData( + context: RequestHandlerContext, + id: string +): Promise { + const query = getESQueryHostMetadataByID(id); + const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser( + 'search', + query + )) as SearchResponse; + + if (response.hits.hits.length === 0) { + return undefined; + } + + return response.hits.hits[0]._source; +} + function mapToHostResultList( queryParams: Record, searchResponse: SearchResponse diff --git a/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.test.ts b/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.test.ts index a3090361d4965..0966b52c79f7d 100644 --- a/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.test.ts +++ b/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.test.ts @@ -7,7 +7,7 @@ import { httpServerMock, loggingServiceMock } from '../../../../../../src/core/s import { EndpointConfigSchema } from '../../config'; import { kibanaRequestToMetadataListESQuery, - kibanaRequestToMetadataGetESQuery, + getESQueryHostMetadataByID, } from './metadata_query_builders'; import { EndpointAppConstants } from '../../../common/types'; @@ -118,15 +118,7 @@ describe('query builder', () => { describe('MetadataGetQuery', () => { it('searches for the correct ID', () => { const mockID = 'AABBCCDD-0011-2233-AA44-DEADBEEF8899'; - const mockRequest = httpServerMock.createKibanaRequest({ - params: { - id: mockID, - }, - }); - const query = kibanaRequestToMetadataGetESQuery(mockRequest, { - logFactory: loggingServiceMock.create(), - config: () => Promise.resolve(EndpointConfigSchema.validate({})), - }); + const query = getESQueryHostMetadataByID(mockID); expect(query).toEqual({ body: { query: { match: { 'host.id.keyword': mockID } }, diff --git a/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.ts b/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.ts index 300e837c4af1e..57b0a4ef10519 100644 --- a/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.ts +++ b/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.ts @@ -74,15 +74,12 @@ function buildQueryBody(request: KibanaRequest): Record, - endpointAppContext: EndpointAppContext -) => { +export function getESQueryHostMetadataByID(hostID: string) { return { body: { query: { match: { - 'host.id.keyword': request.params.id, + 'host.id.keyword': hostID, }, }, sort: [ @@ -96,4 +93,4 @@ export const kibanaRequestToMetadataGetESQuery = ( }, index: EndpointAppConstants.ENDPOINT_INDEX_NAME, }; -}; +} diff --git a/x-pack/plugins/event_log/generated/mappings.json b/x-pack/plugins/event_log/generated/mappings.json index fc1fdb71b0c37..50e7fdd5a9048 100644 --- a/x-pack/plugins/event_log/generated/mappings.json +++ b/x-pack/plugins/event_log/generated/mappings.json @@ -1,12 +1,15 @@ { - "dynamic": "strict", + "dynamic": "false", "properties": { "@timestamp": { "type": "date" }, "tags": { "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "meta": { + "isArray": true + } }, "message": { "norms": false, @@ -18,8 +21,7 @@ "ignore_above": 1024, "type": "keyword" } - }, - "dynamic": "strict" + } }, "event": { "properties": { @@ -40,8 +42,7 @@ "end": { "type": "date" } - }, - "dynamic": "strict" + } }, "error": { "properties": { @@ -49,8 +50,7 @@ "norms": false, "type": "text" } - }, - "dynamic": "strict" + } }, "user": { "properties": { @@ -58,8 +58,7 @@ "ignore_above": 1024, "type": "keyword" } - }, - "dynamic": "strict" + } }, "kibana": { "properties": { @@ -86,11 +85,9 @@ "ignore_above": 1024 } }, - "type": "nested", - "dynamic": "strict" + "type": "nested" } - }, - "dynamic": "strict" + } } } } \ No newline at end of file diff --git a/x-pack/plugins/event_log/scripts/create_schemas.js b/x-pack/plugins/event_log/scripts/create_schemas.js index 6e9ab00d04d1f..b46f7f295ddc7 100755 --- a/x-pack/plugins/event_log/scripts/create_schemas.js +++ b/x-pack/plugins/event_log/scripts/create_schemas.js @@ -30,11 +30,13 @@ function main() { const exportedProperties = mappings.EcsEventLogProperties; const multiValuedProperties = new Set(mappings.EcsEventLogMultiValuedProperties); + augmentMappings(ecsMappings.mappings, multiValuedProperties); + const elMappings = getEventLogMappings(ecsMappings, exportedProperties); console.log(`generating files in ${PLUGIN_DIR}`); writeEventLogMappings(elMappings); - writeEventLogConfigSchema(elMappings, ecsVersion, multiValuedProperties); + writeEventLogConfigSchema(elMappings, ecsVersion); } // return a stripped down version of the ecs schema, with only exportedProperties @@ -57,7 +59,6 @@ function getEventLogMappings(ecsSchema, exportedProperties) { const elValue = lodash.get(result.mappings.properties, prop); elValue.type = ecsValue.type; - elValue.dynamic = 'strict'; } return result; @@ -86,7 +87,7 @@ function writeEventLogMappings(elSchema) { // fixObjectTypes(elSchema.mappings); const mappings = { - dynamic: 'strict', + dynamic: 'false', properties: elSchema.mappings.properties, }; @@ -94,11 +95,10 @@ function writeEventLogMappings(elSchema) { console.log('generated:', EVENT_LOG_MAPPINGS_FILE); } -function writeEventLogConfigSchema(elSchema, ecsVersion, multiValuedProperties) { +function writeEventLogConfigSchema(elSchema, ecsVersion) { const lineWriter = LineWriter.createLineWriter(); - const elSchemaMappings = augmentMappings(elSchema.mappings, multiValuedProperties); - generateSchemaLines(lineWriter, null, elSchemaMappings); + generateSchemaLines(lineWriter, null, elSchema.mappings); // last line will have an extraneous comma const schemaLines = lineWriter.getContent().replace(/,$/, ''); @@ -113,22 +113,21 @@ const StringTypes = new Set(['string', 'keyword', 'text', 'ip']); const NumberTypes = new Set(['long', 'integer', 'float']); function augmentMappings(mappings, multiValuedProperties) { - // clone the mappings, as we're adding some additional properties - mappings = JSON.parse(JSON.stringify(mappings)); - for (const prop of multiValuedProperties) { const fullProp = replaceDotWithProperties(prop); - lodash.set(mappings.properties, `${fullProp}.multiValued`, true); + const metaPropName = `${fullProp}.meta`; + const meta = lodash.get(mappings.properties, metaPropName) || {}; + meta.isArray = true; + lodash.set(mappings.properties, metaPropName, meta); } - - return mappings; } function generateSchemaLines(lineWriter, prop, mappings) { const propKey = legalPropertyName(prop); + if (mappings == null) return; if (StringTypes.has(mappings.type)) { - if (mappings.multiValued) { + if (mappings.meta && mappings.meta.isArray) { lineWriter.addLine(`${propKey}: ecsStringMulti(),`); } else { lineWriter.addLine(`${propKey}: ecsString(),`); @@ -169,6 +168,7 @@ function generateSchemaLines(lineWriter, prop, mappings) { // write the object properties lineWriter.indent(); for (const prop of Object.keys(mappings.properties)) { + if (prop === 'meta') continue; generateSchemaLines(lineWriter, prop, mappings.properties[prop]); } lineWriter.dedent(); diff --git a/x-pack/plugins/graph/kibana.json b/x-pack/plugins/graph/kibana.json index cf3a5fab92f56..96e7ea3ff2232 100644 --- a/x-pack/plugins/graph/kibana.json +++ b/x-pack/plugins/graph/kibana.json @@ -5,6 +5,6 @@ "server": true, "ui": true, "requiredPlugins": ["licensing", "data", "navigation"], - "optionalPlugins": ["home"], + "optionalPlugins": ["home", "features"], "configPath": ["xpack", "graph"] } diff --git a/x-pack/plugins/graph/server/plugin.ts b/x-pack/plugins/graph/server/plugin.ts index dcdf3f51d60bd..a169953d5a10b 100644 --- a/x-pack/plugins/graph/server/plugin.ts +++ b/x-pack/plugins/graph/server/plugin.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { i18n } from '@kbn/i18n'; import { Plugin, CoreSetup } from 'src/core/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { LicenseState } from './lib/license_state'; @@ -11,13 +12,22 @@ import { registerSearchRoute } from './routes/search'; import { registerExploreRoute } from './routes/explore'; import { HomeServerPluginSetup } from '../../../../src/plugins/home/server'; import { registerSampleData } from './sample_data'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; export class GraphPlugin implements Plugin { private licenseState: LicenseState | null = null; public async setup( core: CoreSetup, - { licensing, home }: { licensing: LicensingPluginSetup; home?: HomeServerPluginSetup } + { + licensing, + home, + features, + }: { + licensing: LicensingPluginSetup; + home?: HomeServerPluginSetup; + features?: FeaturesPluginSetup; + } ) { const licenseState = new LicenseState(); licenseState.start(licensing.license$); @@ -27,6 +37,41 @@ export class GraphPlugin implements Plugin { registerSampleData(home.sampleData, licenseState); } + if (features) { + features.registerFeature({ + id: 'graph', + name: i18n.translate('xpack.graph.featureRegistry.graphFeatureName', { + defaultMessage: 'Graph', + }), + order: 1200, + icon: 'graphApp', + navLinkId: 'graph', + app: ['graph', 'kibana'], + catalogue: ['graph'], + validLicenses: ['platinum', 'enterprise', 'trial'], + privileges: { + all: { + app: ['graph', 'kibana'], + catalogue: ['graph'], + savedObject: { + all: ['graph-workspace'], + read: ['index-pattern'], + }, + ui: ['save', 'delete'], + }, + read: { + app: ['graph', 'kibana'], + catalogue: ['graph'], + savedObject: { + all: [], + read: ['index-pattern', 'graph-workspace'], + }, + ui: [], + }, + }, + }); + } + const router = core.http.createRouter(); registerSearchRoute({ licenseState, router }); registerExploreRoute({ licenseState, router }); diff --git a/x-pack/legacy/plugins/grokdebugger/README.md b/x-pack/plugins/grokdebugger/README.md similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/README.md rename to x-pack/plugins/grokdebugger/README.md diff --git a/x-pack/legacy/plugins/grokdebugger/common/constants/editor.js b/x-pack/plugins/grokdebugger/common/constants/editor.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/common/constants/editor.js rename to x-pack/plugins/grokdebugger/common/constants/editor.js diff --git a/x-pack/legacy/plugins/grokdebugger/common/constants/index.js b/x-pack/plugins/grokdebugger/common/constants/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/common/constants/index.js rename to x-pack/plugins/grokdebugger/common/constants/index.js diff --git a/x-pack/legacy/plugins/grokdebugger/common/constants/plugin.js b/x-pack/plugins/grokdebugger/common/constants/plugin.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/common/constants/plugin.js rename to x-pack/plugins/grokdebugger/common/constants/plugin.js diff --git a/x-pack/legacy/plugins/grokdebugger/common/constants/routes.js b/x-pack/plugins/grokdebugger/common/constants/routes.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/common/constants/routes.js rename to x-pack/plugins/grokdebugger/common/constants/routes.js diff --git a/x-pack/plugins/grokdebugger/kibana.json b/x-pack/plugins/grokdebugger/kibana.json new file mode 100644 index 0000000000000..4d37f9ccdb0de --- /dev/null +++ b/x-pack/plugins/grokdebugger/kibana.json @@ -0,0 +1,13 @@ +{ + "id": "grokdebugger", + "version": "8.0.0", + "kibanaVersion": "kibana", + "requiredPlugins": [ + "licensing", + "home", + "devTools" + ], + "server": true, + "ui": true, + "configPath": ["xpack", "grokdebugger"] +} diff --git a/x-pack/legacy/plugins/grokdebugger/public/components/custom_patterns_input/custom_patterns_input.js b/x-pack/plugins/grokdebugger/public/components/custom_patterns_input/custom_patterns_input.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/components/custom_patterns_input/custom_patterns_input.js rename to x-pack/plugins/grokdebugger/public/components/custom_patterns_input/custom_patterns_input.js diff --git a/x-pack/legacy/plugins/grokdebugger/public/components/custom_patterns_input/index.js b/x-pack/plugins/grokdebugger/public/components/custom_patterns_input/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/components/custom_patterns_input/index.js rename to x-pack/plugins/grokdebugger/public/components/custom_patterns_input/index.js diff --git a/x-pack/legacy/plugins/grokdebugger/public/components/event_input/event_input.js b/x-pack/plugins/grokdebugger/public/components/event_input/event_input.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/components/event_input/event_input.js rename to x-pack/plugins/grokdebugger/public/components/event_input/event_input.js diff --git a/x-pack/legacy/plugins/grokdebugger/public/components/event_input/index.js b/x-pack/plugins/grokdebugger/public/components/event_input/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/components/event_input/index.js rename to x-pack/plugins/grokdebugger/public/components/event_input/index.js diff --git a/x-pack/legacy/plugins/grokdebugger/public/components/event_output/event_output.js b/x-pack/plugins/grokdebugger/public/components/event_output/event_output.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/components/event_output/event_output.js rename to x-pack/plugins/grokdebugger/public/components/event_output/event_output.js diff --git a/x-pack/legacy/plugins/grokdebugger/public/components/event_output/index.js b/x-pack/plugins/grokdebugger/public/components/event_output/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/components/event_output/index.js rename to x-pack/plugins/grokdebugger/public/components/event_output/index.js diff --git a/x-pack/legacy/plugins/grokdebugger/public/components/grok_debugger/brace_imports.ts b/x-pack/plugins/grokdebugger/public/components/grok_debugger/brace_imports.ts similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/components/grok_debugger/brace_imports.ts rename to x-pack/plugins/grokdebugger/public/components/grok_debugger/brace_imports.ts diff --git a/x-pack/legacy/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js b/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js similarity index 90% rename from x-pack/legacy/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js rename to x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js index dfdd524b3d3d4..c27f3314e60ae 100644 --- a/x-pack/legacy/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js +++ b/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js @@ -22,10 +22,10 @@ import { PatternInput } from '../pattern_input'; import { CustomPatternsInput } from '../custom_patterns_input'; import { EventOutput } from '../event_output'; import { GrokdebuggerRequest } from '../../models/grokdebugger_request'; -import { toastNotifications } from 'ui/notify'; +import { withKibana } from '../../../../../../src/plugins/kibana_react/public'; import { FormattedMessage } from '@kbn/i18n/react'; -export class GrokDebugger extends React.Component { +export class GrokDebuggerComponent extends React.Component { constructor(props) { super(props); this.state = { @@ -73,6 +73,7 @@ export class GrokDebugger extends React.Component { }; simulateGrok = async () => { + const notifications = this.props.kibana.services.notifications; try { const simulateResponse = await this.props.grokdebuggerService.simulate( this.grokdebuggerRequest @@ -82,10 +83,14 @@ export class GrokDebugger extends React.Component { }); if (!isEmpty(simulateResponse.error)) { - toastNotifications.addDanger(simulateResponse.error); + notifications.toasts.addDanger({ + body: simulateResponse.error, + }); } } catch (e) { - toastNotifications.addDanger(e); + notifications.toasts.addDanger({ + body: e, + }); } }; @@ -138,3 +143,5 @@ export class GrokDebugger extends React.Component { ); } } + +export const GrokDebugger = withKibana(GrokDebuggerComponent); diff --git a/x-pack/legacy/plugins/grokdebugger/public/components/grok_debugger/index.js b/x-pack/plugins/grokdebugger/public/components/grok_debugger/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/components/grok_debugger/index.js rename to x-pack/plugins/grokdebugger/public/components/grok_debugger/index.js diff --git a/x-pack/plugins/grokdebugger/public/components/inactive_license.js b/x-pack/plugins/grokdebugger/public/components/inactive_license.js new file mode 100644 index 0000000000000..ff0306b789190 --- /dev/null +++ b/x-pack/plugins/grokdebugger/public/components/inactive_license.js @@ -0,0 +1,91 @@ +/* + * 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 { + EuiCallOut, + EuiText, + EuiLink, + EuiCode, + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const InactiveLicenseSlate = () => { + const registerLicenseLinkLabel = i18n.translate('xpack.grokDebugger.registerLicenseLinkLabel', { + defaultMessage: 'register a license', + }); + + const trialLicense = i18n.translate('xpack.grokDebugger.trialLicenseTitle', { + defaultMessage: 'Trial', + }); + + const basicLicense = i18n.translate('xpack.grokDebugger.basicLicenseTitle', { + defaultMessage: 'Basic', + }); + + const goldLicense = i18n.translate('xpack.grokDebugger.goldLicenseTitle', { + defaultMessage: 'Gold', + }); + + const platinumLicense = i18n.translate('xpack.grokDebugger.platinumLicenseTitle', { + defaultMessage: 'Platinum', + }); + + return ( + + + + + + +

+ + {trialLicense}, {basicLicense},{' '} + {goldLicense} + + ), + platinumLicenseType: {platinumLicense}, + }} + /> +

+

+ + {registerLicenseLinkLabel} + + ), + }} + /> +

+
+
+
+
+
+
+ ); +}; diff --git a/x-pack/legacy/plugins/grokdebugger/public/components/pattern_input/index.js b/x-pack/plugins/grokdebugger/public/components/pattern_input/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/components/pattern_input/index.js rename to x-pack/plugins/grokdebugger/public/components/pattern_input/index.js diff --git a/x-pack/legacy/plugins/grokdebugger/public/components/pattern_input/pattern_input.js b/x-pack/plugins/grokdebugger/public/components/pattern_input/pattern_input.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/components/pattern_input/pattern_input.js rename to x-pack/plugins/grokdebugger/public/components/pattern_input/pattern_input.js diff --git a/x-pack/legacy/plugins/canvas/types/webpack.d.ts b/x-pack/plugins/grokdebugger/public/index.js similarity index 67% rename from x-pack/legacy/plugins/canvas/types/webpack.d.ts rename to x-pack/plugins/grokdebugger/public/index.js index 8158071e76f08..960c9d8d58e4a 100644 --- a/x-pack/legacy/plugins/canvas/types/webpack.d.ts +++ b/x-pack/plugins/grokdebugger/public/index.js @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -declare module '*.png'; +import { Plugin } from './plugin'; -declare module '*.svg' { - const content: string; - export = content; +export function plugin(initializerContext) { + return new Plugin(initializerContext); } diff --git a/x-pack/legacy/plugins/grokdebugger/public/lib/ace/grok_highlight_rules.js b/x-pack/plugins/grokdebugger/public/lib/ace/grok_highlight_rules.js similarity index 97% rename from x-pack/legacy/plugins/grokdebugger/public/lib/ace/grok_highlight_rules.js rename to x-pack/plugins/grokdebugger/public/lib/ace/grok_highlight_rules.js index 3da1afdc0fc00..1851d53ff0ee8 100644 --- a/x-pack/legacy/plugins/grokdebugger/public/lib/ace/grok_highlight_rules.js +++ b/x-pack/plugins/grokdebugger/public/lib/ace/grok_highlight_rules.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import ace from 'ace'; +import ace from 'brace'; const { TextHighlightRules } = ace.acequire('ace/mode/text_highlight_rules'); diff --git a/x-pack/legacy/plugins/grokdebugger/public/lib/ace/grok_mode.js b/x-pack/plugins/grokdebugger/public/lib/ace/grok_mode.js similarity index 95% rename from x-pack/legacy/plugins/grokdebugger/public/lib/ace/grok_mode.js rename to x-pack/plugins/grokdebugger/public/lib/ace/grok_mode.js index 84b2e482673c6..cd82a5b9917e6 100644 --- a/x-pack/legacy/plugins/grokdebugger/public/lib/ace/grok_mode.js +++ b/x-pack/plugins/grokdebugger/public/lib/ace/grok_mode.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import ace from 'ace'; +import ace from 'brace'; import { GrokHighlightRules } from './grok_highlight_rules'; const TextMode = ace.acequire('ace/mode/text').Mode; diff --git a/x-pack/legacy/plugins/grokdebugger/public/lib/ace/index.js b/x-pack/plugins/grokdebugger/public/lib/ace/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/lib/ace/index.js rename to x-pack/plugins/grokdebugger/public/lib/ace/index.js diff --git a/x-pack/legacy/plugins/grokdebugger/public/models/grokdebugger_request/grokdebugger_request.js b/x-pack/plugins/grokdebugger/public/models/grokdebugger_request/grokdebugger_request.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/models/grokdebugger_request/grokdebugger_request.js rename to x-pack/plugins/grokdebugger/public/models/grokdebugger_request/grokdebugger_request.js diff --git a/x-pack/legacy/plugins/grokdebugger/public/models/grokdebugger_request/index.js b/x-pack/plugins/grokdebugger/public/models/grokdebugger_request/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/models/grokdebugger_request/index.js rename to x-pack/plugins/grokdebugger/public/models/grokdebugger_request/index.js diff --git a/x-pack/legacy/plugins/grokdebugger/public/models/grokdebugger_response/grokdebugger_response.js b/x-pack/plugins/grokdebugger/public/models/grokdebugger_response/grokdebugger_response.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/models/grokdebugger_response/grokdebugger_response.js rename to x-pack/plugins/grokdebugger/public/models/grokdebugger_response/grokdebugger_response.js diff --git a/x-pack/legacy/plugins/grokdebugger/public/models/grokdebugger_response/index.js b/x-pack/plugins/grokdebugger/public/models/grokdebugger_response/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/public/models/grokdebugger_response/index.js rename to x-pack/plugins/grokdebugger/public/models/grokdebugger_response/index.js diff --git a/x-pack/plugins/grokdebugger/public/plugin.js b/x-pack/plugins/grokdebugger/public/plugin.js new file mode 100644 index 0000000000000..9de4f9c3f8bc0 --- /dev/null +++ b/x-pack/plugins/grokdebugger/public/plugin.js @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { first } from 'rxjs/operators'; +import { registerFeature } from './register_feature'; +import { PLUGIN } from '../common/constants'; + +export class Plugin { + setup(coreSetup, plugins) { + registerFeature(plugins.home); + + plugins.devTools.register({ + order: 6, + title: i18n.translate('xpack.grokDebugger.displayName', { + defaultMessage: 'Grok Debugger', + }), + id: PLUGIN.ID, + enableRouting: false, + async mount(context, { element }) { + const [coreStart] = await coreSetup.getStartServices(); + const license = await plugins.licensing.license$.pipe(first()).toPromise(); + const { renderApp } = await import('./render_app'); + return renderApp(license, element, coreStart); + }, + }); + } + + start() {} + + stop() {} +} diff --git a/x-pack/plugins/grokdebugger/public/register_feature.ts b/x-pack/plugins/grokdebugger/public/register_feature.ts new file mode 100644 index 0000000000000..f05d4c973a5fb --- /dev/null +++ b/x-pack/plugins/grokdebugger/public/register_feature.ts @@ -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 { i18n } from '@kbn/i18n'; +import { + FeatureCatalogueCategory, + HomePublicPluginSetup, +} from '../../../../src/plugins/home/public'; + +export const registerFeature = (homePlugin: HomePublicPluginSetup) => { + homePlugin.featureCatalogue.register({ + id: 'grokdebugger', + title: i18n.translate('xpack.grokDebugger.registryProviderTitle', { + defaultMessage: 'Grok Debugger', + }), + description: i18n.translate('xpack.grokDebugger.registryProviderDescription', { + defaultMessage: 'Simulate and debug grok patterns for data transformation on ingestion.', + }), + icon: 'grokApp', + path: '/app/kibana#/dev_tools/grokdebugger', + showOnHomePage: false, + category: FeatureCatalogueCategory.ADMIN, + }); +}; diff --git a/x-pack/legacy/plugins/grokdebugger/public/render_app.js b/x-pack/plugins/grokdebugger/public/render_app.js similarity index 51% rename from x-pack/legacy/plugins/grokdebugger/public/render_app.js rename to x-pack/plugins/grokdebugger/public/render_app.js index 2c4894bb70ee1..82783c7bd9795 100644 --- a/x-pack/legacy/plugins/grokdebugger/public/render_app.js +++ b/x-pack/plugins/grokdebugger/public/render_app.js @@ -10,13 +10,23 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { GrokDebugger } from './components/grok_debugger'; import { GrokdebuggerService } from './services/grokdebugger/grokdebugger_service'; import { I18nProvider } from '@kbn/i18n/react'; +import { KibanaContextProvider } from '../../../../src/plugins/kibana_react/public'; +import { InactiveLicenseSlate } from './components/inactive_license'; -export function renderApp(element, npStart) { - render( +export function renderApp(license, element, coreStart) { + const content = license.isActive ? ( + + + + + + ) : ( - - , - element + + ); + + render(content, element); + return () => unmountComponentAtNode(element); } diff --git a/x-pack/legacy/plugins/grokdebugger/public/services/grokdebugger/grokdebugger_service.js b/x-pack/plugins/grokdebugger/public/services/grokdebugger/grokdebugger_service.js similarity index 59% rename from x-pack/legacy/plugins/grokdebugger/public/services/grokdebugger/grokdebugger_service.js rename to x-pack/plugins/grokdebugger/public/services/grokdebugger/grokdebugger_service.js index 7a656b33d59ff..e26c9c5091e14 100644 --- a/x-pack/legacy/plugins/grokdebugger/public/services/grokdebugger/grokdebugger_service.js +++ b/x-pack/plugins/grokdebugger/public/services/grokdebugger/grokdebugger_service.js @@ -4,21 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome from 'ui/chrome'; import { ROUTES } from '../../../common/constants'; -import { GrokdebuggerResponse } from 'plugins/grokdebugger/models/grokdebugger_response'; +import { GrokdebuggerResponse } from '../../models/grokdebugger_response'; export class GrokdebuggerService { constructor(http) { this.http = http; - this.basePath = chrome.addBasePath(ROUTES.API_ROOT); } simulate(grokdebuggerRequest) { return this.http - .post(`${this.basePath}/simulate`, { body: JSON.stringify(grokdebuggerRequest.upstreamJSON) }) + .post(`${ROUTES.API_ROOT}/simulate`, { + body: JSON.stringify(grokdebuggerRequest.upstreamJSON), + }) .then(response => { - return GrokdebuggerResponse.fromUpstreamJSON(response.grokdebuggerResponse); + return GrokdebuggerResponse.fromUpstreamJSON(response); }) .catch(e => { throw e.body.message; diff --git a/x-pack/plugins/grokdebugger/server/index.js b/x-pack/plugins/grokdebugger/server/index.js new file mode 100644 index 0000000000000..960c9d8d58e4a --- /dev/null +++ b/x-pack/plugins/grokdebugger/server/index.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Plugin } from './plugin'; + +export function plugin(initializerContext) { + return new Plugin(initializerContext); +} diff --git a/x-pack/plugins/grokdebugger/server/lib/kibana_framework.ts b/x-pack/plugins/grokdebugger/server/lib/kibana_framework.ts new file mode 100644 index 0000000000000..749f5e9ebf8f0 --- /dev/null +++ b/x-pack/plugins/grokdebugger/server/lib/kibana_framework.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/array-type */ + +import { i18n } from '@kbn/i18n'; + +import { + CoreSetup, + IRouter, + RequestHandlerContext, + RouteMethod, + RouteConfig, + RequestHandler, +} from 'src/core/server'; + +import { ILicense } from '../../../licensing/server'; + +type GrokDebuggerRouteConfig = { + method: RouteMethod; +} & RouteConfig; + +export class KibanaFramework { + public router: IRouter; + public license?: ILicense; + + constructor(core: CoreSetup) { + this.router = core.http.createRouter(); + } + + public setLicense(license: ILicense) { + this.license = license; + } + + private hasActiveLicense() { + if (!this.license) { + throw new Error( + "Please set license information in the plugin's setup method before trying to check the status" + ); + } + return this.license.isActive; + } + + public registerRoute( + config: GrokDebuggerRouteConfig, + handler: RequestHandler + ) { + // Automatically wrap all route registrations with license checking + const wrappedHandler: RequestHandler = async ( + requestContext, + request, + response + ) => { + if (this.hasActiveLicense()) { + return await handler(requestContext, request, response); + } else { + return response.forbidden({ + body: i18n.translate('xpack.grokDebugger.serverInactiveLicenseError', { + defaultMessage: 'The Grok Debugger tool requires an active license.', + }), + }); + } + }; + + const routeConfig = { + path: config.path, + validate: config.validate, + }; + + switch (config.method) { + case 'get': + this.router.get(routeConfig, wrappedHandler); + break; + case 'post': + this.router.post(routeConfig, wrappedHandler); + break; + case 'delete': + this.router.delete(routeConfig, wrappedHandler); + break; + case 'put': + this.router.put(routeConfig, wrappedHandler); + break; + } + } + + callWithRequest( + requestContext: RequestHandlerContext, + endpoint: 'ingest.simulate', + options?: { + body: any; + } + ): Promise; + + public async callWithRequest( + requestContext: RequestHandlerContext, + endpoint: string, + options?: any + ) { + const { elasticsearch } = requestContext.core; + return elasticsearch.dataClient.callAsCurrentUser(endpoint, options); + } +} diff --git a/x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_request/__tests__/grokdebugger_request.js b/x-pack/plugins/grokdebugger/server/models/grokdebugger_request/__tests__/grokdebugger_request.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_request/__tests__/grokdebugger_request.js rename to x-pack/plugins/grokdebugger/server/models/grokdebugger_request/__tests__/grokdebugger_request.js diff --git a/x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_request/grokdebugger_request.js b/x-pack/plugins/grokdebugger/server/models/grokdebugger_request/grokdebugger_request.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_request/grokdebugger_request.js rename to x-pack/plugins/grokdebugger/server/models/grokdebugger_request/grokdebugger_request.js diff --git a/x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_request/index.js b/x-pack/plugins/grokdebugger/server/models/grokdebugger_request/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_request/index.js rename to x-pack/plugins/grokdebugger/server/models/grokdebugger_request/index.js diff --git a/x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_response/__tests__/grokdebugger_response.js b/x-pack/plugins/grokdebugger/server/models/grokdebugger_response/__tests__/grokdebugger_response.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_response/__tests__/grokdebugger_response.js rename to x-pack/plugins/grokdebugger/server/models/grokdebugger_response/__tests__/grokdebugger_response.js diff --git a/x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_response/grokdebugger_response.js b/x-pack/plugins/grokdebugger/server/models/grokdebugger_response/grokdebugger_response.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_response/grokdebugger_response.js rename to x-pack/plugins/grokdebugger/server/models/grokdebugger_response/grokdebugger_response.js diff --git a/x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_response/index.js b/x-pack/plugins/grokdebugger/server/models/grokdebugger_response/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/server/models/grokdebugger_response/index.js rename to x-pack/plugins/grokdebugger/server/models/grokdebugger_response/index.js diff --git a/x-pack/plugins/grokdebugger/server/plugin.js b/x-pack/plugins/grokdebugger/server/plugin.js new file mode 100644 index 0000000000000..06ddd92aefac9 --- /dev/null +++ b/x-pack/plugins/grokdebugger/server/plugin.js @@ -0,0 +1,30 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { KibanaFramework } from './lib/kibana_framework'; +import { registerGrokdebuggerRoutes } from './routes/api/grokdebugger'; + +export const config = { + schema: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), +}; + +export class Plugin { + setup(coreSetup, plugins) { + const framework = new KibanaFramework(coreSetup); + + plugins.licensing.license$.subscribe(license => { + framework.setLicense(license); + }); + + registerGrokdebuggerRoutes(framework); + } + + start() {} + stop() {} +} diff --git a/x-pack/legacy/plugins/grokdebugger/server/routes/api/grokdebugger/index.js b/x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/index.js similarity index 100% rename from x-pack/legacy/plugins/grokdebugger/server/routes/api/grokdebugger/index.js rename to x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/index.js diff --git a/x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/register_grok_simulate_route.js b/x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/register_grok_simulate_route.js new file mode 100644 index 0000000000000..f953bc64c3b4f --- /dev/null +++ b/x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/register_grok_simulate_route.js @@ -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 { schema } from '@kbn/config-schema'; +import { GrokdebuggerRequest } from '../../../models/grokdebugger_request'; +import { GrokdebuggerResponse } from '../../../models/grokdebugger_response'; + +const requestBodySchema = schema.object({ + pattern: schema.string(), + rawEvent: schema.string(), + // We don't know these key / values up front as they depend on user input + customPatterns: schema.object({}, { unknowns: 'allow' }), +}); + +export function registerGrokSimulateRoute(framework) { + framework.registerRoute( + { + method: 'post', + path: '/api/grokdebugger/simulate', + validate: { + body: requestBodySchema, + }, + }, + async (requestContext, request, response) => { + try { + const grokdebuggerRequest = GrokdebuggerRequest.fromDownstreamJSON(request.body); + const simulateResponseFromES = await framework.callWithRequest( + requestContext, + 'ingest.simulate', + { body: grokdebuggerRequest.upstreamJSON } + ); + const grokdebuggerResponse = GrokdebuggerResponse.fromUpstreamJSON(simulateResponseFromES); + return response.ok({ + body: grokdebuggerResponse, + }); + } catch (error) { + return response.internalError({ + body: error.message, + }); + } + } + ); +} diff --git a/x-pack/legacy/plugins/grokdebugger/server/routes/api/grokdebugger/register_grokdebugger_routes.js b/x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/register_grokdebugger_routes.js similarity index 77% rename from x-pack/legacy/plugins/grokdebugger/server/routes/api/grokdebugger/register_grokdebugger_routes.js rename to x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/register_grokdebugger_routes.js index 6d27ce2ca0545..d7ecd1c360d81 100644 --- a/x-pack/legacy/plugins/grokdebugger/server/routes/api/grokdebugger/register_grokdebugger_routes.js +++ b/x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/register_grokdebugger_routes.js @@ -6,6 +6,6 @@ import { registerGrokSimulateRoute } from './register_grok_simulate_route'; -export function registerGrokdebuggerRoutes(server) { - registerGrokSimulateRoute(server); +export function registerGrokdebuggerRoutes(framework) { + registerGrokSimulateRoute(framework); } diff --git a/x-pack/plugins/infra/common/http_api/log_entries/entries.ts b/x-pack/plugins/infra/common/http_api/log_entries/entries.ts index 419ee021a9189..d532c079e3e9c 100644 --- a/x-pack/plugins/infra/common/http_api/log_entries/entries.ts +++ b/x-pack/plugins/infra/common/http_api/log_entries/entries.ts @@ -78,6 +78,11 @@ export const logEntryRT = rt.type({ id: rt.string, cursor: logEntriesCursorRT, columns: rt.array(logColumnRT), + context: rt.partial({ + 'log.file.path': rt.string, + 'host.name': rt.string, + 'container.id': rt.string, + }), }); export type LogMessageConstantPart = rt.TypeOf; diff --git a/x-pack/plugins/infra/common/log_analysis/log_analysis.ts b/x-pack/plugins/infra/common/log_analysis/log_analysis.ts index 9b2f1a55eb8c1..f0aa2067a24c2 100644 --- a/x-pack/plugins/infra/common/log_analysis/log_analysis.ts +++ b/x-pack/plugins/infra/common/log_analysis/log_analysis.ts @@ -14,19 +14,28 @@ export type JobStatus = | 'finished' | 'failed'; +export type SetupStatusRequiredReason = + | 'missing' // jobs are missing + | 'reconfiguration' // the configurations don't match the source configurations + | 'update'; // the definitions don't match the module definitions + export type SetupStatus = - | 'initializing' // acquiring job statuses to determine setup status - | 'unknown' // job status could not be acquired (failed request etc) - | 'required' // jobs are missing - | 'requiredForReconfiguration' // the configurations don't match the source configurations - | 'requiredForUpdate' // the definitions don't match the module definitions - | 'pending' // In the process of setting up the module for the first time or retrying, waiting for response - | 'succeeded' // setup succeeded, notifying user - | 'failed' // setup failed, notifying user - | 'hiddenAfterSuccess' // hide the setup screen and we show the results for the first time - | 'skipped' // setup hidden because the module is in a correct state already - | 'skippedButReconfigurable' // setup hidden even though the job configurations are outdated - | 'skippedButUpdatable'; // setup hidden even though the job definitions are outdated + | { type: 'initializing' } // acquiring job statuses to determine setup status + | { type: 'unknown' } // job status could not be acquired (failed request etc) + | { + type: 'required'; + reason: SetupStatusRequiredReason; + } // setup required + | { type: 'pending' } // In the process of setting up the module for the first time or retrying, waiting for response + | { type: 'succeeded' } // setup succeeded, notifying user + | { + type: 'failed'; + reasons: string[]; + } // setup failed, notifying user + | { + type: 'skipped'; + newlyCreated?: boolean; + }; // setup is hidden /** * Maps a job status to the possibility that results have already been produced @@ -43,9 +52,7 @@ export const isHealthyJobStatus = (jobStatus: JobStatus) => * produced before this state was reached. */ export const isSetupStatusWithResults = (setupStatus: SetupStatus) => - ['skipped', 'hiddenAfterSuccess', 'skippedButReconfigurable', 'skippedButUpdatable'].includes( - setupStatus - ); + setupStatus.type === 'skipped'; const KIBANA_SAMPLE_DATA_INDICES = ['kibana_sample_data_logs*']; diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx index 8a16d819e12c2..e7e89bb365e4f 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx @@ -6,33 +6,36 @@ import React from 'react'; -import { JobStatus, SetupStatus } from '../../../../common/log_analysis'; import { JobConfigurationOutdatedCallout } from './job_configuration_outdated_callout'; import { JobDefinitionOutdatedCallout } from './job_definition_outdated_callout'; import { JobStoppedCallout } from './job_stopped_callout'; +import { FirstUseCallout } from '../log_analysis_results'; export const LogAnalysisJobProblemIndicator: React.FC<{ - jobStatus: JobStatus; - setupStatus: SetupStatus; + hasOutdatedJobConfigurations: boolean; + hasOutdatedJobDefinitions: boolean; + hasStoppedJobs: boolean; + isFirstUse: boolean; onRecreateMlJobForReconfiguration: () => void; onRecreateMlJobForUpdate: () => void; -}> = ({ jobStatus, setupStatus, onRecreateMlJobForReconfiguration, onRecreateMlJobForUpdate }) => { - if (isStopped(jobStatus)) { - return ; - } else if (isUpdatable(setupStatus)) { - return ; - } else if (isReconfigurable(setupStatus)) { - return ; - } - - return null; // no problem to indicate +}> = ({ + hasOutdatedJobConfigurations, + hasOutdatedJobDefinitions, + hasStoppedJobs, + isFirstUse, + onRecreateMlJobForReconfiguration, + onRecreateMlJobForUpdate, +}) => { + return ( + <> + {hasOutdatedJobDefinitions ? ( + + ) : null} + {hasOutdatedJobConfigurations ? ( + + ) : null} + {hasStoppedJobs ? : null} + {isFirstUse ? : null} + + ); }; - -const isStopped = (jobStatus: JobStatus) => jobStatus === 'stopped'; - -const isUpdatable = (setupStatus: SetupStatus) => setupStatus === 'skippedButUpdatable'; - -const isReconfigurable = (setupStatus: SetupStatus) => setupStatus === 'skippedButReconfigurable'; - -export const jobHasProblem = (jobStatus: JobStatus, setupStatus: SetupStatus) => - isStopped(jobStatus) || isUpdatable(setupStatus) || isReconfigurable(setupStatus); diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx index de20dd12c17bd..4ec895dfed4bc 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx @@ -45,7 +45,7 @@ export const InitialConfigurationStep: React.FunctionComponent { - const disabled = useMemo(() => !editableFormStatus.includes(setupStatus), [setupStatus]); + const disabled = useMemo(() => !editableFormStatus.includes(setupStatus.type), [setupStatus]); return ( <> @@ -72,12 +72,7 @@ export const InitialConfigurationStep: React.FunctionComponent, status: - props.setupStatus === 'pending' + props.setupStatus.type === 'pending' ? 'incomplete' - : props.setupStatus === 'failed' + : props.setupStatus.type === 'failed' ? 'danger' - : props.setupStatus === 'succeeded' + : props.setupStatus.type === 'succeeded' ? 'complete' : undefined, }); @@ -55,7 +55,7 @@ export const ProcessStep: React.FunctionComponent = ({ }) => { return ( - {setupStatus === 'pending' ? ( + {setupStatus.type === 'pending' ? ( @@ -67,7 +67,7 @@ export const ProcessStep: React.FunctionComponent = ({ /> - ) : setupStatus === 'failed' ? ( + ) : setupStatus.type === 'failed' ? ( <> = ({ />
- ) : setupStatus === 'succeeded' ? ( + ) : setupStatus.type === 'succeeded' ? ( <> = ({ /> - ) : setupStatus === 'requiredForUpdate' || setupStatus === 'requiredForReconfiguration' ? ( + ) : setupStatus.type === 'required' && + (setupStatus.reason === 'update' || setupStatus.reason === 'reconfiguration') ? ( ) : ( diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_actions_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_actions_column.tsx new file mode 100644 index 0000000000000..e02346c4e758a --- /dev/null +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_actions_column.tsx @@ -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 React, { useCallback } from 'react'; +import { EuiButtonIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { LogEntryColumnContent } from './log_entry_column'; +import { + euiStyled, + ActionMenu, + Section, + SectionTitle, + SectionLinks, + SectionLink, +} from '../../../../../observability/public'; + +interface LogEntryActionsColumnProps { + isHovered: boolean; + isMenuOpen: boolean; + onOpenMenu: () => void; + onCloseMenu: () => void; + onViewDetails: () => void; +} + +const MENU_LABEL = i18n.translate('xpack.infra.logEntryItemView.logEntryActionsMenuToolTip', { + defaultMessage: 'View Details', +}); + +const LOG_DETAILS_LABEL = i18n.translate('xpack.infra.logs.logEntryActionsDetailsButton', { + defaultMessage: 'View actions for line', +}); + +export const LogEntryActionsColumn: React.FC = ({ + isHovered, + isMenuOpen, + onOpenMenu, + onCloseMenu, + onViewDetails, +}) => { + const handleClickViewDetails = useCallback(() => { + onCloseMenu(); + onViewDetails(); + }, [onCloseMenu, onViewDetails]); + + const button = ( + + + + ); + + return ( + + {isHovered || isMenuOpen ? ( + + +
+ + + + + + +
+
+
+ ) : null} +
+ ); +}; + +const ActionsColumnContent = euiStyled(LogEntryColumnContent)` + overflow: hidden; + user-select: none; +`; + +const ButtonWrapper = euiStyled.div` + background: ${props => props.theme.eui.euiColorPrimary}; + border-radius: 50%; +`; + +// this prevents the button from influencing the line height +const AbsoluteWrapper = euiStyled.div` + overflow: hidden; + position: absolute; +`; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx index 5fc4606a774d5..d6068b6e60992 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx @@ -25,8 +25,6 @@ describe('LogEntryFieldColumn', () => { columnValue={column} highlights={[]} isActiveHighlight={false} - isHighlighted={false} - isHovered={false} wrapMode="pre-wrapped" />, { wrappingComponent: EuiThemeProvider } as any // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/36075 @@ -58,8 +56,6 @@ describe('LogEntryFieldColumn', () => { columnValue={column} highlights={[]} isActiveHighlight={false} - isHighlighted={false} - isHovered={false} wrapMode="pre-wrapped" />, { wrappingComponent: EuiThemeProvider } as any // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/36075 @@ -81,8 +77,6 @@ describe('LogEntryFieldColumn', () => { columnValue={column} highlights={[]} isActiveHighlight={false} - isHighlighted={false} - isHovered={false} wrapMode="pre-wrapped" />, { wrappingComponent: EuiThemeProvider } as any // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/36075 diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx index 202108cda5ac0..c73c9674f9683 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx @@ -13,7 +13,6 @@ import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './h import { LogEntryColumnContent } from './log_entry_column'; import { LogColumn } from '../../../../common/http_api'; import { - hoveredContentStyle, longWrappedContentStyle, preWrappedContentStyle, unwrappedContentStyle, @@ -24,8 +23,6 @@ interface LogEntryFieldColumnProps { columnValue: LogColumn; highlights: LogColumn[]; isActiveHighlight: boolean; - isHighlighted: boolean; - isHovered: boolean; wrapMode: WrapMode; } @@ -33,8 +30,6 @@ export const LogEntryFieldColumn: React.FunctionComponent { const value = useMemo(() => { @@ -63,11 +58,7 @@ export const LogEntryFieldColumn: React.FunctionComponent - {formattedValue} - - ); + return {formattedValue}; }; const CommaSeparatedLi = euiStyled.li` @@ -81,15 +72,12 @@ const CommaSeparatedLi = euiStyled.li` `; interface LogEntryColumnContentProps { - isHighlighted: boolean; - isHovered: boolean; wrapMode: WrapMode; } const FieldColumnContent = euiStyled(LogEntryColumnContent)` text-overflow: ellipsis; - ${props => (props.isHovered || props.isHighlighted ? hoveredContentStyle : '')}; ${props => props.wrapMode === 'long' ? longWrappedContentStyle diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_icon_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_icon_column.tsx deleted file mode 100644 index a4099cdf5a1fb..0000000000000 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_icon_column.tsx +++ /dev/null @@ -1,67 +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 { EuiButtonIcon } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; - -import { LogEntryColumnContent } from './log_entry_column'; -import { hoveredContentStyle } from './text_styles'; -import { euiStyled } from '../../../../../observability/public'; - -interface LogEntryIconColumnProps { - isHighlighted: boolean; - isHovered: boolean; -} - -export const LogEntryIconColumn: React.FunctionComponent = ({ - children, - isHighlighted, - isHovered, -}) => { - return ( - - {children} - - ); -}; - -export const LogEntryDetailsIconColumn: React.FunctionComponent void; -}> = ({ isHighlighted, isHovered, openFlyout }) => { - const label = i18n.translate('xpack.infra.logEntryItemView.viewDetailsToolTip', { - defaultMessage: 'View Details', - }); - - return ( - - {isHovered ? ( - - - - ) : null} - - ); -}; - -interface IconColumnContentProps { - isHighlighted: boolean; - isHovered: boolean; -} - -const IconColumnContent = euiStyled(LogEntryColumnContent)` - background-color: ${props => props.theme.eui.euiColorEmptyShade}; - overflow: hidden; - user-select: none; - - ${props => (props.isHovered || props.isHighlighted ? hoveredContentStyle : '')}; -`; - -// this prevents the button from influencing the line height -const AbsoluteIconButtonWrapper = euiStyled.div` - overflow: hidden; - position: absolute; -`; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx index 5ad7cba6427d1..0fe0cbdfac593 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx @@ -18,7 +18,6 @@ import { import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './highlighting'; import { LogEntryColumnContent } from './log_entry_column'; import { - hoveredContentStyle, longWrappedContentStyle, preWrappedContentStyle, unwrappedContentStyle, @@ -30,13 +29,11 @@ interface LogEntryMessageColumnProps { columnValue: LogColumn; highlights: LogColumn[]; isActiveHighlight: boolean; - isHighlighted: boolean; - isHovered: boolean; wrapMode: WrapMode; } export const LogEntryMessageColumn = memo( - ({ columnValue, highlights, isActiveHighlight, isHighlighted, isHovered, wrapMode }) => { + ({ columnValue, highlights, isActiveHighlight, wrapMode }) => { const message = useMemo( () => isMessageColumn(columnValue) @@ -45,24 +42,16 @@ export const LogEntryMessageColumn = memo( [columnValue, highlights, isActiveHighlight] ); - return ( - - {message} - - ); + return {message}; } ); interface MessageColumnContentProps { - isHovered: boolean; - isHighlighted: boolean; wrapMode: WrapMode; } const MessageColumnContent = euiStyled(LogEntryColumnContent)` text-overflow: ellipsis; - - ${props => (props.isHovered || props.isHighlighted ? hoveredContentStyle : '')}; ${props => props.wrapMode === 'long' ? longWrappedContentStyle diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx index ce264245d385b..7d7df796d13ad 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx @@ -17,10 +17,10 @@ import { import { TextScale } from '../../../../common/log_text_scale'; import { LogEntryColumn, LogEntryColumnWidths, iconColumnId } from './log_entry_column'; import { LogEntryFieldColumn } from './log_entry_field_column'; -import { LogEntryDetailsIconColumn } from './log_entry_icon_column'; +import { LogEntryActionsColumn } from './log_entry_actions_column'; import { LogEntryMessageColumn } from './log_entry_message_column'; import { LogEntryTimestampColumn } from './log_entry_timestamp_column'; -import { monospaceTextStyle } from './text_styles'; +import { monospaceTextStyle, hoveredContentStyle, highlightedContentStyle } from './text_styles'; import { LogEntry, LogColumn } from '../../../../common/http_api'; interface LogEntryRowProps { @@ -50,14 +50,13 @@ export const LogEntryRow = memo( wrap, }: LogEntryRowProps) => { const [isHovered, setIsHovered] = useState(false); + const [isMenuOpen, setIsMenuOpen] = useState(false); - const setItemIsHovered = useCallback(() => { - setIsHovered(true); - }, []); + const openMenu = useCallback(() => setIsMenuOpen(true), []); + const closeMenu = useCallback(() => setIsMenuOpen(false), []); - const setItemIsNotHovered = useCallback(() => { - setIsHovered(false); - }, []); + const setItemIsHovered = useCallback(() => setIsHovered(true), []); + const setItemIsNotHovered = useCallback(() => setIsHovered(false), []); const openFlyout = useCallback(() => openFlyoutWithItem?.(logEntry.id), [ openFlyoutWithItem, @@ -105,6 +104,7 @@ export const LogEntryRow = memo( } onMouseEnter={setItemIsHovered} onMouseLeave={setItemIsNotHovered} + isHighlighted={isHighlighted} scale={scale} > {columnConfigurations.map(columnConfiguration => { @@ -119,11 +119,7 @@ export const LogEntryRow = memo( {...columnWidth} > {isTimestampColumn(column) ? ( - + ) : null} ); @@ -141,9 +137,7 @@ export const LogEntryRow = memo( ) : null} @@ -164,8 +158,6 @@ export const LogEntryRow = memo( columnValue={column} highlights={highlightsByColumnId[column.columnId] || []} isActiveHighlight={isActiveHighlight} - isHighlighted={isHighlighted} - isHovered={isHovered} wrapMode={wrap ? 'long' : 'pre-wrapped'} /> ) : null} @@ -177,10 +169,12 @@ export const LogEntryRow = memo( key="logColumn iconLogColumn iconLogColumn:details" {...columnWidths[iconColumnId]} > - @@ -190,6 +184,7 @@ export const LogEntryRow = memo( interface LogEntryRowWrapperProps { scale: TextScale; + isHighlighted?: boolean; } export const LogEntryRowWrapper = euiStyled.div.attrs(() => ({ @@ -204,4 +199,9 @@ export const LogEntryRowWrapper = euiStyled.div.attrs(() => ({ overflow: hidden; ${props => monospaceTextStyle(props.scale)}; + ${props => (props.isHighlighted ? highlightedContentStyle : '')} + + &:hover { + ${hoveredContentStyle} + } `; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx index f3ea9c81108c6..cf9c75a361b55 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx @@ -4,54 +4,28 @@ * you may not use this file except in compliance with the Elastic License. */ -import { darken, transparentize } from 'polished'; import React, { memo } from 'react'; -import { euiStyled, css } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../observability/public'; import { TimeFormat, useFormattedTime } from '../../formatted_time'; import { LogEntryColumnContent } from './log_entry_column'; interface LogEntryTimestampColumnProps { format?: TimeFormat; - isHighlighted: boolean; - isHovered: boolean; time: number; } export const LogEntryTimestampColumn = memo( - ({ format = 'time', isHighlighted, isHovered, time }) => { + ({ format = 'time', time }) => { const formattedTime = useFormattedTime(time, { format }); - return ( - - {formattedTime} - - ); + return {formattedTime}; } ); -const hoveredContentStyle = css` - background-color: ${props => - props.theme.darkMode - ? transparentize(0.9, darken(0.05, props.theme.eui.euiColorHighlight)) - : darken(0.05, props.theme.eui.euiColorHighlight)}; - border-color: ${props => - props.theme.darkMode - ? transparentize(0.7, darken(0.2, props.theme.eui.euiColorHighlight)) - : darken(0.2, props.theme.eui.euiColorHighlight)}; - color: ${props => props.theme.eui.euiColorFullShade}; -`; - -interface TimestampColumnContentProps { - isHovered: boolean; - isHighlighted: boolean; -} - -const TimestampColumnContent = euiStyled(LogEntryColumnContent)` +const TimestampColumnContent = euiStyled(LogEntryColumnContent)` color: ${props => props.theme.eui.euiColorDarkShade}; overflow: hidden; text-overflow: clip; white-space: pre; - - ${props => (props.isHovered || props.isHighlighted ? hoveredContentStyle : '')}; `; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx index 434258343eefb..69a6abbca4b34 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { darken, transparentize } from 'polished'; import React, { useMemo, useState, useCallback } from 'react'; import { euiStyled, css } from '../../../../../observability/public'; @@ -30,10 +29,11 @@ export const monospaceTextStyle = (scale: TextScale) => css` `; export const hoveredContentStyle = css` - background-color: ${props => - props.theme.darkMode - ? transparentize(0.9, darken(0.05, props.theme.eui.euiColorHighlight)) - : darken(0.05, props.theme.eui.euiColorHighlight)}; + background-color: ${props => props.theme.eui.euiFocusBackgroundColor}; +`; + +export const highlightedContentStyle = css` + background-color: ${props => props.theme.eui.euiFocusBackgroundColor}; `; export const longWrappedContentStyle = css` diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts index 49112dd8ec8d8..c3c119f51ff7d 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts @@ -54,6 +54,22 @@ const jobStateRT = rt.keyof({ opening: null, }); +const jobCategorizationStatusRT = rt.keyof({ + ok: null, + warn: null, +}); + +const jobModelSizeStatsRT = rt.type({ + categorization_status: jobCategorizationStatusRT, + categorized_doc_count: rt.number, + dead_category_count: rt.number, + frequent_category_count: rt.number, + rare_category_count: rt.number, + total_category_count: rt.number, +}); + +export type JobModelSizeStats = rt.TypeOf; + export const jobSummaryRT = rt.intersection([ rt.type({ id: rt.string, @@ -65,6 +81,7 @@ export const jobSummaryRT = rt.intersection([ fullJob: rt.partial({ custom_settings: jobCustomSettingsRT, finished_time: rt.number, + model_size_stats: jobModelSizeStatsRT, }), }), ]); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/index.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/index.ts index 185f6936662bd..9828ad72ec909 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/index.ts @@ -7,6 +7,10 @@ export * from './log_analysis_capabilities'; export * from './log_analysis_cleanup'; export * from './log_analysis_module'; +export * from './log_analysis_module_configuration'; +export * from './log_analysis_module_definition'; export * from './log_analysis_module_status'; export * from './log_analysis_module_types'; export * from './log_analysis_setup_state'; + +export { JobModelSizeStats, JobSummary } from './api/ml_get_jobs_summary_api'; diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx index efe9629e84e42..99c5a3df7c9b1 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useCallback, useEffect, useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { useModuleStatus } from './log_analysis_module_status'; @@ -17,36 +17,10 @@ export const useLogAnalysisModule = ({ sourceConfiguration: ModuleSourceConfiguration; moduleDescriptor: ModuleDescriptor; }) => { - const { spaceId, sourceId, timestampField, indices } = sourceConfiguration; - const [moduleStatus, dispatchModuleStatus] = useModuleStatus(moduleDescriptor.jobTypes, { - bucketSpan: moduleDescriptor.bucketSpan, - indexPattern: indices.join(','), - timestampField, - }); + const { spaceId, sourceId, timestampField } = sourceConfiguration; + const [moduleStatus, dispatchModuleStatus] = useModuleStatus(moduleDescriptor.jobTypes); - const [fetchModuleDefinitionRequest, fetchModuleDefinition] = useTrackedPromise( - { - cancelPreviousOn: 'resolution', - createPromise: async () => { - dispatchModuleStatus({ type: 'fetchingModuleDefinition' }); - return await moduleDescriptor.getModuleDefinition(); - }, - onResolve: response => { - dispatchModuleStatus({ - type: 'fetchedModuleDefinition', - spaceId, - sourceId, - moduleDefinition: response, - }); - }, - onReject: () => { - dispatchModuleStatus({ type: 'failedFetchingModuleDefinition' }); - }, - }, - [moduleDescriptor.getModuleDefinition, spaceId, sourceId] - ); - - const [fetchJobStatusRequest, fetchJobStatus] = useTrackedPromise( + const [, fetchJobStatus] = useTrackedPromise( { cancelPreviousOn: 'resolution', createPromise: async () => { @@ -68,12 +42,6 @@ export const useLogAnalysisModule = ({ [spaceId, sourceId] ); - const isLoadingModuleStatus = useMemo( - () => - fetchJobStatusRequest.state === 'pending' || fetchModuleDefinitionRequest.state === 'pending', - [fetchJobStatusRequest.state, fetchModuleDefinitionRequest.state] - ); - const [, setUpModule] = useTrackedPromise( { cancelPreviousOn: 'resolution', @@ -83,15 +51,24 @@ export const useLogAnalysisModule = ({ end: number | undefined ) => { dispatchModuleStatus({ type: 'startedSetup' }); - return await moduleDescriptor.setUpModule(start, end, { + const setupResult = await moduleDescriptor.setUpModule(start, end, { indices: selectedIndices, sourceId, spaceId, timestampField, }); + const jobSummaries = await moduleDescriptor.getJobSummary(spaceId, sourceId); + return { setupResult, jobSummaries }; }, - onResolve: ({ datafeeds, jobs }) => { - dispatchModuleStatus({ type: 'finishedSetup', datafeeds, jobs, spaceId, sourceId }); + onResolve: ({ setupResult: { datafeeds, jobs }, jobSummaries }) => { + dispatchModuleStatus({ + type: 'finishedSetup', + datafeedSetupResults: datafeeds, + jobSetupResults: jobs, + jobSummaries, + spaceId, + sourceId, + }); }, onReject: () => { dispatchModuleStatus({ type: 'failedSetup' }); @@ -146,36 +123,14 @@ export const useLogAnalysisModule = ({ sourceId, ]); - useEffect(() => { - dispatchModuleStatus({ - type: 'updatedSourceConfiguration', - spaceId, - sourceId, - sourceConfiguration: { - timestampField, - indexPattern: indices.join(','), - bucketSpan: moduleDescriptor.bucketSpan, - }, - }); - }, [ - dispatchModuleStatus, - indices, - moduleDescriptor.bucketSpan, - sourceConfiguration, - sourceId, - spaceId, - timestampField, - ]); - return { cleanUpAndSetUpModule, cleanUpModule, fetchJobStatus, - fetchModuleDefinition, isCleaningUp, - isLoadingModuleStatus, jobIds, jobStatus: moduleStatus.jobStatus, + jobSummaries: moduleStatus.jobSummaries, lastSetupErrorMessages: moduleStatus.lastSetupErrorMessages, moduleDescriptor, setUpModule, diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_configuration.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_configuration.ts new file mode 100644 index 0000000000000..ba2185ff83784 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_configuration.ts @@ -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 { useMemo } from 'react'; +import { JobSummary } from './api/ml_get_jobs_summary_api'; +import { ModuleDescriptor, ModuleSourceConfiguration } from './log_analysis_module_types'; + +export const useLogAnalysisModuleConfiguration = ({ + moduleDescriptor, + sourceConfiguration, +}: { + moduleDescriptor: ModuleDescriptor; + sourceConfiguration: ModuleSourceConfiguration; +}) => { + const getIsJobConfigurationOutdated = useMemo( + () => isJobConfigurationOutdated(moduleDescriptor, sourceConfiguration), + [sourceConfiguration, moduleDescriptor] + ); + + return { + getIsJobConfigurationOutdated, + }; +}; + +export const isJobConfigurationOutdated = ( + { bucketSpan }: ModuleDescriptor, + currentSourceConfiguration: ModuleSourceConfiguration +) => (jobSummary: JobSummary): boolean => { + if (!jobSummary.fullJob || !jobSummary.fullJob.custom_settings) { + return false; + } + + const jobConfiguration = jobSummary.fullJob.custom_settings.logs_source_config; + + return !( + jobConfiguration && + jobConfiguration.bucketSpan === bucketSpan && + jobConfiguration.indexPattern && + isSubset( + new Set(jobConfiguration.indexPattern.split(',')), + new Set(currentSourceConfiguration.indices) + ) && + jobConfiguration.timestampField === currentSourceConfiguration.timestampField + ); +}; + +const isSubset = (subset: Set, superset: Set) => { + return Array.from(subset).every(subsetElement => superset.has(subsetElement)); +}; diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_definition.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_definition.tsx new file mode 100644 index 0000000000000..ea0cd32c0b93e --- /dev/null +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_definition.tsx @@ -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 { useCallback, useMemo, useState } from 'react'; +import { getJobId } from '../../../../common/log_analysis'; +import { useTrackedPromise } from '../../../utils/use_tracked_promise'; +import { JobSummary } from './api/ml_get_jobs_summary_api'; +import { GetMlModuleResponsePayload, JobDefinition } from './api/ml_get_module'; +import { ModuleDescriptor, ModuleSourceConfiguration } from './log_analysis_module_types'; + +export const useLogAnalysisModuleDefinition = ({ + sourceConfiguration: { spaceId, sourceId }, + moduleDescriptor, +}: { + sourceConfiguration: ModuleSourceConfiguration; + moduleDescriptor: ModuleDescriptor; +}) => { + const [moduleDefinition, setModuleDefinition] = useState< + GetMlModuleResponsePayload | undefined + >(); + + const jobDefinitionByJobId = useMemo( + () => + moduleDefinition + ? moduleDefinition.jobs.reduce>( + (accumulatedJobDefinitions, jobDefinition) => ({ + ...accumulatedJobDefinitions, + [getJobId(spaceId, sourceId, jobDefinition.id)]: jobDefinition, + }), + {} + ) + : {}, + [moduleDefinition, sourceId, spaceId] + ); + + const [fetchModuleDefinitionRequest, fetchModuleDefinition] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + return await moduleDescriptor.getModuleDefinition(); + }, + onResolve: response => { + setModuleDefinition(response); + }, + onReject: () => { + setModuleDefinition(undefined); + }, + }, + [moduleDescriptor.getModuleDefinition, spaceId, sourceId] + ); + + const getIsJobDefinitionOutdated = useCallback( + (jobSummary: JobSummary): boolean => { + const jobDefinition: JobDefinition | undefined = jobDefinitionByJobId[jobSummary.id]; + + if (jobDefinition == null) { + return false; + } + + const currentRevision = jobDefinition?.config.custom_settings.job_revision; + return (jobSummary.fullJob?.custom_settings?.job_revision ?? 0) < (currentRevision ?? 0); + }, + [jobDefinitionByJobId] + ); + + return { + fetchModuleDefinition, + fetchModuleDefinitionRequestState: fetchModuleDefinitionRequest.state, + getIsJobDefinitionOutdated, + jobDefinitionByJobId, + moduleDefinition, + }; +}; diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx index cf248f419f6f2..b5530f9ebf72e 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx @@ -7,25 +7,21 @@ import { useReducer } from 'react'; import { - JobSourceConfiguration, JobStatus, - SetupStatus, getDatafeedId, getJobId, isJobStatusWithResults, + SetupStatus, } from '../../../../common/log_analysis'; import { FetchJobStatusResponsePayload, JobSummary } from './api/ml_get_jobs_summary_api'; -import { GetMlModuleResponsePayload, JobDefinition } from './api/ml_get_module'; import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; import { MandatoryProperty } from '../../../../common/utility_types'; interface StatusReducerState { - jobDefinitions: JobDefinition[]; jobStatus: Record; jobSummaries: JobSummary[]; lastSetupErrorMessages: string[]; setupStatus: SetupStatus; - sourceConfiguration: JobSourceConfiguration; } type StatusReducerAction = @@ -34,8 +30,9 @@ type StatusReducerAction = type: 'finishedSetup'; sourceId: string; spaceId: string; - jobs: SetupMlModuleResponsePayload['jobs']; - datafeeds: SetupMlModuleResponsePayload['datafeeds']; + jobSetupResults: SetupMlModuleResponsePayload['jobs']; + jobSummaries: FetchJobStatusResponsePayload; + datafeedSetupResults: SetupMlModuleResponsePayload['datafeeds']; } | { type: 'failedSetup' } | { type: 'fetchingJobStatuses' } @@ -46,32 +43,15 @@ type StatusReducerAction = payload: FetchJobStatusResponsePayload; } | { type: 'failedFetchingJobStatuses' } - | { type: 'fetchingModuleDefinition' } - | { - type: 'fetchedModuleDefinition'; - spaceId: string; - sourceId: string; - moduleDefinition: GetMlModuleResponsePayload; - } - | { type: 'failedFetchingModuleDefinition' } - | { - type: 'updatedSourceConfiguration'; - spaceId: string; - sourceId: string; - sourceConfiguration: JobSourceConfiguration; - } | { type: 'requestedJobConfigurationUpdate' } | { type: 'requestedJobDefinitionUpdate' } | { type: 'viewedResults' }; const createInitialState = ({ jobTypes, - sourceConfiguration, }: { jobTypes: JobType[]; - sourceConfiguration: JobSourceConfiguration; }): StatusReducerState => ({ - jobDefinitions: [], jobStatus: jobTypes.reduce( (accumulatedJobStatus, jobType) => ({ ...accumulatedJobStatus, @@ -81,8 +61,7 @@ const createInitialState = ({ ), jobSummaries: [], lastSetupErrorMessages: [], - setupStatus: 'initializing', - sourceConfiguration, + setupStatus: { type: 'initializing' }, }); const createStatusReducer = (jobTypes: JobType[]) => ( @@ -100,39 +79,44 @@ const createStatusReducer = (jobTypes: JobType[]) => ( }), {} as Record ), - setupStatus: 'pending', + setupStatus: { type: 'pending' }, }; } case 'finishedSetup': { - const { jobs, datafeeds, spaceId, sourceId } = action; + const { datafeedSetupResults, jobSetupResults, jobSummaries, spaceId, sourceId } = action; const nextJobStatus = jobTypes.reduce( (accumulatedJobStatus, jobType) => ({ ...accumulatedJobStatus, [jobType]: - hasSuccessfullyCreatedJob(getJobId(spaceId, sourceId, jobType))(jobs) && - hasSuccessfullyStartedDatafeed(getDatafeedId(spaceId, sourceId, jobType))(datafeeds) + hasSuccessfullyCreatedJob(getJobId(spaceId, sourceId, jobType))(jobSetupResults) && + hasSuccessfullyStartedDatafeed(getDatafeedId(spaceId, sourceId, jobType))( + datafeedSetupResults + ) ? 'started' : 'failed', }), {} as Record ); - const nextSetupStatus = Object.values(nextJobStatus).every( + const nextSetupStatus: SetupStatus = Object.values(nextJobStatus).every( jobState => jobState === 'started' ) - ? 'succeeded' - : 'failed'; - const nextErrorMessages = [ - ...Object.values(datafeeds) - .filter(hasError) - .map(datafeed => datafeed.error.msg), - ...Object.values(jobs) - .filter(hasError) - .map(job => job.error.msg), - ]; + ? { type: 'succeeded' } + : { + type: 'failed', + reasons: [ + ...Object.values(datafeedSetupResults) + .filter(hasError) + .map(datafeed => datafeed.error.msg), + ...Object.values(jobSetupResults) + .filter(hasError) + .map(job => job.error.msg), + ], + }; + return { ...state, jobStatus: nextJobStatus, - lastSetupErrorMessages: nextErrorMessages, + jobSummaries, setupStatus: nextSetupStatus, }; } @@ -146,19 +130,19 @@ const createStatusReducer = (jobTypes: JobType[]) => ( }), {} as Record ), - setupStatus: 'failed', + setupStatus: { type: 'failed', reasons: ['unknown'] }, }; } - case 'fetchingModuleDefinition': case 'fetchingJobStatuses': { return { ...state, - setupStatus: state.setupStatus === 'unknown' ? 'initializing' : state.setupStatus, + setupStatus: + state.setupStatus.type === 'unknown' ? { type: 'initializing' } : state.setupStatus, }; } case 'fetchedJobStatuses': { const { payload: jobSummaries, spaceId, sourceId } = action; - const { jobDefinitions, setupStatus, sourceConfiguration } = state; + const { setupStatus } = state; const nextJobStatus = jobTypes.reduce( (accumulatedJobStatus, jobType) => ({ @@ -167,14 +151,7 @@ const createStatusReducer = (jobTypes: JobType[]) => ( }), {} as Record ); - const nextSetupStatus = getSetupStatus( - spaceId, - sourceId, - sourceConfiguration, - nextJobStatus, - jobDefinitions, - jobSummaries - )(setupStatus); + const nextSetupStatus = getSetupStatus(nextJobStatus)(setupStatus); return { ...state, @@ -186,7 +163,7 @@ const createStatusReducer = (jobTypes: JobType[]) => ( case 'failedFetchingJobStatuses': { return { ...state, - setupStatus: 'unknown', + setupStatus: { type: 'unknown' }, jobStatus: jobTypes.reduce( (accumulatedJobStatus, jobType) => ({ ...accumulatedJobStatus, @@ -196,60 +173,22 @@ const createStatusReducer = (jobTypes: JobType[]) => ( ), }; } - case 'fetchedModuleDefinition': { - const { spaceId, sourceId, moduleDefinition } = action; - const { jobStatus, jobSummaries, setupStatus, sourceConfiguration } = state; - - const nextSetupStatus = getSetupStatus( - spaceId, - sourceId, - sourceConfiguration, - jobStatus, - moduleDefinition.jobs, - jobSummaries - )(setupStatus); - - return { - ...state, - jobDefinitions: moduleDefinition.jobs, - setupStatus: nextSetupStatus, - }; - } - case 'updatedSourceConfiguration': { - const { spaceId, sourceId, sourceConfiguration } = action; - const { jobDefinitions, jobStatus, jobSummaries, setupStatus } = state; - - const nextSetupStatus = getSetupStatus( - spaceId, - sourceId, - sourceConfiguration, - jobStatus, - jobDefinitions, - jobSummaries - )(setupStatus); - - return { - ...state, - setupStatus: nextSetupStatus, - sourceConfiguration, - }; - } case 'requestedJobConfigurationUpdate': { return { ...state, - setupStatus: 'requiredForReconfiguration', + setupStatus: { type: 'required', reason: 'reconfiguration' }, }; } case 'requestedJobDefinitionUpdate': { return { ...state, - setupStatus: 'requiredForUpdate', + setupStatus: { type: 'required', reason: 'update' }, }; } case 'viewedResults': { return { ...state, - setupStatus: 'hiddenAfterSuccess', + setupStatus: { type: 'skipped', newlyCreated: true }, }; } default: { @@ -307,100 +246,28 @@ const getJobStatus = (jobId: string) => (jobSummaries: FetchJobStatusResponsePay } )[0] || 'missing'; -const getSetupStatus = ( - spaceId: string, - sourceId: string, - sourceConfiguration: JobSourceConfiguration, - everyJobStatus: Record, - jobDefinitions: JobDefinition[], - jobSummaries: JobSummary[] -) => (previousSetupStatus: SetupStatus) => - Object.entries(everyJobStatus).reduce( - (setupStatus, [jobType, jobStatus]) => { - const jobId = getJobId(spaceId, sourceId, jobType); - const jobDefinition = jobDefinitions.find(({ id }) => id === jobType); - - if (jobStatus === 'missing') { - return 'required'; - } else if ( - setupStatus === 'required' || - setupStatus === 'requiredForUpdate' || - setupStatus === 'requiredForReconfiguration' - ) { - return setupStatus; - } else if ( - jobDefinition && - !isJobRevisionCurrent( - jobId, - jobDefinition.config.custom_settings.job_revision || 0 - )(jobSummaries) - ) { - return 'skippedButUpdatable'; - } else if (!isJobConfigurationConsistent(jobId, sourceConfiguration)(jobSummaries)) { - return 'skippedButReconfigurable'; - } else if (setupStatus === 'hiddenAfterSuccess') { - return setupStatus; - } else if (setupStatus === 'skipped' || isJobStatusWithResults(jobStatus)) { - return 'skipped'; - } - +const getSetupStatus = (everyJobStatus: Record) => ( + previousSetupStatus: SetupStatus +): SetupStatus => + Object.entries(everyJobStatus).reduce((setupStatus, [, jobStatus]) => { + if (jobStatus === 'missing') { + return { type: 'required', reason: 'missing' }; + } else if (setupStatus.type === 'required') { return setupStatus; - }, - previousSetupStatus - ); - -const isJobRevisionCurrent = (jobId: string, currentRevision: number) => ( - jobSummaries: FetchJobStatusResponsePayload -): boolean => - jobSummaries - .filter(jobSummary => jobSummary.id === jobId) - .every( - jobSummary => (jobSummary?.fullJob?.custom_settings?.job_revision ?? 0) >= currentRevision - ); - -const isJobConfigurationConsistent = ( - jobId: string, - sourceConfiguration: { - bucketSpan: number; - indexPattern: string; - timestampField: string; - } -) => (jobSummaries: FetchJobStatusResponsePayload): boolean => - jobSummaries - .filter(jobSummary => jobSummary.id === jobId) - .every(jobSummary => { - if (!jobSummary.fullJob || !jobSummary.fullJob.custom_settings) { - return false; - } - - const jobConfiguration = jobSummary.fullJob.custom_settings.logs_source_config; - - return ( - jobConfiguration && - jobConfiguration.bucketSpan === sourceConfiguration.bucketSpan && - jobConfiguration.indexPattern && - isIndexPatternSubset(jobConfiguration.indexPattern, sourceConfiguration.indexPattern) && - jobConfiguration.timestampField === sourceConfiguration.timestampField - ); - }); - -const isIndexPatternSubset = (indexPatternSubset: string, indexPatternSuperset: string) => { - const subsetSubPatterns = indexPatternSubset.split(','); - const supersetSubPatterns = new Set(indexPatternSuperset.split(',')); + } else if (setupStatus.type === 'skipped' || isJobStatusWithResults(jobStatus)) { + return { + type: 'skipped', + // preserve newlyCreated status + newlyCreated: setupStatus.type === 'skipped' && setupStatus.newlyCreated, + }; + } - return subsetSubPatterns.every(subPattern => supersetSubPatterns.has(subPattern)); -}; + return setupStatus; + }, previousSetupStatus); const hasError = (value: Value): value is MandatoryProperty => value.error != null; -export const useModuleStatus = ( - jobTypes: JobType[], - sourceConfiguration: JobSourceConfiguration -) => { - return useReducer( - createStatusReducer(jobTypes), - { jobTypes, sourceConfiguration }, - createInitialState - ); +export const useModuleStatus = (jobTypes: JobType[]) => { + return useReducer(createStatusReducer(jobTypes), { jobTypes }, createInitialState); }; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx index d1c10820f7c9c..ed1aa9e72ebae 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx @@ -37,18 +37,13 @@ export const LogEntryCategoriesPageContent = () => { hasLogAnalysisSetupCapabilities, } = useLogAnalysisCapabilitiesContext(); - const { - fetchJobStatus, - fetchModuleDefinition, - setupStatus, - } = useLogEntryCategoriesModuleContext(); + const { fetchJobStatus, setupStatus } = useLogEntryCategoriesModuleContext(); useEffect(() => { if (hasLogAnalysisReadCapabilities) { - fetchModuleDefinition(); fetchJobStatus(); } - }, [fetchJobStatus, fetchModuleDefinition, hasLogAnalysisReadCapabilities]); + }, [fetchJobStatus, hasLogAnalysisReadCapabilities]); if (isLoadingSource || isUninitialized) { return ; @@ -58,7 +53,7 @@ export const LogEntryCategoriesPageContent = () => { return ; } else if (!hasLogAnalysisReadCapabilities) { return ; - } else if (setupStatus === 'initializing') { + } else if (setupStatus.type === 'initializing') { return ( { })} /> ); - } else if (setupStatus === 'unknown') { + } else if (setupStatus.type === 'unknown') { return ; } else if (isSetupStatusWithResults(setupStatus)) { return ; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx index b783aa9c79007..e304a8835e99c 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx @@ -9,17 +9,11 @@ import { EuiFlexGroup, EuiFlexItem, EuiPage, EuiPanel, EuiSuperDatePicker } from import { i18n } from '@kbn/i18n'; import moment from 'moment'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; - import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled, useTrackPageview } from '../../../../../observability/public'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; -import { - LogAnalysisJobProblemIndicator, - jobHasProblem, -} from '../../../components/logging/log_analysis_job_status'; -import { FirstUseCallout } from '../../../components/logging/log_analysis_results'; import { useInterval } from '../../../hooks/use_interval'; -import { useTrackPageview } from '../../../../../observability/public'; +import { CategoryJobNoticesSection } from './sections/notices/notices_section'; import { TopCategoriesSection } from './sections/top_categories'; import { useLogEntryCategoriesModuleContext } from './use_log_entry_categories_module'; import { useLogEntryCategoriesResults } from './use_log_entry_categories_results'; @@ -36,11 +30,15 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent = () => { const { fetchJobStatus, - jobStatus, + fetchModuleDefinition, setupStatus, viewSetupForReconfiguration, viewSetupForUpdate, + hasOutdatedJobConfigurations, + hasOutdatedJobDefinitions, + hasStoppedJobs, jobIds, + categoryQualityWarnings, sourceConfiguration: { sourceId }, } = useLogEntryCategoriesModuleContext(); @@ -124,12 +122,15 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent = () => { [setAutoRefresh] ); - const isFirstUse = useMemo(() => setupStatus === 'hiddenAfterSuccess', [setupStatus]); - const hasResults = useMemo(() => topLogEntryCategories.length > 0, [ topLogEntryCategories.length, ]); + const isFirstUse = useMemo( + () => setupStatus.type === 'skipped' && !!setupStatus.newlyCreated && !hasResults, + [hasResults, setupStatus] + ); + useEffect(() => { getTopLogEntryCategories(); }, [getTopLogEntryCategories, categoryQueryDatasets, categoryQueryTimeRange.lastChangedTime]); @@ -138,6 +139,10 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent = () => { getLogEntryCategoryDatasets(); }, [getLogEntryCategoryDatasets, categoryQueryTimeRange.lastChangedTime]); + useEffect(() => { + fetchModuleDefinition(); + }, [fetchModuleDefinition]); + useInterval(() => { fetchJobStatus(); }, JOB_STATUS_POLLING_INTERVAL); @@ -172,21 +177,17 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent = () => {
- {jobHasProblem(jobStatus['log-entry-categories-count'], setupStatus) ? ( - - - - ) : null} - {isFirstUse && !hasResults ? ( - - - - ) : null} + + + void; + onRecreateMlJobForUpdate: () => void; + qualityWarnings: QualityWarning[]; +}> = ({ + hasOutdatedJobConfigurations, + hasOutdatedJobDefinitions, + hasStoppedJobs, + isFirstUse, + onRecreateMlJobForReconfiguration, + onRecreateMlJobForUpdate, + qualityWarnings, +}) => ( + <> + + + +); diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/notices/quality_warning_notices.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/notices/quality_warning_notices.tsx new file mode 100644 index 0000000000000..73b6b88db873a --- /dev/null +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/notices/quality_warning_notices.tsx @@ -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 { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; +import { CategoryQualityWarningReason, QualityWarning } from './quality_warnings'; + +export const CategoryQualityWarnings: React.FC<{ qualityWarnings: QualityWarning[] }> = ({ + qualityWarnings, +}) => ( + <> + {qualityWarnings.map((qualityWarning, qualityWarningIndex) => ( + +

+ +

+
    + {qualityWarning.reasons.map((reason, reasonIndex) => ( +
  • + +
  • + ))} +
+
+ ))} + +); + +const categoryQualityWarningCalloutTitle = i18n.translate( + 'xpack.infra.logs.logEntryCategories.categoryQUalityWarningCalloutTitle', + { + defaultMessage: 'Quality warning', + } +); + +const CategoryQualityWarningReasonDescription: React.FC<{ + reason: CategoryQualityWarningReason; +}> = ({ reason }) => { + switch (reason.type) { + case 'singleCategory': + return ( + + ); + case 'manyRareCategories': + return ( + + ); + case 'manyCategories': + return ( + + ); + case 'noFrequentCategories': + return ( + + ); + case 'manyDeadCategories': + return ( + + ); + } +}; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/notices/quality_warnings.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/notices/quality_warnings.tsx new file mode 100644 index 0000000000000..e0d3aa105e004 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/notices/quality_warnings.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +interface ManyCategoriesWarningReason { + type: 'manyCategories'; + categoriesDocumentRatio: number; +} + +interface ManyDeadCategoriesWarningReason { + type: 'manyDeadCategories'; + deadCategoriesRatio: number; +} + +interface ManyRareCategoriesWarningReason { + type: 'manyRareCategories'; + rareCategoriesRatio: number; +} + +interface NoFrequentCategoriesWarningReason { + type: 'noFrequentCategories'; +} + +interface SingleCategoryWarningReason { + type: 'singleCategory'; +} + +export type CategoryQualityWarningReason = + | ManyCategoriesWarningReason + | ManyDeadCategoriesWarningReason + | ManyRareCategoriesWarningReason + | NoFrequentCategoriesWarningReason + | SingleCategoryWarningReason; + +export type CategoryQualityWarningReasonType = CategoryQualityWarningReason['type']; + +export interface CategoryQualityWarning { + type: 'categoryQualityWarning'; + jobId: string; + reasons: CategoryQualityWarningReason[]; +} + +export type QualityWarning = CategoryQualityWarning; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx index 023082154565c..3855706bb6d47 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx @@ -34,12 +34,7 @@ export const CategoryExampleMessage: React.FunctionComponent<{ return ( - + @@ -63,8 +56,6 @@ export const CategoryExampleMessage: React.FunctionComponent<{ highlights: [], }} highlights={noHighlights} - isHovered={false} - isHighlighted={false} isActiveHighlight={false} wrapMode="none" /> diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx index 918c252f6350c..2917719af283b 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx @@ -6,12 +6,14 @@ import createContainer from 'constate'; import { useMemo } from 'react'; - import { - useLogAnalysisModule, ModuleSourceConfiguration, + useLogAnalysisModule, + useLogAnalysisModuleConfiguration, + useLogAnalysisModuleDefinition, } from '../../../containers/logs/log_analysis'; import { logEntryCategoriesModule } from './module_descriptor'; +import { useLogEntryCategoriesQuality } from './use_log_entry_categories_quality'; export const useLogEntryCategoriesModule = ({ indexPattern, @@ -34,10 +36,51 @@ export const useLogEntryCategoriesModule = ({ [indexPattern, sourceId, spaceId, timestampField] ); - return useLogAnalysisModule({ + const logAnalysisModule = useLogAnalysisModule({ + moduleDescriptor: logEntryCategoriesModule, + sourceConfiguration, + }); + + const { getIsJobConfigurationOutdated } = useLogAnalysisModuleConfiguration({ + sourceConfiguration, moduleDescriptor: logEntryCategoriesModule, + }); + + const { fetchModuleDefinition, getIsJobDefinitionOutdated } = useLogAnalysisModuleDefinition({ sourceConfiguration, + moduleDescriptor: logEntryCategoriesModule, + }); + + const { categoryQualityWarnings } = useLogEntryCategoriesQuality({ + jobSummaries: logAnalysisModule.jobSummaries, }); + + const hasOutdatedJobConfigurations = useMemo( + () => logAnalysisModule.jobSummaries.some(getIsJobConfigurationOutdated), + [getIsJobConfigurationOutdated, logAnalysisModule.jobSummaries] + ); + + const hasOutdatedJobDefinitions = useMemo( + () => logAnalysisModule.jobSummaries.some(getIsJobDefinitionOutdated), + [getIsJobDefinitionOutdated, logAnalysisModule.jobSummaries] + ); + + const hasStoppedJobs = useMemo( + () => + Object.values(logAnalysisModule.jobStatus).some( + currentJobStatus => currentJobStatus === 'stopped' + ), + [logAnalysisModule.jobStatus] + ); + + return { + ...logAnalysisModule, + categoryQualityWarnings, + fetchModuleDefinition, + hasOutdatedJobConfigurations, + hasOutdatedJobDefinitions, + hasStoppedJobs, + }; }; export const [ diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_quality.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_quality.ts new file mode 100644 index 0000000000000..031479e66386f --- /dev/null +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_quality.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useMemo } from 'react'; + +import { JobModelSizeStats, JobSummary } from '../../../containers/logs/log_analysis'; +import { QualityWarning, CategoryQualityWarningReason } from './sections/notices/quality_warnings'; + +export const useLogEntryCategoriesQuality = ({ jobSummaries }: { jobSummaries: JobSummary[] }) => { + const categoryQualityWarnings: QualityWarning[] = useMemo( + () => + jobSummaries + .filter( + jobSummary => jobSummary.fullJob?.model_size_stats?.categorization_status === 'warn' + ) + .map(jobSummary => ({ + type: 'categoryQualityWarning', + jobId: jobSummary.id, + reasons: jobSummary.fullJob?.model_size_stats + ? getCategoryQualityWarningReasons(jobSummary.fullJob.model_size_stats) + : [], + })), + [jobSummaries] + ); + + return { + categoryQualityWarnings, + }; +}; + +const getCategoryQualityWarningReasons = ({ + categorized_doc_count: categorizedDocCount, + dead_category_count: deadCategoryCount, + frequent_category_count: frequentCategoryCount, + rare_category_count: rareCategoryCount, + total_category_count: totalCategoryCount, +}: JobModelSizeStats): CategoryQualityWarningReason[] => { + const rareCategoriesRatio = rareCategoryCount / totalCategoryCount; + const categoriesDocumentRatio = totalCategoryCount / categorizedDocCount; + const deadCategoriesRatio = deadCategoryCount / totalCategoryCount; + + return [ + ...(totalCategoryCount === 1 + ? [ + { + type: 'singleCategory' as const, + }, + ] + : []), + ...(rareCategoriesRatio >= 0.9 + ? [ + { + type: 'manyRareCategories' as const, + rareCategoriesRatio, + }, + ] + : []), + ...(categorizedDocCount > 100 && categoriesDocumentRatio >= 0.5 + ? [ + { + type: 'manyCategories' as const, + categoriesDocumentRatio, + }, + ] + : []), + ...(frequentCategoryCount === 0 + ? [ + { + type: 'noFrequentCategories' as const, + }, + ] + : []), + ...(deadCategoriesRatio >= 0.5 + ? [ + { + type: 'manyDeadCategories' as const, + deadCategoriesRatio, + }, + ] + : []), + ]; +}; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx index fe12ca5455a75..2f34e62d8e611 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx @@ -37,14 +37,13 @@ export const LogEntryRatePageContent = () => { hasLogAnalysisSetupCapabilities, } = useLogAnalysisCapabilitiesContext(); - const { fetchJobStatus, fetchModuleDefinition, setupStatus } = useLogEntryRateModuleContext(); + const { fetchJobStatus, setupStatus } = useLogEntryRateModuleContext(); useEffect(() => { if (hasLogAnalysisReadCapabilities) { - fetchModuleDefinition(); fetchJobStatus(); } - }, [fetchJobStatus, fetchModuleDefinition, hasLogAnalysisReadCapabilities]); + }, [fetchJobStatus, hasLogAnalysisReadCapabilities]); if (isLoadingSource || isUninitialized) { return ; @@ -54,7 +53,7 @@ export const LogEntryRatePageContent = () => { return ; } else if (!hasLogAnalysisReadCapabilities) { return ; - } else if (setupStatus === 'initializing') { + } else if (setupStatus.type === 'initializing') { return ( { })} /> ); - } else if (setupStatus === 'unknown') { + } else if (setupStatus.type === 'unknown') { return ; } else if (isSetupStatusWithResults(setupStatus)) { return ; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx index 50d58865e9746..156c9a919440e 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx @@ -11,21 +11,19 @@ import { EuiFlexItem, EuiPage, EuiPanel, - EuiSpacer, EuiSuperDatePicker, EuiText, } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment'; -import React, { useCallback, useMemo, useState, useEffect } from 'react'; - -import { euiStyled } from '../../../../../observability/public'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { euiStyled, useTrackPageview } from '../../../../../observability/public'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { bucketSpan } from '../../../../common/log_analysis'; import { LoadingOverlayWrapper } from '../../../components/loading_overlay_wrapper'; +import { LogAnalysisJobProblemIndicator } from '../../../components/logging/log_analysis_job_status'; import { useInterval } from '../../../hooks/use_interval'; -import { useTrackPageview } from '../../../../../observability/public'; import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting'; import { AnomaliesResults } from './sections/anomalies'; import { LogRateResults } from './sections/log_rate'; @@ -35,7 +33,6 @@ import { StringTimeRange, useLogAnalysisResultsUrlState, } from './use_log_entry_rate_results_url_state'; -import { FirstUseCallout } from '../../../components/logging/log_analysis_results'; const JOB_STATUS_POLLING_INTERVAL = 30000; @@ -47,10 +44,13 @@ export const LogEntryRateResultsContent: React.FunctionComponent = () => { const { fetchJobStatus, - jobStatus, + fetchModuleDefinition, setupStatus, viewSetupForReconfiguration, viewSetupForUpdate, + hasOutdatedJobConfigurations, + hasOutdatedJobDefinitions, + hasStoppedJobs, jobIds, sourceConfiguration: { sourceId }, } = useLogEntryRateModuleContext(); @@ -82,10 +82,6 @@ export const LogEntryRateResultsContent: React.FunctionComponent = () => { bucketDuration, }); - const hasResults = useMemo(() => (logEntryRate?.histogramBuckets?.length ?? 0) > 0, [ - logEntryRate, - ]); - const handleQueryTimeRangeChange = useCallback( ({ start: startTime, end: endTime }: { start: string; end: string }) => { setQueryTimeRange({ @@ -131,12 +127,23 @@ export const LogEntryRateResultsContent: React.FunctionComponent = () => { [setAutoRefresh] ); - const isFirstUse = useMemo(() => setupStatus === 'hiddenAfterSuccess', [setupStatus]); + const hasResults = useMemo(() => (logEntryRate?.histogramBuckets?.length ?? 0) > 0, [ + logEntryRate, + ]); + + const isFirstUse = useMemo( + () => setupStatus.type === 'skipped' && !!setupStatus.newlyCreated && !hasResults, + [hasResults, setupStatus] + ); useEffect(() => { getLogEntryRate(); }, [getLogEntryRate, queryTimeRange.lastChangedTime]); + useEffect(() => { + fetchModuleDefinition(); + }, [fetchModuleDefinition]); + useInterval(() => { fetchJobStatus(); }, JOB_STATUS_POLLING_INTERVAL); @@ -195,14 +202,18 @@ export const LogEntryRateResultsContent: React.FunctionComponent = () => {
+ + + - {isFirstUse && !hasResults ? ( - <> - - - - ) : null} { diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx index 2551170c44f4e..e216162d7a295 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx @@ -20,7 +20,7 @@ import React, { useMemo } from 'react'; import { euiStyled } from '../../../../../../../observability/public'; import { LogEntryRateResults } from '../../use_log_entry_rate_results'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; -import { formatAnomalyScore, JobStatus, SetupStatus } from '../../../../../../common/log_analysis'; +import { formatAnomalyScore } from '../../../../../../common/log_analysis'; import { getAnnotationsForAll, getLogEntryRateCombinedSeries, @@ -28,34 +28,18 @@ import { } from '../helpers/data_formatters'; import { AnomaliesChart } from './chart'; import { AnomaliesTable } from './table'; -import { - LogAnalysisJobProblemIndicator, - RecreateJobButton, -} from '../../../../../components/logging/log_analysis_job_status'; +import { RecreateJobButton } from '../../../../../components/logging/log_analysis_job_status'; import { AnalyzeInMlButton } from '../../../../../components/logging/log_analysis_results'; import { LoadingOverlayWrapper } from '../../../../../components/loading_overlay_wrapper'; export const AnomaliesResults: React.FunctionComponent<{ isLoading: boolean; - jobStatus: JobStatus; results: LogEntryRateResults | null; setTimeRange: (timeRange: TimeRange) => void; - setupStatus: SetupStatus; timeRange: TimeRange; viewSetupForReconfiguration: () => void; - viewSetupForUpdate: () => void; jobId: string; -}> = ({ - isLoading, - jobStatus, - results, - setTimeRange, - setupStatus, - timeRange, - viewSetupForReconfiguration, - viewSetupForUpdate, - jobId, -}) => { +}> = ({ isLoading, results, setTimeRange, timeRange, viewSetupForReconfiguration, jobId }) => { const hasAnomalies = useMemo(() => { return results && results.histogramBuckets ? results.histogramBuckets.some(bucket => { @@ -100,20 +84,13 @@ export const AnomaliesResults: React.FunctionComponent<{ - + - - }> {!results || (results && results.histogramBuckets && !results.histogramBuckets.length) ? ( logAnalysisModule.jobSummaries.some(getIsJobConfigurationOutdated), + [getIsJobConfigurationOutdated, logAnalysisModule.jobSummaries] + ); + + const hasOutdatedJobDefinitions = useMemo( + () => logAnalysisModule.jobSummaries.some(getIsJobDefinitionOutdated), + [getIsJobDefinitionOutdated, logAnalysisModule.jobSummaries] + ); + + const hasStoppedJobs = useMemo( + () => + Object.values(logAnalysisModule.jobStatus).some( + currentJobStatus => currentJobStatus === 'stopped' + ), + [logAnalysisModule.jobStatus] + ); + + return { + ...logAnalysisModule, + fetchModuleDefinition, + hasOutdatedJobConfigurations, + hasOutdatedJobDefinitions, + hasStoppedJobs, + }; }; export const [LogEntryRateModuleProvider, useLogEntryRateModuleContext] = createContainer( diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts index a88b1d9049c76..528b9a69327fa 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts +++ b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts @@ -49,6 +49,8 @@ export interface LogEntriesAroundParams { export const LOG_ENTRIES_PAGE_SIZE = 200; +const FIELDS_FROM_CONTEXT = ['log.file.path', 'host.name', 'container.id'] as const; + export class InfraLogEntriesDomain { constructor( private readonly adapter: LogEntriesAdapter, @@ -154,6 +156,14 @@ export class InfraLogEntriesDomain { } } ), + context: FIELDS_FROM_CONTEXT.reduce((ctx, field) => { + // Users might have different types here in their mappings. + const value = doc.fields[field]; + if (typeof value === 'string') { + ctx[field] = value; + } + return ctx; + }, {}), }; }); @@ -329,7 +339,9 @@ const getRequiredFields = ( ); const fieldsFromFormattingRules = messageFormattingRules.requiredFields; - return Array.from(new Set([...fieldsFromCustomColumns, ...fieldsFromFormattingRules])); + return Array.from( + new Set([...fieldsFromCustomColumns, ...fieldsFromFormattingRules, ...FIELDS_FROM_CONTEXT]) + ); }; const createHighlightQueryDsl = (phrase: string, fields: string[]) => ({ diff --git a/x-pack/plugins/ingest_manager/CHANGELOG.md b/x-pack/plugins/ingest_manager/CHANGELOG.md new file mode 100644 index 0000000000000..b336c65b7c4b9 --- /dev/null +++ b/x-pack/plugins/ingest_manager/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog +Significant or breaking changes to the Ingest Manager API will be documented in this file + +## 2020-03-30 + +### Breaking Changes +* Change EPM file path route from epm/packages/{pkgkey}/{filePath*} to epm/packages/{packageName}/{packageVersion}/{filePath*} [#61910](https://github.com/elastic/kibana/pull/61910) \ No newline at end of file diff --git a/x-pack/plugins/ingest_manager/common/constants/routes.ts b/x-pack/plugins/ingest_manager/common/constants/routes.ts index 5bf7c910168c0..a31d38a723c2c 100644 --- a/x-pack/plugins/ingest_manager/common/constants/routes.ts +++ b/x-pack/plugins/ingest_manager/common/constants/routes.ts @@ -13,12 +13,13 @@ export const FLEET_API_ROOT = `${API_ROOT}/fleet`; // EPM API routes const EPM_PACKAGES_MANY = `${EPM_API_ROOT}/packages`; const EPM_PACKAGES_ONE = `${EPM_PACKAGES_MANY}/{pkgkey}`; +const EPM_PACKAGES_FILE = `${EPM_PACKAGES_MANY}/{pkgName}/{pkgVersion}`; export const EPM_API_ROUTES = { LIST_PATTERN: EPM_PACKAGES_MANY, INFO_PATTERN: EPM_PACKAGES_ONE, INSTALL_PATTERN: EPM_PACKAGES_ONE, DELETE_PATTERN: EPM_PACKAGES_ONE, - FILEPATH_PATTERN: `${EPM_PACKAGES_ONE}/{filePath*}`, + FILEPATH_PATTERN: `${EPM_PACKAGES_FILE}/{filePath*}`, CATEGORIES_PATTERN: `${EPM_API_ROOT}/categories`, }; diff --git a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts index 8623d02e72862..48f37a4d65ac6 100644 --- a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts @@ -75,8 +75,8 @@ export const getFileHandler: RequestHandler { try { - const { pkgkey, filePath } = request.params; - const registryResponse = await getFile(`/package/${pkgkey}/${filePath}`); + const { pkgName, pkgVersion, filePath } = request.params; + const registryResponse = await getFile(`/package/${pkgName}/${pkgVersion}/${filePath}`); const contentType = registryResponse.headers.get('Content-Type'); const customResponseObj: CustomHttpResponseOptions = { body: registryResponse.body, diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.test.ts index 5153f9205dde7..6d5ca036aeb13 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.test.ts @@ -11,19 +11,18 @@ const tests = [ { package: { assets: [ - '/package/coredns-1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', - '/package/coredns-1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-json.json', + '/package/coredns/1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', + '/package/coredns/1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-json.json', ], - name: 'coredns', - version: '1.0.1', + path: '/package/coredns/1.0.1', }, dataset: 'log', filter: (path: string) => { return true; }, expected: [ - '/package/coredns-1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', - '/package/coredns-1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-json.json', + '/package/coredns/1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', + '/package/coredns/1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-json.json', ], }, { @@ -32,8 +31,7 @@ const tests = [ '/package/coredns-1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', '/package/coredns-1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-json.json', ], - name: 'coredns', - version: '1.0.1', + path: '/package/coredns/1.0.1', }, // Non existant dataset dataset: 'foo', diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.ts index e36c2de1b4e80..d7a5c5569986e 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.ts @@ -9,14 +9,16 @@ import * as Registry from '../registry'; import { cacheHas } from '../registry/cache'; // paths from RegistryPackage are routes to the assets on EPR -// e.g. `/package/nginx-1.2.0/dataset/access/fields/fields.yml` +// e.g. `/package/nginx/1.2.0/dataset/access/fields/fields.yml` // paths for ArchiveEntry are routes to the assets in the archive // e.g. `nginx-1.2.0/dataset/access/fields/fields.yml` // RegistryPackage paths have a `/package/` prefix compared to ArchiveEntry paths +// and different package and version structure const EPR_PATH_PREFIX = '/package'; function registryPathToArchivePath(registryPath: RegistryPackage['path']): string { - const archivePath = registryPath.replace(`${EPR_PATH_PREFIX}/`, ''); - return archivePath; + const path = registryPath.replace(`${EPR_PATH_PREFIX}/`, ''); + const [pkgName, pkgVersion] = path.split('/'); + return path.replace(`${pkgName}/${pkgVersion}`, `${pkgName}-${pkgVersion}`); } export function getAssets( @@ -35,7 +37,7 @@ export function getAssets( // if dataset, filter for them if (datasetName) { - const comparePath = `${EPR_PATH_PREFIX}/${packageInfo.name}-${packageInfo.version}/dataset/${datasetName}/`; + const comparePath = `${packageInfo.path}/dataset/${datasetName}/`; if (!path.includes(comparePath)) { continue; } diff --git a/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts b/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts index 7c315f7616e1f..36a04b88bba29 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts @@ -6,7 +6,6 @@ import { Response } from 'node-fetch'; import { URL } from 'url'; -import { sortBy } from 'lodash'; import { AssetParts, AssetsGroupedByServiceByType, @@ -51,11 +50,7 @@ export async function fetchFindLatestPackage( const res = await fetchUrl(url.toString()); const searchResults = JSON.parse(res); if (searchResults.length) { - // sort by version, then get the last (most recent) - const latestPackage = sortBy(searchResults, ['version'])[ - searchResults.length - 1 - ]; - return latestPackage; + return searchResults[0]; } else { throw new Error('package not found'); } @@ -63,7 +58,8 @@ export async function fetchFindLatestPackage( export async function fetchInfo(pkgkey: string): Promise { const registryUrl = appContextService.getConfig()?.epm.registryUrl; - return fetchUrl(`${registryUrl}/package/${pkgkey}`).then(JSON.parse); + // change pkg-version to pkg/version + return fetchUrl(`${registryUrl}/package/${pkgkey.replace('-', '/')}`).then(JSON.parse); } export async function fetchFile(filePath: string): Promise { diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts index 2ca83276b0228..3ed6ee553a507 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts @@ -13,7 +13,8 @@ export const GetPackagesRequestSchema = { export const GetFileRequestSchema = { params: schema.object({ - pkgkey: schema.string(), + pkgName: schema.string(), + pkgVersion: schema.string(), filePath: schema.string(), }), }; diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 814825483d0dd..30a3350ad754e 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -90,16 +90,16 @@ export const FEATURE_VISIBLE_PROPERTY_NAME = '__kbn_isvisibleduetojoin__'; export const MB_SOURCE_ID_LAYER_ID_PREFIX_DELIMITER = '_'; -export const ES_GEO_FIELD_TYPE = { - GEO_POINT: 'geo_point', - GEO_SHAPE: 'geo_shape', -}; +export enum ES_GEO_FIELD_TYPE { + GEO_POINT = 'geo_point', + GEO_SHAPE = 'geo_shape', +} -export const ES_SPATIAL_RELATIONS = { - INTERSECTS: 'INTERSECTS', - DISJOINT: 'DISJOINT', - WITHIN: 'WITHIN', -}; +export enum ES_SPATIAL_RELATIONS { + INTERSECTS = 'INTERSECTS', + DISJOINT = 'DISJOINT', + WITHIN = 'WITHIN', +} export const GEO_JSON_TYPE = { POINT: 'Point', @@ -120,11 +120,11 @@ export const EMPTY_FEATURE_COLLECTION = { features: [], }; -export const DRAW_TYPE = { - BOUNDS: 'BOUNDS', - DISTANCE: 'DISTANCE', - POLYGON: 'POLYGON', -}; +export enum DRAW_TYPE { + BOUNDS = 'BOUNDS', + DISTANCE = 'DISTANCE', + POLYGON = 'POLYGON', +} export enum AGG_TYPE { AVG = 'avg', diff --git a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.d.ts b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.d.ts index a0102a4249a59..ca0e474491780 100644 --- a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.d.ts +++ b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.d.ts @@ -5,21 +5,14 @@ */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import { Query } from './map_descriptor'; - -type Extent = { - maxLat: number; - maxLon: number; - minLat: number; - minLon: number; -}; +import { MapExtent, MapQuery } from './map_descriptor'; // Global map state passed to every layer. export type MapFilters = { - buffer: Extent; // extent with additional buffer - extent: Extent; // map viewport + buffer: MapExtent; // extent with additional buffer + extent: MapExtent; // map viewport filters: unknown[]; - query: Query; + query: MapQuery; refreshTimerLastTriggeredAt: string; timeFilters: unknown; zoom: number; @@ -29,14 +22,14 @@ export type VectorSourceRequestMeta = MapFilters & { applyGlobalQuery: boolean; fieldNames: string[]; geogridPrecision: number; - sourceQuery: Query; + sourceQuery: MapQuery; sourceMeta: unknown; }; export type VectorStyleRequestMeta = MapFilters & { dynamicStyleFields: string[]; isTimeAware: boolean; - sourceQuery: Query; + sourceQuery: MapQuery; timeFilters: unknown; }; diff --git a/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts b/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts index 570398e37c5d4..b2a4c6b85a856 100644 --- a/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts +++ b/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts @@ -3,11 +3,67 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - /* eslint-disable @typescript-eslint/consistent-type-definitions */ -export type Query = { - language: string; - query: string; +import { Query } from '../../../../../src/plugins/data/public'; +import { DRAW_TYPE, ES_GEO_FIELD_TYPE, ES_SPATIAL_RELATIONS } from '../constants'; + +export type MapExtent = { + maxLat: number; + maxLon: number; + minLat: number; + minLon: number; +}; + +export type MapQuery = Query & { queryLastTriggeredAt: string; }; + +export type MapRefreshConfig = { + isPaused: boolean; + interval: number; +}; + +export type MapCenter = { + lat: number; + lon: number; +}; + +export type MapCenterAndZoom = MapCenter & { + zoom: number; +}; + +// TODO replace with map_descriptors.MapExtent. Both define the same thing but with different casing +type MapBounds = { + min_lon: number; + max_lon: number; + min_lat: number; + max_lat: number; +}; + +export type Goto = { + bounds?: MapBounds; + center?: MapCenterAndZoom; +}; + +export type TooltipFeature = { + id: number; + layerId: string; +}; + +export type TooltipState = { + features: TooltipFeature[]; + id: string; + isLocked: boolean; + location: number[]; // 0 index is lon, 1 index is lat +}; + +export type DrawState = { + drawType: DRAW_TYPE; + filterLabel?: string; // point radius filter alias + geoFieldName?: string; + geoFieldType?: ES_GEO_FIELD_TYPE; + geometryLabel?: string; + indexPatternId?: string; + relation?: ES_SPATIAL_RELATIONS; +}; diff --git a/x-pack/plugins/maps/public/index.ts b/x-pack/plugins/maps/public/index.ts index c465700a4f9c5..e3feb47691877 100644 --- a/x-pack/plugins/maps/public/index.ts +++ b/x-pack/plugins/maps/public/index.ts @@ -10,3 +10,5 @@ import { MapsPlugin, MapsPluginSetup, MapsPluginStart } from './plugin'; export const plugin: PluginInitializer = () => { return new MapsPlugin(); }; + +export { MAP_SAVED_OBJECT_TYPE } from '../common/constants'; diff --git a/x-pack/plugins/maps/public/reducers/map.d.ts b/x-pack/plugins/maps/public/reducers/map.d.ts new file mode 100644 index 0000000000000..30271d4d5fa8b --- /dev/null +++ b/x-pack/plugins/maps/public/reducers/map.d.ts @@ -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. + */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ + +import { + DrawState, + Goto, + LayerDescriptor, + MapCenter, + MapExtent, + MapQuery, + MapRefreshConfig, + TooltipState, +} from '../../common/descriptor_types'; +import { Filter, TimeRange } from '../../../../../src/plugins/data/public'; + +export type MapContext = { + zoom?: number; + center?: MapCenter; + scrollZoom: boolean; + extent?: MapExtent; + mouseCoordinates?: { + lat: number; + lon: number; + }; + timeFilters?: TimeRange; + query?: MapQuery; + filters: Filter[]; + refreshConfig?: MapRefreshConfig; + refreshTimerLastTriggeredAt?: string; + drawState?: DrawState; + disableInteractive: boolean; + disableTooltipControl: boolean; + hideToolbarOverlay: boolean; + hideLayerControl: boolean; + hideViewControl: boolean; +}; + +export type MapState = { + ready: boolean; + mapInitError?: string | null; + goto?: Goto | null; + openTooltips: TooltipState[]; + mapState: MapContext; + selectedLayerId: string | null; + __transientLayerId: string | null; + layerList: LayerDescriptor[]; + waitingForMapReadyLayerList: LayerDescriptor[]; +}; diff --git a/x-pack/plugins/maps/public/reducers/non_serializable_instances.d.ts b/x-pack/plugins/maps/public/reducers/non_serializable_instances.d.ts new file mode 100644 index 0000000000000..6d216eb60c45d --- /dev/null +++ b/x-pack/plugins/maps/public/reducers/non_serializable_instances.d.ts @@ -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 { Adapters } from 'src/plugins/inspector/public'; +import { AnyAction } from 'redux'; + +interface EventHandlers { + /** + * Take action on data load. + */ + onDataLoad: (layerId: string, dataId: string) => void; + /** + * Take action on data load end. + */ + onDataLoadEnd: (layerId: string, dataId: string, resultMeta: object) => void; + /** + * Take action on data load error. + */ + onDataLoadError: (layerId: string, dataId: string, errorMessage: string) => void; +} + +export function setEventHandlers(eventHandlers?: EventHandlers): AnyAction; + +export function getInspectorAdapters(args: unknown): Adapters | undefined; diff --git a/x-pack/plugins/maps/public/reducers/store.d.ts b/x-pack/plugins/maps/public/reducers/store.d.ts new file mode 100644 index 0000000000000..72713f943d6a6 --- /dev/null +++ b/x-pack/plugins/maps/public/reducers/store.d.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Store } from 'redux'; +import { MapState } from './map'; +import { MapUiState } from './ui'; + +export interface MapStoreState { + ui: MapUiState; + map: MapState; +} + +export type MapStore = Store; + +export function createMapStore(): MapStore; diff --git a/x-pack/plugins/maps/public/reducers/ui.js b/x-pack/plugins/maps/public/reducers/ui.ts similarity index 76% rename from x-pack/plugins/maps/public/reducers/ui.js rename to x-pack/plugins/maps/public/reducers/ui.ts index 287e1f8dd3dda..7429545ec0e46 100644 --- a/x-pack/plugins/maps/public/reducers/ui.js +++ b/x-pack/plugins/maps/public/reducers/ui.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ import { UPDATE_FLYOUT, @@ -15,19 +16,30 @@ import { SHOW_TOC_DETAILS, HIDE_TOC_DETAILS, UPDATE_INDEXING_STAGE, + // @ts-ignore } from '../actions/ui_actions'; -export const FLYOUT_STATE = { - NONE: 'NONE', - LAYER_PANEL: 'LAYER_PANEL', - ADD_LAYER_WIZARD: 'ADD_LAYER_WIZARD', -}; +export enum FLYOUT_STATE { + NONE = 'NONE', + LAYER_PANEL = 'LAYER_PANEL', + ADD_LAYER_WIZARD = 'ADD_LAYER_WIZARD', +} + +export enum INDEXING_STAGE { + READY = 'READY', + TRIGGERED = 'TRIGGERED', + SUCCESS = 'SUCCESS', + ERROR = 'ERROR', +} -export const INDEXING_STAGE = { - READY: 'READY', - TRIGGERED: 'TRIGGERED', - SUCCESS: 'SUCCESS', - ERROR: 'ERROR', +export type MapUiState = { + flyoutDisplay: FLYOUT_STATE; + isFullScreen: boolean; + isReadOnly: boolean; + isLayerTOCOpen: boolean; + isSetViewOpen: boolean; + openTOCDetails: string[]; + importIndexingStage: INDEXING_STAGE | null; }; export const DEFAULT_IS_LAYER_TOC_OPEN = true; @@ -45,7 +57,7 @@ const INITIAL_STATE = { }; // Reducer -export function ui(state = INITIAL_STATE, action) { +export function ui(state: MapUiState = INITIAL_STATE, action: any) { switch (action.type) { case UPDATE_FLYOUT: return { ...state, flyoutDisplay: action.display }; diff --git a/x-pack/plugins/ml/common/constants/search.ts b/x-pack/plugins/ml/common/constants/search.ts index e17f6b3098421..da65748668a4f 100644 --- a/x-pack/plugins/ml/common/constants/search.ts +++ b/x-pack/plugins/ml/common/constants/search.ts @@ -11,3 +11,8 @@ export enum SEARCH_QUERY_LANGUAGE { KUERY = 'kuery', LUCENE = 'lucene', } + +export interface ErrorMessage { + query: string; + message: string; +} diff --git a/x-pack/plugins/ml/common/util/errors.ts b/x-pack/plugins/ml/common/util/errors.ts new file mode 100644 index 0000000000000..4446624bf2e7f --- /dev/null +++ b/x-pack/plugins/ml/common/util/errors.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 { isErrorResponse } from '../types/errors'; + +export function getErrorMessage(error: any) { + if (isErrorResponse(error)) { + return `${error.body.error}: ${error.body.message}`; + } + + if (typeof error === 'object' && typeof error.message === 'string') { + return error.message; + } + + return JSON.stringify(error); +} diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js index 7da49a378ec96..2a34f12330a75 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js @@ -21,7 +21,7 @@ import { checkPermission } from '../../privilege/check_privilege'; import { SEARCH_QUERY_LANGUAGE } from '../../../../common/constants/search'; import { isRuleSupported } from '../../../../common/util/anomaly_utils'; import { parseInterval } from '../../../../common/util/parse_interval'; -import { escapeDoubleQuotes } from '../kql_filter_bar/utils'; +import { escapeDoubleQuotes } from '../../explorer/explorer_utils'; import { getFieldTypeFromMapping } from '../../services/mapping_service'; import { ml } from '../../services/ml_api_service'; import { mlJobService } from '../../services/job_service'; diff --git a/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.js b/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.js index 02a9e569f28a4..d3917412bfb7b 100644 --- a/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.js +++ b/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.js @@ -10,6 +10,8 @@ import React from 'react'; import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import { EMPTY_FIELD_VALUE_LABEL } from '../../timeseriesexplorer/components/entity_control/entity_control'; +import { MLCATEGORY } from '../../../../common/constants/field_types'; function getAddFilter({ entityName, entityValue, filter }) { return ( @@ -68,7 +70,11 @@ export const EntityCell = function EntityCell({ filter, wrapText = false, }) { - const valueText = entityName !== 'mlcategory' ? entityValue : `mlcategory ${entityValue}`; + let valueText = entityValue === '' ? {EMPTY_FIELD_VALUE_LABEL} : entityValue; + if (entityName === MLCATEGORY) { + valueText = `${MLCATEGORY} ${valueText}`; + } + const textStyle = { maxWidth: '100%' }; const textWrapperClass = wrapText ? 'field-value-long' : 'field-value-short'; const shouldDisplayIcons = diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/__snapshots__/kql_filter_bar.test.js.snap b/x-pack/plugins/ml/public/application/components/kql_filter_bar/__snapshots__/kql_filter_bar.test.js.snap deleted file mode 100644 index f2eeb00b6c643..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/__snapshots__/kql_filter_bar.test.js.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`KqlFilterBar snapshot 1`] = ` - - - -`; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/__tests__/utils.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/__tests__/utils.js deleted file mode 100644 index 6029799ffe8b8..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/__tests__/utils.js +++ /dev/null @@ -1,123 +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 expect from '@kbn/expect'; -import { removeFilterFromQueryString, getQueryPattern, escapeRegExp } from '../utils'; - -describe('ML - KqlFilterBar utils', () => { - const fieldName = 'http.response.status_code'; - const fieldValue = '200'; - const speciaCharFieldName = 'normal(brackets)name'; - const speciaCharFieldValue = '<>:;[})'; - - describe('removeFilterFromQueryString', () => { - it('removes selected fieldName/fieldValue from query string with one value', () => { - const currentQueryString = 'http.response.status_code : "200"'; - const expectedOutput = ''; - const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue); - expect(result).to.be(expectedOutput); - }); - - it('removes selected fieldName/fieldValue of type number from existing query string with one value', () => { - const currentQueryString = 'http.response.status_code : 200'; - const expectedOutput = ''; - const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue); - expect(result).to.be(expectedOutput); - }); - - it('removes selected fieldName/fieldValue from query string with multiple values', () => { - const currentQueryString = 'test_field : test_value or http.response.status_code : "200"'; - const expectedOutput = 'test_field : test_value'; - const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue); - expect(result).to.be(expectedOutput); - }); - - it('removes selected fieldName/fieldValue of type number from existing query string with multiple values', () => { - const currentQueryString = 'http.response.status_code : 200 or test_field : test_value'; - const expectedOutput = 'test_field : test_value'; - const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue); - expect(result).to.be(expectedOutput); - }); - - it("removes 'and' from end of the query to ensure kuery syntax is valid", () => { - const currentQueryString = 'http.response.status_code : "200" and'; - const expectedOutput = ''; - const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue); - expect(result).to.be(expectedOutput); - }); - - it("removes 'or' from end of the query to ensure kuery syntax is valid", () => { - const currentQueryString = 'http.response.status_code : "200" or'; - const expectedOutput = ''; - const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue); - expect(result).to.be(expectedOutput); - }); - - it("removes 'and' from start of the query to ensure kuery syntax is valid", () => { - const currentQueryString = ' and http.response.status_code : "200"'; - const expectedOutput = ''; - const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue); - expect(result).to.be(expectedOutput); - }); - - it("removes 'or' from start of the query to ensure kuery syntax is valid", () => { - const currentQueryString = ' or http.response.status_code : "200" '; - const expectedOutput = ''; - const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue); - expect(result).to.be(expectedOutput); - }); - - it('removes selected fieldName/fieldValue correctly from AND query string when it is the middle value', () => { - const currentQueryString = `http.response.status_code : "400" and http.response.status_code : "200" - and http.response.status_code : "300"`; - const expectedOutput = - 'http.response.status_code : "400" and http.response.status_code : "300"'; - const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue); - expect(result).to.be(expectedOutput); - }); - - it('removes selected fieldName/fieldValue correctly from OR query string when it is the middle value', () => { - const currentQueryString = `http.response.status_code : "400" or http.response.status_code : "200" - or http.response.status_code : "300"`; - const expectedOutput = - 'http.response.status_code : "400" or http.response.status_code : "300"'; - const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue); - expect(result).to.be(expectedOutput); - }); - }); - - describe('getQueryPattern', () => { - it('creates a regular expression pattern for given fieldName/fieldValue', () => { - // The source property returns a String containing the source text of the regexp object - // and it doesn't contain the two forward slashes on both sides and any flags. - const expectedOutput = /(http\.response\.status_code)\s?:\s?(")?(200)(")?/i.source; - const result = getQueryPattern(fieldName, fieldValue).source; - expect(result).to.be(expectedOutput); - }); - - it('creates a regular expression pattern for given fieldName/fieldValue containing special characters', () => { - // The source property returns a String containing the source text of the regexp object - // and it doesn't contain the two forward slashes on both sides and any flags. - const expectedOutput = /(normal\(brackets\)name)\s?:\s?(")?(<>:;\[\}\))(")?/i.source; - const result = getQueryPattern(speciaCharFieldName, speciaCharFieldValue).source; - expect(result).to.be(expectedOutput); - }); - }); - - describe('escapeRegExp', () => { - it('escapes regex special characters for given fieldName/fieldValue', () => { - // The source property returns a String containing the source text of the regexp object - // and it doesn't contain the two forward slashes on both sides and any flags. - const expectedFieldName = 'normal\\(brackets\\)name'; - const expectedFieldValue = '<>:;\\[\\}\\)'; - const resultFieldName = escapeRegExp(speciaCharFieldName); - const resultFieldValue = escapeRegExp(speciaCharFieldValue); - - expect(resultFieldName).to.be(expectedFieldName); - expect(resultFieldValue).to.be(expectedFieldValue); - }); - }); -}); diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/__snapshots__/click_outside.test.js.snap b/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/__snapshots__/click_outside.test.js.snap deleted file mode 100644 index eb3e5e6005dee..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/__snapshots__/click_outside.test.js.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ClickOutside snapshot 1`] = `
`; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.js deleted file mode 100644 index 02d6750dca965..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.js +++ /dev/null @@ -1,42 +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 React, { Component } from 'react'; -import PropTypes from 'prop-types'; - -export class ClickOutside extends Component { - componentDidMount() { - document.addEventListener('mousedown', this.onClick); - } - - componentWillUnmount() { - document.removeEventListener('mousedown', this.onClick); - } - - setNodeRef = node => { - this.nodeRef = node; - }; - - onClick = event => { - if (this.nodeRef && !this.nodeRef.contains(event.target)) { - this.props.onClickOutside(); - } - }; - - render() { - const { style, children } = this.props; - - return ( -
- {children} -
- ); - } -} - -ClickOutside.propTypes = { - onClickOutside: PropTypes.func.isRequired, -}; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.test.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.test.js deleted file mode 100644 index 1cd1dc6e4d715..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.test.js +++ /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 React from 'react'; -import { shallow } from 'enzyme'; -import { ClickOutside } from './click_outside'; - -describe('ClickOutside', () => { - test('snapshot', () => { - const wrapper = shallow( {}} />); - expect(wrapper).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/index.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/index.js deleted file mode 100644 index 884481f9848dd..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/index.js +++ /dev/null @@ -1,7 +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. - */ - -export { ClickOutside } from './click_outside'; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/__snapshots__/filter_bar.test.js.snap b/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/__snapshots__/filter_bar.test.js.snap deleted file mode 100644 index f3c825a66ee2f..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/__snapshots__/filter_bar.test.js.snap +++ /dev/null @@ -1,133 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`FilterBar snapshot suggestions not shown 1`] = ` - -
- -
- Test description for fieldValueOne

", - "end": 1, - "start": 0, - "text": "fieldValueOne", - "type": "field", - }, - Object { - "description": "

Test description for fieldValueTwo

", - "end": 1, - "start": 0, - "text": "fieldValueTwo", - "type": "field", - }, - ] - } - /> -
-`; - -exports[`FilterBar snapshot suggestions shown 1`] = ` - -
- -
- Test description for fieldValueOne

", - "end": 1, - "start": 0, - "text": "fieldValueOne", - "type": "field", - }, - Object { - "description": "

Test description for fieldValueTwo

", - "end": 1, - "start": 0, - "text": "fieldValueTwo", - "type": "field", - }, - ] - } - /> -
-`; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.js deleted file mode 100644 index 0c1796a6e01ca..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.js +++ /dev/null @@ -1,224 +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 React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { Suggestions } from '../suggestions'; -import { ClickOutside } from '../click_outside'; -import { EuiFieldSearch, EuiProgress, keyCodes } from '@elastic/eui'; - -export class FilterBar extends Component { - state = { - isSuggestionsVisible: false, - index: null, - value: '', - inputIsPristine: true, - }; - - static getDerivedStateFromProps(props, state) { - if (state.inputIsPristine && props.initialValue) { - return { - value: props.initialValue, - }; - } - - return null; - } - - // Set value to filter created via filter table - componentDidUpdate(oldProps) { - const newProps = this.props; - if (oldProps.valueExternal !== newProps.valueExternal) { - this.setState({ value: newProps.valueExternal, index: null }); - } - } - - incrementIndex = currentIndex => { - let nextIndex = currentIndex + 1; - if (currentIndex === null || nextIndex >= this.props.suggestions.length) { - nextIndex = 0; - } - this.setState({ index: nextIndex }); - }; - - decrementIndex = currentIndex => { - let previousIndex = currentIndex - 1; - if (previousIndex < 0) { - previousIndex = null; - } - this.setState({ index: previousIndex }); - }; - - onKeyUp = event => { - const { selectionStart } = event.target; - const { value } = this.state; - switch (event.keyCode) { - case keyCodes.LEFT: - case keyCodes.RIGHT: - this.setState({ isSuggestionsVisible: true }); - this.props.onChange(value, selectionStart); - break; - } - }; - - onKeyDown = event => { - const { isSuggestionsVisible, index, value } = this.state; - switch (event.keyCode) { - case keyCodes.DOWN: - event.preventDefault(); - if (isSuggestionsVisible) { - this.incrementIndex(index); - } else { - this.setState({ isSuggestionsVisible: true, index: 0 }); - } - break; - case keyCodes.UP: - event.preventDefault(); - if (isSuggestionsVisible) { - this.decrementIndex(index); - } - break; - case keyCodes.ENTER: - event.preventDefault(); - if (isSuggestionsVisible && this.props.suggestions[index]) { - this.selectSuggestion(this.props.suggestions[index]); - } else { - this.setState({ isSuggestionsVisible: false }); - this.props.onSubmit(value); - } - break; - case keyCodes.ESC: - event.preventDefault(); - this.setState({ isSuggestionsVisible: false }); - break; - case keyCodes.TAB: - this.setState({ isSuggestionsVisible: false }); - break; - } - }; - - selectSuggestion = suggestion => { - const nextInputValue = - this.state.value.substr(0, suggestion.start) + - suggestion.text + - this.state.value.substr(suggestion.end); - - this.setState({ value: nextInputValue, index: null }); - this.props.onChange(nextInputValue, nextInputValue.length); - }; - - onClickOutside = () => { - this.setState({ isSuggestionsVisible: false }); - }; - - onChangeInputValue = event => { - const { value, selectionStart } = event.target; - const hasValue = Boolean(value.trim()); - this.setState({ - value, - inputIsPristine: false, - isSuggestionsVisible: hasValue, - index: null, - }); - - if (!hasValue) { - this.props.onSubmit(value); - } - this.props.onChange(value, selectionStart); - }; - - onClickInput = event => { - const { selectionStart } = event.target; - this.props.onChange(this.state.value, selectionStart); - }; - - onClickSuggestion = suggestion => { - this.selectSuggestion(suggestion); - this.inputRef.focus(); - }; - - onMouseEnterSuggestion = index => { - this.setState({ index }); - }; - - onSubmit = () => { - this.props.onSubmit(this.state.value); - this.setState({ isSuggestionsVisible: false }); - }; - - render() { - const { disabled } = this.props; - const { value } = this.state; - - return ( - -
- { - if (node) { - this.inputRef = node; - } - }} - disabled={disabled} - value={value} - onKeyDown={this.onKeyDown} - onKeyUp={this.onKeyUp} - onChange={this.onChangeInputValue} - onClick={this.onClickInput} - autoComplete="off" - spellCheck={false} - data-test-subj={this.props.testSubj} - /> - - {this.props.isLoading && ( - - )} -
- - -
- ); - } -} - -FilterBar.propTypes = { - initialValue: PropTypes.string, - isLoading: PropTypes.bool, - disabled: PropTypes.bool, - onChange: PropTypes.func.isRequired, - placeholder: PropTypes.string, - onSubmit: PropTypes.func.isRequired, - valueExternal: PropTypes.string, - suggestions: PropTypes.array.isRequired, - testSubj: PropTypes.string, -}; - -FilterBar.defaultProps = { - isLoading: false, - disabled: false, - placeholder: 'tag : engineering OR tag : marketing', - suggestions: [], - testSubj: undefined, -}; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.test.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.test.js deleted file mode 100644 index 287803f9eb40a..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.test.js +++ /dev/null @@ -1,83 +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 React from 'react'; -import { shallow, mount } from 'enzyme'; -import { keyCodes } from '@elastic/eui'; -import { FilterBar } from './filter_bar'; - -const defaultProps = { - disabled: false, - initialValue: '', - placeholder: 'Test placeholder', - isLoading: false, - onChange: () => {}, - onSubmit: () => {}, - suggestions: [ - { - description: '

Test description for fieldValueOne

', - end: 1, - start: 0, - text: 'fieldValueOne', - type: 'field', - }, - { - description: '

Test description for fieldValueTwo

', - end: 1, - start: 0, - text: 'fieldValueTwo', - type: 'field', - }, - ], -}; - -describe('FilterBar', () => { - test('snapshot suggestions not shown', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - }); - - test('snapshot suggestions shown', () => { - const wrapper = shallow(); - wrapper.setState({ isSuggestionsVisible: true }); - expect(wrapper).toMatchSnapshot(); - }); - - test('index updated in state when suggestion is navigated to via mouse', () => { - const wrapper = mount(); - wrapper.setState({ isSuggestionsVisible: true }); - - expect(wrapper.state('index')).toEqual(null); - - const firstSuggestion = wrapper.find('li').first(); - firstSuggestion.simulate('mouseenter'); - expect(wrapper.state('index')).toEqual(0); - }); - - test('index updated and suggestions set to visible when input added', () => { - const wrapper = shallow(); - // default values - expect(wrapper.state('index')).toEqual(null); - expect(wrapper.state('isSuggestionsVisible')).toBe(false); - - const searchBar = wrapper.find('EuiFieldSearch'); - searchBar.simulate('keydown', { preventDefault: () => {}, keyCode: keyCodes.DOWN }); - wrapper.update(); - // updated values - expect(wrapper.state('index')).toEqual(0); - expect(wrapper.state('isSuggestionsVisible')).toBe(true); - }); - - test('index updated in state when suggestion is navigated to via keyboard', () => { - const wrapper = shallow(); - wrapper.setState({ isSuggestionsVisible: true, value: 'f', index: 0 }); - const searchBar = wrapper.find('EuiFieldSearch'); - searchBar.simulate('keydown', { preventDefault: () => {}, keyCode: keyCodes.DOWN }); - wrapper.update(); - - expect(wrapper.state('index')).toEqual(1); - }); -}); diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/index.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/index.js deleted file mode 100644 index d229943f6afe7..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/index.js +++ /dev/null @@ -1,7 +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. - */ - -export { KqlFilterBar } from './kql_filter_bar'; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.js deleted file mode 100644 index 0f3c6d25fe641..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.js +++ /dev/null @@ -1,115 +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 React, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; -import { uniqueId } from 'lodash'; -import { FilterBar } from './filter_bar'; -import { EuiCallOut, EuiLink, EuiText } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { getSuggestions, getKqlQueryValues } from './utils'; -import { getDocLinks } from '../../util/dependency_cache'; - -function getErrorWithLink(errorMessage) { - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = getDocLinks(); - return ( - - {`${errorMessage} Input must be valid `} - - {'Kibana Query Language'} - - {' (KQL) syntax.'} - - ); -} - -export class KqlFilterBar extends Component { - state = { - error: null, - suggestions: [], - isLoadingSuggestions: false, - isLoadingIndexPattern: true, - }; - - onChange = async (inputValue, selectionStart) => { - const { indexPattern } = this.props; - - this.setState({ error: null, suggestions: [], isLoadingSuggestions: true }); - - const currentRequest = uniqueId(); - this.currentRequest = currentRequest; - const boolFilter = []; - - try { - const suggestions = - (await getSuggestions(inputValue, selectionStart, indexPattern, boolFilter)) || []; - - if (currentRequest !== this.currentRequest) { - return; - } - - this.setState({ suggestions, isLoadingSuggestions: false }); - } catch (e) { - console.error('Error while fetching suggestions', e); - const errorMessage = i18n.translate('xpack.ml.explorer.fetchingSuggestionsErrorMessage', { - defaultMessage: 'Error while fetching suggestions', - }); - this.setState({ isLoadingSuggestions: false, error: e.message ? e.message : errorMessage }); - } - }; - - onSubmit = inputValue => { - const { indexPattern } = this.props; - const { onSubmit } = this.props; - - try { - // returns object with properties: { influencersFilterQuery, filteredFields, queryString, isAndOperator } - const kqlQueryValues = getKqlQueryValues(inputValue, indexPattern); - onSubmit(kqlQueryValues); - } catch (e) { - console.log('Invalid kuery syntax', e); // eslint-disable-line no-console - const errorWithLink = getErrorWithLink(e.message); - const errorMessage = i18n.translate('xpack.ml.explorer.invalidKuerySyntaxErrorMessage', { - defaultMessage: 'Invalid kuery syntax', - }); - this.setState({ error: e.message ? errorWithLink : errorMessage }); - } - }; - - render() { - const { error } = this.state; - const { initialValue, placeholder, valueExternal, testSubj } = this.props; - - return ( - - - {error && {error}} - - ); - } -} - -KqlFilterBar.propTypes = { - indexPattern: PropTypes.object.isRequired, - initialValue: PropTypes.string, - onSubmit: PropTypes.func.isRequired, - placeholder: PropTypes.string, - valueExternal: PropTypes.string, - testSubj: PropTypes.string, -}; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.test.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.test.js deleted file mode 100644 index 610d924651406..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.test.js +++ /dev/null @@ -1,63 +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 React from 'react'; -import { shallow } from 'enzyme'; -import { KqlFilterBar } from './kql_filter_bar'; - -const defaultProps = { - indexPattern: { - title: '.ml-anomalies-*', - fields: [ - { - name: 'nginx.access.geoip.country_iso_code', - type: 'string', - aggregatable: true, - searchable: true, - }, - { - name: 'nginx.access.url', - type: 'string', - aggregatable: true, - searchable: true, - }, - ], - }, - initialValue: '', - onSubmit: () => {}, - placeholder: undefined, -}; - -jest.mock('../../util/dependency_cache', () => ({ - getAutocomplete: () => ({ - getQuerySuggestions: () => {}, - }), -})); - -describe('KqlFilterBar', () => { - test('snapshot', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - }); - - test('error message callout when error is present', () => { - const wrapper = shallow(); - wrapper.setState({ error: 'Invalid syntax' }); - wrapper.update(); - const callout = wrapper.find('EuiCallOut'); - - expect(callout.contains('Invalid syntax')).toBe(true); - }); - - test('suggestions loading when typing into search bar', () => { - const wrapper = shallow(); - expect(wrapper.state('isLoadingSuggestions')).toBe(false); - // Simulate typing in by triggering change with inputValue and selectionStart - const filterBar = wrapper.find('FilterBar'); - filterBar.simulate('change', 'n', 1); - expect(wrapper.state('isLoadingSuggestions')).toBe(true); - }); -}); diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/__snapshots__/suggestion.test.js.snap b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/__snapshots__/suggestion.test.js.snap deleted file mode 100644 index 4eb236f50be05..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/__snapshots__/suggestion.test.js.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Suggestion snapshot 1`] = ` - - - - - - fieldValue - - - <p>Test description for fieldValue</p> - - -`; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/index.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/index.js deleted file mode 100644 index 98aedf068a987..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/index.js +++ /dev/null @@ -1,7 +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. - */ - -export { Suggestion } from './suggestion'; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.js deleted file mode 100644 index 121082a776c80..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.js +++ /dev/null @@ -1,117 +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 React from 'react'; -import PropTypes from 'prop-types'; -import styled from 'styled-components'; -import { EuiIcon } from '@elastic/eui'; -import { tint } from 'polished'; -import theme from '@elastic/eui/dist/eui_theme_light.json'; - -function getIconColor(type) { - switch (type) { - case 'field': - return theme.euiColorVis7; - case 'value': - return theme.euiColorVis0; - case 'operator': - return theme.euiColorVis1; - case 'conjunction': - return theme.euiColorVis3; - case 'recentSearch': - return theme.euiColorMediumShade; - } -} - -const Description = styled.div` - color: ${theme.euiColorDarkShade}; - - p { - display: inline; - - span { - font-family: ${theme.euiFontFamily}; - color: ${theme.euiColorFullShade}; - padding: 0 ${theme.euiSizeXS}; - display: inline-block; - } - } -`; - -const ListItem = styled.li` - font-size: ${theme.euiFontSizeXS}; - height: ${theme.euiSizeXL}; - align-items: center; - display: flex; - background: ${props => (props.selected ? theme.euiColorLightestShade : 'initial')}; - cursor: pointer; - border-radius: ${theme.euiSizeXS}; - - ${Description} { - p span { - background: ${props => - props.selected ? theme.euiColorEmptyShade : theme.euiColorLightestShade}; - } - } -`; - -const Icon = styled.div` - flex: 0 0 ${theme.euiSizeXL}; - background: ${props => tint(0.1, getIconColor(props.type))}; - color: ${props => getIconColor(props.type)}; - width: 100%; - height: 100%; - text-align: center; - line-height: ${theme.euiSizeXL}; -`; - -const TextValue = styled.div` - flex: 0 0 ${theme.euiSize * 16}px; - color: ${theme.euiColorDarkestShade}; - padding: 0 ${theme.euiSizeS}; -`; - -function getEuiIconType(type) { - switch (type) { - case 'field': - return 'kqlField'; - case 'value': - return 'kqlValue'; - case 'recentSearch': - return 'search'; - case 'conjunction': - return 'kqlSelector'; - case 'operator': - return 'kqlOperand'; - default: - throw new Error('Unknown type', type); - } -} - -export const Suggestion = props => { - return ( - props.onClick(props.suggestion)} - onMouseEnter={props.onMouseEnter} - > - - - - {props.suggestion.text} - {props.suggestion.description} - - ); -}; - -Suggestion.propTypes = { - onClick: PropTypes.func.isRequired, - onMouseEnter: PropTypes.func.isRequired, - selected: PropTypes.bool, - suggestion: PropTypes.object.isRequired, - innerRef: PropTypes.func.isRequired, -}; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.test.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.test.js deleted file mode 100644 index d60f2004db445..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.test.js +++ /dev/null @@ -1,30 +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 React from 'react'; -import { shallow } from 'enzyme'; -import { Suggestion } from './suggestion'; - -const defaultProps = { - innerRef: () => {}, - onClick: () => {}, - onMouseEnter: () => {}, - selected: true, - suggestion: { - description: '

Test description for fieldValue

', - end: 1, - start: 0, - text: 'fieldValue', - type: 'field', - }, -}; - -describe('Suggestion', () => { - test('snapshot', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/__snapshots__/suggestions.test.js.snap b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/__snapshots__/suggestions.test.js.snap deleted file mode 100644 index 869e321d4a7af..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/__snapshots__/suggestions.test.js.snap +++ /dev/null @@ -1,40 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Suggestions snapshot 1`] = ` - - Test description for fieldValueOne

", - "end": 1, - "start": 0, - "text": "fieldValueOne", - "type": "field", - } - } - /> - Test description for fieldValueTwo

", - "end": 1, - "start": 0, - "text": "fieldValueTwo", - "type": "field", - } - } - /> -
-`; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/index.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/index.js deleted file mode 100644 index 70fb46e06bfa0..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/index.js +++ /dev/null @@ -1,7 +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. - */ - -export { Suggestions } from './suggestions'; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.js deleted file mode 100644 index 94960e1fcc865..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.js +++ /dev/null @@ -1,81 +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 React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import styled from 'styled-components'; -import { Suggestion } from '../suggestion'; -import { rgba } from 'polished'; -import theme from '@elastic/eui/dist/eui_theme_light.json'; - -const List = styled.ul` - width: 100%; - border: 1px solid ${theme.euiColorLightShade}; - border-radius: ${theme.euiSizeXS}; - box-shadow: 0px ${theme.euiSizeXS} ${theme.euiSizeXL} ${rgba(theme.euiTextColor, 0.1)}; - position: absolute; - background: #fff; - z-index: 10; - left: 0; - max-height: ${theme.euiSize * 20}px; - overflow: scroll; -`; - -export class Suggestions extends Component { - childNodes = []; - - scrollIntoView = () => { - const parent = this.parentNode; - const child = this.childNodes[this.props.index]; - - if (this.props.index == null || !parent || !child) { - return; - } - - const scrollTop = Math.max( - Math.min(parent.scrollTop, child.offsetTop), - child.offsetTop + child.offsetHeight - parent.offsetHeight - ); - - parent.scrollTop = scrollTop; - }; - - componentDidUpdate(prevProps) { - if (prevProps.index !== this.props.index) { - this.scrollIntoView(); - } - } - - render() { - if (!this.props.show || this.props.suggestions.length === 0) { - return null; - } - - const suggestions = this.props.suggestions.map((suggestion, index) => { - const key = `${suggestion}_${index}`; - return ( - (this.childNodes[index] = node)} - selected={index === this.props.index} - suggestion={suggestion} - onClick={this.props.onClick} - onMouseEnter={() => this.props.onMouseEnter(index)} - key={key} - /> - ); - }); - - return (this.parentNode = node)}>{suggestions}; - } -} - -Suggestions.propTypes = { - index: PropTypes.number, - onClick: PropTypes.func.isRequired, - onMouseEnter: PropTypes.func.isRequired, - show: PropTypes.bool, - suggestions: PropTypes.array.isRequired, -}; diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.test.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.test.js deleted file mode 100644 index 666bfa4cfa1fa..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.test.js +++ /dev/null @@ -1,57 +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 React from 'react'; -import { shallow, mount } from 'enzyme'; -import { Suggestions } from './suggestions'; - -const defaultProps = { - index: 0, - onClick: () => {}, - onMouseEnter: () => {}, - show: true, - suggestions: [ - { - description: '

Test description for fieldValueOne

', - end: 1, - start: 0, - text: 'fieldValueOne', - type: 'field', - }, - { - description: '

Test description for fieldValueTwo

', - end: 1, - start: 0, - text: 'fieldValueTwo', - type: 'field', - }, - ], -}; - -describe('Suggestions', () => { - test('snapshot', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - }); - - test('is null when show is false', () => { - const noShowProps = { ...defaultProps, show: false }; - const wrapper = shallow(); - expect(wrapper.isEmptyRender()).toBeTruthy(); - }); - - test('is null when no suggestions are available', () => { - const noSuggestions = { ...defaultProps, suggestions: [] }; - const wrapper = shallow(); - expect(wrapper.isEmptyRender()).toBeTruthy(); - }); - - test('creates suggestion list item for each suggestion passed in via props', () => { - const wrapper = mount(); - const suggestions = wrapper.find('li'); - expect(suggestions.length).toEqual(2); - }); -}); diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/utils.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/utils.js deleted file mode 100644 index d632f4079e5b9..0000000000000 --- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/utils.js +++ /dev/null @@ -1,97 +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 { esKuery } from '../../../../../../../src/plugins/data/public'; -import { getAutocomplete } from '../../util/dependency_cache'; - -export function getSuggestions(query, selectionStart, indexPattern, boolFilter) { - const autocomplete = getAutocomplete(); - return autocomplete.getQuerySuggestions({ - language: 'kuery', - indexPatterns: [indexPattern], - boolFilter, - query, - selectionStart, - selectionEnd: selectionStart, - }); -} - -function convertKueryToEsQuery(kuery, indexPattern) { - const ast = esKuery.fromKueryExpression(kuery); - return esKuery.toElasticsearchQuery(ast, indexPattern); -} -// Recommended by MDN for escaping user input to be treated as a literal string within a regular expression -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions -export function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} - -export function escapeParens(string) { - return string.replace(/[()]/g, '\\$&'); -} - -export function escapeDoubleQuotes(string) { - return string.replace(/\"/g, '\\$&'); -} - -export function getKqlQueryValues(inputValue, indexPattern) { - const ast = esKuery.fromKueryExpression(inputValue); - const isAndOperator = ast.function === 'and'; - const query = convertKueryToEsQuery(inputValue, indexPattern); - const filteredFields = []; - - if (!query) { - return; - } - - // if ast.type == 'function' then layout of ast.arguments: - // [{ arguments: [ { type: 'literal', value: 'AAL' } ] },{ arguments: [ { type: 'literal', value: 'AAL' } ] }] - if (ast && Array.isArray(ast.arguments)) { - ast.arguments.forEach(arg => { - if (arg.arguments !== undefined) { - arg.arguments.forEach(nestedArg => { - if (typeof nestedArg.value === 'string') { - filteredFields.push(nestedArg.value); - } - }); - } else if (typeof arg.value === 'string') { - filteredFields.push(arg.value); - } - }); - } - - return { - filterQuery: query, - filteredFields, - queryString: inputValue, - isAndOperator, - tableQueryString: inputValue, - }; -} - -export function getQueryPattern(fieldName, fieldValue) { - const sanitizedFieldName = escapeRegExp(fieldName); - const sanitizedFieldValue = escapeRegExp(fieldValue); - - return new RegExp(`(${sanitizedFieldName})\\s?:\\s?(")?(${sanitizedFieldValue})(")?`, 'i'); -} - -export function removeFilterFromQueryString(currentQueryString, fieldName, fieldValue) { - let newQueryString = ''; - // Remove the passed in fieldName and value from the existing filter - const queryPattern = getQueryPattern(fieldName, fieldValue); - newQueryString = currentQueryString.replace(queryPattern, ''); - // match 'and' or 'or' at the start/end of the string - const endPattern = /\s(and|or)\s*$/gi; - const startPattern = /^\s*(and|or)\s/gi; - // If string has a double operator (e.g. tag:thing or or tag:other) remove and replace with the first occurring operator - const invalidOperatorPattern = /\s+(and|or)\s+(and|or)\s+/gi; - newQueryString = newQueryString.replace(invalidOperatorPattern, ' $1 '); - // If string starts/ends with 'and' or 'or' remove that as that is illegal kuery syntax - newQueryString = newQueryString.replace(endPattern, ''); - newQueryString = newQueryString.replace(startPattern, ''); - - return newQueryString; -} diff --git a/x-pack/plugins/ml/public/application/components/messagebar/messagebar_service.js b/x-pack/plugins/ml/public/application/components/messagebar/messagebar_service.js index d79fe14cbac4e..1dc7140bd2687 100644 --- a/x-pack/plugins/ml/public/application/components/messagebar/messagebar_service.js +++ b/x-pack/plugins/ml/public/application/components/messagebar/messagebar_service.js @@ -21,7 +21,7 @@ function errorNotify(text, resp) { const toastNotifications = getToastNotifications(); toastNotifications.addError(new MLRequestFailure(err, resp), { title: i18n.translate('xpack.ml.messagebarService.errorTitle', { - defaultMessage: 'An error has ocurred', + defaultMessage: 'An error has occurred', }), }); } diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/index.ts b/x-pack/plugins/ml/public/application/contexts/kibana/index.ts index 2add1b6ea161c..0f071a42a5688 100644 --- a/x-pack/plugins/ml/public/application/contexts/kibana/index.ts +++ b/x-pack/plugins/ml/public/application/contexts/kibana/index.ts @@ -7,3 +7,4 @@ export { useMlKibana, StartServices, MlKibanaReactContextValue } from './kibana_context'; export { useUiSettings } from './use_ui_settings_context'; export { useTimefilter } from './use_timefilter'; +export { useNotifications } from './use_notifications_context'; diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/use_notifications_context.ts b/x-pack/plugins/ml/public/application/contexts/kibana/use_notifications_context.ts new file mode 100644 index 0000000000000..a03d9da49f365 --- /dev/null +++ b/x-pack/plugins/ml/public/application/contexts/kibana/use_notifications_context.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useMlKibana } from './kibana_context'; + +export const useNotifications = () => { + return useMlKibana().services.notifications; +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts index 95a8dfbb308f8..d77f19c0df79d 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts @@ -11,7 +11,7 @@ import { Subscription } from 'rxjs'; import { cloneDeep } from 'lodash'; import { ml } from '../../services/ml_api_service'; import { Dictionary } from '../../../../common/types/common'; -import { getErrorMessage } from '../pages/analytics_management/hooks/use_create_analytics_form'; +import { getErrorMessage } from '../../../../common/util/errors'; import { SavedSearchQuery } from '../../contexts/ml'; import { SortDirection } from '../../components/ml_in_memory_table'; @@ -62,6 +62,9 @@ export interface LoadExploreDataArg { export const SEARCH_SIZE = 1000; +export const TRAINING_PERCENT_MIN = 1; +export const TRAINING_PERCENT_MAX = 100; + export const defaultSearchQuery = { match_all: {}, }; @@ -172,6 +175,19 @@ export const getDependentVar = (analysis: AnalysisConfig) => { return depVar; }; +export const getTrainingPercent = (analysis: AnalysisConfig) => { + let trainingPercent; + + if (isRegressionAnalysis(analysis)) { + trainingPercent = analysis.regression.training_percent; + } + + if (isClassificationAnalysis(analysis)) { + trainingPercent = analysis.classification.training_percent; + } + return trainingPercent; +}; + export const getPredictionFieldName = (analysis: AnalysisConfig) => { // If undefined will be defaulted to dependent_variable when config is created let predictionFieldName; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts index fb1d4edb37af8..59b42935a141d 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts @@ -243,7 +243,8 @@ export function getFlattenedFields(obj: EsDocSource, resultsField: string): EsFi export const getDefaultFieldsFromJobCaps = ( fields: Field[], - jobConfig: DataFrameAnalyticsConfig + jobConfig: DataFrameAnalyticsConfig, + needsDestIndexFields: boolean ): { selectedFields: Field[]; docFields: Field[]; depVarType?: ES_FIELD_TYPES } => { const fieldsObj = { selectedFields: [], docFields: [] }; if (fields.length === 0) { @@ -260,16 +261,22 @@ export const getDefaultFieldsFromJobCaps = ( const predictedField = `${resultsField}.${ predictionFieldName ? predictionFieldName : defaultPredictionField }`; - - const allFields: any = [ - { - id: `${resultsField}.is_training`, - name: `${resultsField}.is_training`, - type: ES_FIELD_TYPES.BOOLEAN, - }, - { id: predictedField, name: predictedField, type }, - ...fields, - ].sort(({ name: a }, { name: b }) => sortRegressionResultsFields(a, b, jobConfig)); + // Only need to add these first two fields if we didn't use dest index pattern to get the fields + const allFields: any = + needsDestIndexFields === true + ? [ + { + id: `${resultsField}.is_training`, + name: `${resultsField}.is_training`, + type: ES_FIELD_TYPES.BOOLEAN, + }, + { id: predictedField, name: predictedField, type }, + ] + : []; + + allFields.push(...fields); + // @ts-ignore + allFields.sort(({ name: a }, { name: b }) => sortRegressionResultsFields(a, b, jobConfig)); let selectedFields = allFields .slice(0, DEFAULT_REGRESSION_COLUMNS * 2) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx index 263d43ceb2630..5c151166829ab 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx @@ -15,9 +15,10 @@ import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/ import { ResultsSearchQuery, defaultSearchQuery } from '../../../../common/analytics'; import { LoadingPanel } from '../loading_panel'; import { getIndexPatternIdFromName } from '../../../../../util/index_utils'; -import { IIndexPattern } from '../../../../../../../../../../src/plugins/data/common/index_patterns'; +import { IndexPattern } from '../../../../../../../../../../src/plugins/data/public'; import { newJobCapsService } from '../../../../../services/new_job_capabilities_service'; import { useMlContext } from '../../../../../contexts/ml'; +import { isGetDataFrameAnalyticsStatsResponseOk } from '../../../analytics_management/services/analytics_service/get_analytics'; export const ExplorationTitle: React.FC<{ jobId: string }> = ({ jobId }) => ( @@ -47,11 +48,12 @@ const jobCapsErrorTitle = i18n.translate( interface Props { jobId: string; - jobStatus: DATA_FRAME_TASK_STATE; } -export const ClassificationExploration: FC = ({ jobId, jobStatus }) => { +export const ClassificationExploration: FC = ({ jobId }) => { const [jobConfig, setJobConfig] = useState(undefined); + const [jobStatus, setJobStatus] = useState(undefined); + const [indexPattern, setIndexPattern] = useState(undefined); const [isLoadingJobConfig, setIsLoadingJobConfig] = useState(false); const [isInitialized, setIsInitialized] = useState(false); const [jobConfigErrorMessage, setJobConfigErrorMessage] = useState(undefined); @@ -65,6 +67,15 @@ export const ClassificationExploration: FC = ({ jobId, jobStatus }) => { setIsLoadingJobConfig(true); try { const analyticsConfigs = await ml.dataFrameAnalytics.getDataFrameAnalytics(jobId); + const analyticsStats = await ml.dataFrameAnalytics.getDataFrameAnalyticsStats(jobId); + const stats = isGetDataFrameAnalyticsStatsResponseOk(analyticsStats) + ? analyticsStats.data_frame_analytics[0] + : undefined; + + if (stats !== undefined && stats.state) { + setJobStatus(stats.state); + } + if ( Array.isArray(analyticsConfigs.data_frame_analytics) && analyticsConfigs.data_frame_analytics.length > 0 @@ -98,11 +109,27 @@ export const ClassificationExploration: FC = ({ jobId, jobStatus }) => { const initializeJobCapsService = async () => { if (jobConfig !== undefined) { try { - const sourceIndex = jobConfig.source.index[0]; - const indexPatternId = getIndexPatternIdFromName(sourceIndex) || sourceIndex; - const indexPattern: IIndexPattern = await mlContext.indexPatterns.get(indexPatternId); - if (indexPattern !== undefined) { - await newJobCapsService.initializeFromIndexPattern(indexPattern, false, false); + const destIndex = Array.isArray(jobConfig.dest.index) + ? jobConfig.dest.index[0] + : jobConfig.dest.index; + const destIndexPatternId = getIndexPatternIdFromName(destIndex) || destIndex; + let indexP: IndexPattern | undefined; + + try { + indexP = await mlContext.indexPatterns.get(destIndexPatternId); + } catch (e) { + indexP = undefined; + } + + if (indexP === undefined) { + const sourceIndex = jobConfig.source.index[0]; + const sourceIndexPatternId = getIndexPatternIdFromName(sourceIndex) || sourceIndex; + indexP = await mlContext.indexPatterns.get(sourceIndexPatternId); + } + + if (indexP !== undefined) { + setIndexPattern(indexP); + await newJobCapsService.initializeFromIndexPattern(indexP, false, false); } setIsInitialized(true); } catch (e) { @@ -117,7 +144,7 @@ export const ClassificationExploration: FC = ({ jobId, jobStatus }) => { useEffect(() => { initializeJobCapsService(); - }, [JSON.stringify(jobConfig)]); + }, [jobConfig && jobConfig.id]); if (jobConfigErrorMessage !== undefined || jobCapsServiceErrorMessage !== undefined) { return ( @@ -143,13 +170,17 @@ export const ClassificationExploration: FC = ({ jobId, jobStatus }) => { )} {isLoadingJobConfig === true && jobConfig === undefined && } - {isLoadingJobConfig === false && jobConfig !== undefined && isInitialized === true && ( - - )} + {isLoadingJobConfig === false && + jobConfig !== undefined && + indexPattern !== undefined && + isInitialized === true && ( + + )} ); }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx index 1c5563bdb4f83..91dae49ba5c49 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx @@ -50,10 +50,47 @@ const defaultPanelWidth = 500; interface Props { jobConfig: DataFrameAnalyticsConfig; - jobStatus: DATA_FRAME_TASK_STATE; + jobStatus?: DATA_FRAME_TASK_STATE; searchQuery: ResultsSearchQuery; } +enum SUBSET_TITLE { + TRAINING = 'training', + TESTING = 'testing', + ENTIRE = 'entire', +} + +const entireDatasetHelpText = i18n.translate( + 'xpack.ml.dataframe.analytics.classificationExploration.confusionMatrixEntireHelpText', + { + defaultMessage: 'Normalized confusion matrix for entire dataset', + } +); + +const testingDatasetHelpText = i18n.translate( + 'xpack.ml.dataframe.analytics.classificationExploration.confusionMatrixTestingHelpText', + { + defaultMessage: 'Normalized confusion matrix for testing dataset', + } +); + +const trainingDatasetHelpText = i18n.translate( + 'xpack.ml.dataframe.analytics.classificationExploration.confusionMatrixTrainingHelpText', + { + defaultMessage: 'Normalized confusion matrix for training dataset', + } +); + +function getHelpText(dataSubsetTitle: string) { + let helpText = entireDatasetHelpText; + if (dataSubsetTitle === SUBSET_TITLE.TESTING) { + helpText = testingDatasetHelpText; + } else if (dataSubsetTitle === SUBSET_TITLE.TRAINING) { + helpText = trainingDatasetHelpText; + } + return helpText; +} + export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) => { const { services: { docLinks }, @@ -66,6 +103,7 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) const [popoverContents, setPopoverContents] = useState([]); const [docsCount, setDocsCount] = useState(null); const [error, setError] = useState(null); + const [dataSubsetTitle, setDataSubsetTitle] = useState(SUBSET_TITLE.ENTIRE); const [panelWidth, setPanelWidth] = useState(defaultPanelWidth); // Column visibility const [visibleColumns, setVisibleColumns] = useState(() => @@ -197,6 +235,18 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) hasIsTrainingClause[0] && hasIsTrainingClause[0].match[`${resultsField}.is_training`]; + const noTrainingQuery = isTrainingClause === false || isTrainingClause === undefined; + + if (noTrainingQuery) { + setDataSubsetTitle(SUBSET_TITLE.ENTIRE); + } else { + setDataSubsetTitle( + isTrainingClause && isTrainingClause.query === 'true' + ? SUBSET_TITLE.TRAINING + : SUBSET_TITLE.TESTING + ); + } + loadData({ isTrainingClause }); }, [JSON.stringify(searchQuery)]); @@ -268,9 +318,11 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) - - {getTaskStateBadge(jobStatus)} - + {jobStatus !== undefined && ( + + {getTaskStateBadge(jobStatus)} + + )} @@ -302,14 +354,7 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) - - {i18n.translate( - 'xpack.ml.dataframe.analytics.classificationExploration.confusionMatrixHelpText', - { - defaultMessage: 'Normalized confusion matrix', - } - )} - + {getHelpText(dataSubsetTitle)} >; } export const ResultsTable: FC = React.memo( - ({ jobConfig, jobStatus, setEvaluateSearchQuery }) => { + ({ indexPattern, jobConfig, jobStatus, setEvaluateSearchQuery }) => { const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(25); const [selectedFields, setSelectedFields] = useState([] as Field[]); @@ -126,6 +128,8 @@ export const ResultsTable: FC = React.memo( } } + const needsDestIndexFields = indexPattern && indexPattern.title === jobConfig.source.index[0]; + const { errorMessage, loadExploreData, @@ -133,12 +137,24 @@ export const ResultsTable: FC = React.memo( sortDirection, status, tableItems, - } = useExploreData(jobConfig, selectedFields, setSelectedFields, setDocFields, setDepVarType); + } = useExploreData( + jobConfig, + needsDestIndexFields, + selectedFields, + setSelectedFields, + setDocFields, + setDepVarType + ); const columns: Array> = selectedFields .sort(({ name: a }, { name: b }) => sortRegressionResultsFields(a, b, jobConfig)) .map(field => { const { type } = field; + let format: any; + + if (indexPattern !== undefined) { + format = mlFieldFormatService.getFieldFormatFromIndexPattern(indexPattern, field.id, ''); + } const isNumber = type !== undefined && (BASIC_NUMERICAL_TYPES.has(type) || EXTENDED_NUMERICAL_TYPES.has(type)); @@ -151,6 +167,11 @@ export const ResultsTable: FC = React.memo( }; const render = (d: any, fullItem: EsDoc) => { + if (format !== undefined) { + d = format.convert(d, 'text'); + return d; + } + if (Array.isArray(d) && d.every(item => typeof item === 'string')) { // If the cells data is an array of strings, return as a comma separated list. // The list will get limited to 5 items with `…` at the end if there's more in the original array. @@ -193,12 +214,16 @@ export const ResultsTable: FC = React.memo( break; case ES_FIELD_TYPES.DATE: column.align = 'right'; - column.render = (d: any) => { - if (d !== undefined) { - return formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000); - } - return d; - }; + if (format !== undefined) { + column.render = render; + } else { + column.render = (d: any) => { + if (d !== undefined) { + return formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000); + } + return d; + }; + } break; default: column.render = render; @@ -381,9 +406,11 @@ export const ResultsTable: FC = React.memo( - - {getTaskStateBadge(jobStatus)} - + {jobStatus !== undefined && ( + + {getTaskStateBadge(jobStatus)} + + )} = React.memo( - - {getTaskStateBadge(jobStatus)} - + {jobStatus !== undefined && ( + + {getTaskStateBadge(jobStatus)} + + )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_explore_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_explore_data.ts index ff6d8377508d8..6038def592e5c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_explore_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_explore_data.ts @@ -51,6 +51,7 @@ export interface UseExploreDataReturnType { export const useExploreData = ( jobConfig: DataFrameAnalyticsConfig | undefined, + needsDestIndexFields: boolean, selectedFields: Field[], setSelectedFields: React.Dispatch>, setDocFields: React.Dispatch>, @@ -70,7 +71,7 @@ export const useExploreData = ( selectedFields: defaultSelected, docFields, depVarType, - } = getDefaultFieldsFromJobCaps(fields, jobConfig); + } = getDefaultFieldsFromJobCaps(fields, jobConfig, needsDestIndexFields); setDepVarType(depVarType); setSelectedFields(defaultSelected); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_data_grid/exploration_data_grid.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_data_grid/exploration_data_grid.tsx index 2df0f70a56722..e88bc1cd06f95 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_data_grid/exploration_data_grid.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_data_grid/exploration_data_grid.tsx @@ -12,6 +12,10 @@ import { EuiDataGrid, EuiDataGridPaginationProps, EuiDataGridSorting } from '@el import { euiDataGridStyle, euiDataGridToolbarSettings } from '../../../../common'; +import { mlFieldFormatService } from '../../../../../services/field_format_service'; + +import { IndexPattern } from '../../../../../../../../../../src/plugins/data/public'; + const FEATURE_INFLUENCE = 'feature_influence'; const PAGE_SIZE_OPTIONS = [5, 10, 25, 50]; @@ -21,6 +25,7 @@ type TableItem = Record; interface ExplorationDataGridProps { colorRange: (d: number) => string; columns: any[]; + indexPattern: IndexPattern; pagination: Pagination; resultsField: string; rowCount: number; @@ -35,6 +40,7 @@ interface ExplorationDataGridProps { export const ExplorationDataGrid: FC = ({ colorRange, columns, + indexPattern, pagination, resultsField, rowCount, @@ -63,6 +69,12 @@ export const ExplorationDataGrid: FC = ({ return null; } + let format: any; + + if (indexPattern !== undefined) { + format = mlFieldFormatService.getFieldFormatFromIndexPattern(indexPattern, columnId, ''); + } + const cellValue = fullItem.hasOwnProperty(columnId) && fullItem[columnId] !== undefined ? fullItem[columnId] @@ -87,6 +99,10 @@ export const ExplorationDataGrid: FC = ({ }); } + if (format !== undefined) { + return format.convert(cellValue, 'text'); + } + if (typeof cellValue === 'string' || cellValue === null) { return cellValue; } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.test.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.test.tsx index 030447873f6a5..7cdd15e49bd14 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.test.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.test.tsx @@ -6,7 +6,6 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; import { MlContext } from '../../../../../contexts/ml'; import { kibanaContextValueMock } from '../../../../../contexts/ml/__mocks__/kibana_context_value'; @@ -22,7 +21,7 @@ describe('Data Frame Analytics: ', () => { test('Minimal initialization', () => { const wrapper = shallow( - + ); // Without the jobConfig being loaded, the component will just return empty. diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx index 214bc01c6a2ef..fdcb7d9d237e3 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx @@ -27,7 +27,6 @@ import { import { sortColumns, INDEX_STATUS, defaultSearchQuery } from '../../../../common'; -import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns'; import { useExploreData, TableItem } from '../../hooks/use_explore_data'; @@ -50,7 +49,6 @@ const ExplorationTitle: FC<{ jobId: string }> = ({ jobId }) => ( interface ExplorationProps { jobId: string; - jobStatus: DATA_FRAME_TASK_STATE; } const getFeatureCount = (resultsField: string, tableItems: TableItem[] = []) => { @@ -63,11 +61,12 @@ const getFeatureCount = (resultsField: string, tableItems: TableItem[] = []) => ).length; }; -export const OutlierExploration: FC = React.memo(({ jobId, jobStatus }) => { +export const OutlierExploration: FC = React.memo(({ jobId }) => { const { errorMessage, indexPattern, jobConfig, + jobStatus, pagination, searchQuery, selectedFields, @@ -173,9 +172,11 @@ export const OutlierExploration: FC = React.memo(({ jobId, job - - {getTaskStateBadge(jobStatus)} - + {jobStatus !== undefined && ( + + {getTaskStateBadge(jobStatus)} + + )} {(columns.length > 0 || searchQuery !== defaultSearchQuery) && ( @@ -202,6 +203,7 @@ export const OutlierExploration: FC = React.memo(({ jobId, job = ({ jobConfig, jobStatus, searchQuery }) - - {getTaskStateBadge(jobStatus)} - + {jobStatus !== undefined && ( + + {getTaskStateBadge(jobStatus)} + + )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx index 3dfd95a27f8a7..bfeca76a2b1c7 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx @@ -18,6 +18,7 @@ import { getIndexPatternIdFromName } from '../../../../../util/index_utils'; import { IIndexPattern } from '../../../../../../../../../../src/plugins/data/common/index_patterns'; import { newJobCapsService } from '../../../../../services/new_job_capabilities_service'; import { useMlContext } from '../../../../../contexts/ml'; +import { isGetDataFrameAnalyticsStatsResponseOk } from '../../../analytics_management/services/analytics_service/get_analytics'; export const ExplorationTitle: React.FC<{ jobId: string }> = ({ jobId }) => ( @@ -47,11 +48,12 @@ const jobCapsErrorTitle = i18n.translate( interface Props { jobId: string; - jobStatus: DATA_FRAME_TASK_STATE; } -export const RegressionExploration: FC = ({ jobId, jobStatus }) => { +export const RegressionExploration: FC = ({ jobId }) => { const [jobConfig, setJobConfig] = useState(undefined); + const [jobStatus, setJobStatus] = useState(undefined); + const [indexPattern, setIndexPattern] = useState(undefined); const [isLoadingJobConfig, setIsLoadingJobConfig] = useState(false); const [isInitialized, setIsInitialized] = useState(false); const [jobConfigErrorMessage, setJobConfigErrorMessage] = useState(undefined); @@ -65,6 +67,15 @@ export const RegressionExploration: FC = ({ jobId, jobStatus }) => { setIsLoadingJobConfig(true); try { const analyticsConfigs = await ml.dataFrameAnalytics.getDataFrameAnalytics(jobId); + const analyticsStats = await ml.dataFrameAnalytics.getDataFrameAnalyticsStats(jobId); + const stats = isGetDataFrameAnalyticsStatsResponseOk(analyticsStats) + ? analyticsStats.data_frame_analytics[0] + : undefined; + + if (stats !== undefined && stats.state) { + setJobStatus(stats.state); + } + if ( Array.isArray(analyticsConfigs.data_frame_analytics) && analyticsConfigs.data_frame_analytics.length > 0 @@ -89,11 +100,27 @@ export const RegressionExploration: FC = ({ jobId, jobStatus }) => { const initializeJobCapsService = async () => { if (jobConfig !== undefined) { try { - const sourceIndex = jobConfig.source.index[0]; - const indexPatternId = getIndexPatternIdFromName(sourceIndex) || sourceIndex; - const indexPattern: IIndexPattern = await mlContext.indexPatterns.get(indexPatternId); - if (indexPattern !== undefined) { - await newJobCapsService.initializeFromIndexPattern(indexPattern, false, false); + const destIndex = Array.isArray(jobConfig.dest.index) + ? jobConfig.dest.index[0] + : jobConfig.dest.index; + const destIndexPatternId = getIndexPatternIdFromName(destIndex) || destIndex; + let indexP: IIndexPattern | undefined; + + try { + indexP = await mlContext.indexPatterns.get(destIndexPatternId); + } catch (e) { + indexP = undefined; + } + + if (indexP === undefined) { + const sourceIndex = jobConfig.source.index[0]; + const sourceIndexPatternId = getIndexPatternIdFromName(sourceIndex) || sourceIndex; + indexP = await mlContext.indexPatterns.get(sourceIndexPatternId); + } + + if (indexP !== undefined) { + setIndexPattern(indexP); + await newJobCapsService.initializeFromIndexPattern(indexP, false, false); } setIsInitialized(true); } catch (e) { @@ -108,7 +135,7 @@ export const RegressionExploration: FC = ({ jobId, jobStatus }) => { useEffect(() => { initializeJobCapsService(); - }, [JSON.stringify(jobConfig)]); + }, [jobConfig && jobConfig.id]); if (jobConfigErrorMessage !== undefined || jobCapsServiceErrorMessage !== undefined) { return ( @@ -134,13 +161,17 @@ export const RegressionExploration: FC = ({ jobId, jobStatus }) => { )} {isLoadingJobConfig === true && jobConfig === undefined && } - {isLoadingJobConfig === false && jobConfig !== undefined && isInitialized === true && ( - - )} + {isLoadingJobConfig === false && + jobConfig !== undefined && + indexPattern !== undefined && + isInitialized === true && ( + + )} ); }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx index 7a6b2b23ba7a3..8d53a9278a1af 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx @@ -28,6 +28,7 @@ import { import { Query as QueryType } from '../../../analytics_management/components/analytics_list/common'; import { ES_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/public'; +import { mlFieldFormatService } from '../../../../../services/field_format_service'; import { ColumnType, @@ -61,6 +62,7 @@ import { } from '../../../../common'; import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns'; import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; +import { IndexPattern } from '../../../../../../../../../../src/plugins/data/public'; import { useExploreData, TableItem } from './use_explore_data'; import { ExplorationTitle } from './regression_exploration'; @@ -85,13 +87,14 @@ const showingFirstDocs = i18n.translate( ); interface Props { + indexPattern: IndexPattern; jobConfig: DataFrameAnalyticsConfig; - jobStatus: DATA_FRAME_TASK_STATE; + jobStatus?: DATA_FRAME_TASK_STATE; setEvaluateSearchQuery: React.Dispatch>; } export const ResultsTable: FC = React.memo( - ({ jobConfig, jobStatus, setEvaluateSearchQuery }) => { + ({ indexPattern, jobConfig, jobStatus, setEvaluateSearchQuery }) => { const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(25); const [selectedFields, setSelectedFields] = useState([] as Field[]); @@ -126,6 +129,8 @@ export const ResultsTable: FC = React.memo( } } + const needsDestIndexFields = indexPattern && indexPattern.title === jobConfig.source.index[0]; + const { errorMessage, loadExploreData, @@ -133,12 +138,25 @@ export const ResultsTable: FC = React.memo( sortDirection, status, tableItems, - } = useExploreData(jobConfig, selectedFields, setSelectedFields, setDocFields, setDepVarType); + } = useExploreData( + jobConfig, + needsDestIndexFields, + selectedFields, + setSelectedFields, + setDocFields, + setDepVarType + ); const columns: Array> = selectedFields .sort(({ name: a }, { name: b }) => sortRegressionResultsFields(a, b, jobConfig)) .map(field => { const { type } = field; + let format: any; + + if (indexPattern !== undefined) { + format = mlFieldFormatService.getFieldFormatFromIndexPattern(indexPattern, field.id, ''); + } + const isNumber = type !== undefined && (BASIC_NUMERICAL_TYPES.has(type) || EXTENDED_NUMERICAL_TYPES.has(type)); @@ -151,6 +169,11 @@ export const ResultsTable: FC = React.memo( }; const render = (d: any, fullItem: EsDoc) => { + if (format !== undefined) { + d = format.convert(d, 'text'); + return d; + } + if (Array.isArray(d) && d.every(item => typeof item === 'string')) { // If the cells data is an array of strings, return as a comma separated list. // The list will get limited to 5 items with `…` at the end if there's more in the original array. @@ -193,12 +216,16 @@ export const ResultsTable: FC = React.memo( break; case ES_FIELD_TYPES.DATE: column.align = 'right'; - column.render = (d: any) => { - if (d !== undefined) { - return formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000); - } - return d; - }; + if (format !== undefined) { + column.render = render; + } else { + column.render = (d: any) => { + if (d !== undefined) { + return formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000); + } + return d; + }; + } break; default: column.render = render; @@ -381,9 +408,11 @@ export const ResultsTable: FC = React.memo( - - {getTaskStateBadge(jobStatus)} - + {jobStatus !== undefined && ( + + {getTaskStateBadge(jobStatus)} + + )} = React.memo( - - {getTaskStateBadge(jobStatus)} - + {jobStatus !== undefined && ( + + {getTaskStateBadge(jobStatus)} + + )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts index 22fbbaac99a18..e158e952c1c18 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts @@ -46,6 +46,7 @@ export interface UseExploreDataReturnType { export const useExploreData = ( jobConfig: DataFrameAnalyticsConfig | undefined, + needsDestIndexFields: boolean, selectedFields: Field[], setSelectedFields: React.Dispatch>, setDocFields: React.Dispatch>, @@ -65,7 +66,7 @@ export const useExploreData = ( selectedFields: defaultSelected, docFields, depVarType, - } = getDefaultFieldsFromJobCaps(fields, jobConfig); + } = getDefaultFieldsFromJobCaps(fields, jobConfig, needsDestIndexFields); setDepVarType(depVarType); setSelectedFields(defaultSelected); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/hooks/use_explore_data/use_explore_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/hooks/use_explore_data/use_explore_data.ts index 6ad0a1822e490..30f2139a263f7 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/hooks/use_explore_data/use_explore_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/hooks/use_explore_data/use_explore_data.ts @@ -19,6 +19,7 @@ import { newJobCapsService } from '../../../../../services/new_job_capabilities_ import { getIndexPatternIdFromName } from '../../../../../util/index_utils'; import { getNestedProperty } from '../../../../../util/object_utils'; import { useMlContext } from '../../../../../contexts/ml'; +import { isGetDataFrameAnalyticsStatsResponseOk } from '../../../analytics_management/services/analytics_service/get_analytics'; import { getDefaultSelectableFields, @@ -31,6 +32,7 @@ import { import { isKeywordAndTextType } from '../../../../common/fields'; import { getOutlierScoreFieldName } from './common'; +import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; export type TableItem = Record; @@ -40,6 +42,7 @@ interface UseExploreDataReturnType { errorMessage: string; indexPattern: IndexPattern | undefined; jobConfig: DataFrameAnalyticsConfig | undefined; + jobStatus: DATA_FRAME_TASK_STATE | undefined; pagination: Pagination; searchQuery: SavedSearchQuery; selectedFields: EsFieldName[]; @@ -74,6 +77,7 @@ export const useExploreData = (jobId: string): UseExploreDataReturnType => { const [indexPattern, setIndexPattern] = useState(undefined); const [jobConfig, setJobConfig] = useState(undefined); + const [jobStatus, setJobStatus] = useState(undefined); const [errorMessage, setErrorMessage] = useState(''); const [status, setStatus] = useState(INDEX_STATUS.UNUSED); @@ -90,6 +94,15 @@ export const useExploreData = (jobId: string): UseExploreDataReturnType => { useEffect(() => { (async function() { const analyticsConfigs = await ml.dataFrameAnalytics.getDataFrameAnalytics(jobId); + const analyticsStats = await ml.dataFrameAnalytics.getDataFrameAnalyticsStats(jobId); + const stats = isGetDataFrameAnalyticsStatsResponseOk(analyticsStats) + ? analyticsStats.data_frame_analytics[0] + : undefined; + + if (stats !== undefined && stats.state) { + setJobStatus(stats.state); + } + if ( Array.isArray(analyticsConfigs.data_frame_analytics) && analyticsConfigs.data_frame_analytics.length > 0 @@ -103,12 +116,32 @@ export const useExploreData = (jobId: string): UseExploreDataReturnType => { useEffect(() => { (async () => { if (jobConfig !== undefined) { - const sourceIndex = jobConfig.source.index[0]; - const indexPatternId = getIndexPatternIdFromName(sourceIndex) || sourceIndex; - const jobCapsIndexPattern: IndexPattern = await mlContext.indexPatterns.get(indexPatternId); - if (jobCapsIndexPattern !== undefined) { - setIndexPattern(jobCapsIndexPattern); - await newJobCapsService.initializeFromIndexPattern(jobCapsIndexPattern, false, false); + try { + const destIndex = Array.isArray(jobConfig.dest.index) + ? jobConfig.dest.index[0] + : jobConfig.dest.index; + const destIndexPatternId = getIndexPatternIdFromName(destIndex) || destIndex; + let indexP: IndexPattern | undefined; + + try { + indexP = await mlContext.indexPatterns.get(destIndexPatternId); + } catch (e) { + indexP = undefined; + } + + if (indexP === undefined) { + const sourceIndex = jobConfig.source.index[0]; + const sourceIndexPatternId = getIndexPatternIdFromName(sourceIndex) || sourceIndex; + indexP = await mlContext.indexPatterns.get(sourceIndexPatternId); + } + + if (indexP !== undefined) { + setIndexPattern(indexP); + await newJobCapsService.initializeFromIndexPattern(indexP, false, false); + } + } catch (e) { + // eslint-disable-next-line + console.log('Error loading index field data', e); } } })(); @@ -215,6 +248,7 @@ export const useExploreData = (jobId: string): UseExploreDataReturnType => { errorMessage, indexPattern, jobConfig, + jobStatus, pagination, rowCount, searchQuery, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx index efbebc1564bf9..c8349084dbda8 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx @@ -27,13 +27,11 @@ import { RegressionExploration } from './components/regression_exploration'; import { ClassificationExploration } from './components/classification_exploration'; import { ANALYSIS_CONFIG_TYPE } from '../../common/analytics'; -import { DATA_FRAME_TASK_STATE } from '../analytics_management/components/analytics_list/common'; export const Page: FC<{ jobId: string; analysisType: ANALYSIS_CONFIG_TYPE; - jobStatus: DATA_FRAME_TASK_STATE; -}> = ({ jobId, analysisType, jobStatus }) => ( +}> = ({ jobId, analysisType }) => ( @@ -68,13 +66,13 @@ export const Page: FC<{ {analysisType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION && ( - + )} {analysisType === ANALYSIS_CONFIG_TYPE.REGRESSION && ( - + )} {analysisType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION && ( - + )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx index 425e3bc903d04..4e19df9ae22a8 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx @@ -33,13 +33,12 @@ export const AnalyticsViewAction = { isPrimary: true, render: (item: DataFrameAnalyticsListRow) => { const analysisType = getAnalysisType(item.config.analysis); - const jobStatus = item.stats.state; const isDisabled = !isRegressionAnalysis(item.config.analysis) && !isOutlierAnalysis(item.config.analysis) && !isClassificationAnalysis(item.config.analysis); - const url = getResultsUrl(item.id, analysisType, jobStatus); + const url = getResultsUrl(item.id, analysisType); return ( = ({ actions, state }) => { - const { - resetAdvancedEditorMessages, - setAdvancedEditorRawString, - setFormState, - setJobConfig, - } = actions; + const { setAdvancedEditorRawString, setFormState } = actions; const { advancedEditorMessages, advancedEditorRawString, isJobCreated, requestMessages } = state; @@ -45,12 +39,6 @@ export const CreateAnalyticsAdvancedEditor: FC = ({ ac const onChange = (str: string) => { setAdvancedEditorRawString(str); - try { - const resultJobConfig = JSON.parse(collapseLiteralStrings(str)); - setJobConfig(resultJobConfig); - } catch (e) { - resetAdvancedEditorMessages(); - } }; // Temp effect to close the context menu popover on Clone button click diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_flyout/create_analytics_flyout.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_flyout/create_analytics_flyout.tsx index 32384e1949d0a..b0f13e398cc50 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_flyout/create_analytics_flyout.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_flyout/create_analytics_flyout.tsx @@ -26,7 +26,14 @@ export const CreateAnalyticsFlyout: FC = ({ state, }) => { const { closeModal, createAnalyticsJob, startAnalyticsJob } = actions; - const { isJobCreated, isJobStarted, isModalButtonDisabled, isValid, cloneJob } = state; + const { + isJobCreated, + isJobStarted, + isModalButtonDisabled, + isValid, + isAdvancedEditorValidJson, + cloneJob, + } = state; const headerText = !!cloneJob ? i18n.translate('xpack.ml.dataframe.analytics.clone.flyoutHeaderTitle', { @@ -61,7 +68,7 @@ export const CreateAnalyticsFlyout: FC = ({ {!isJobCreated && !isJobStarted && ( = ({ actions, sta })} > setFormState({ trainingPercent: e.target.value })} + onChange={e => setFormState({ trainingPercent: +e.target.value })} data-test-subj="mlAnalyticsCreateJobFlyoutTrainingPercentSlider" /> diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/index.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/index.ts index 0eb3d7180c200..9df0b542f50a1 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/index.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/index.ts @@ -4,8 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { - useCreateAnalyticsForm, - CreateAnalyticsFormProps, - getErrorMessage, -} from './use_create_analytics_form'; +export { useCreateAnalyticsForm, CreateAnalyticsFormProps } from './use_create_analytics_form'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts index 8112a0fdb9e29..c40ab31f6615f 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts @@ -16,9 +16,11 @@ type SourceIndex = DataFrameAnalyticsConfig['source']['index']; const getMockState = ({ index, + trainingPercent = 75, modelMemoryLimit = '100mb', }: { index: SourceIndex; + trainingPercent?: number; modelMemoryLimit?: string; }) => merge(getInitialState(), { @@ -31,7 +33,9 @@ const getMockState = ({ jobConfig: { source: { index }, dest: { index: 'the-destination-index' }, - analysis: {}, + analysis: { + classification: { dependent_variable: 'the-variable', training_percent: trainingPercent }, + }, model_memory_limit: modelMemoryLimit, }, }); @@ -151,6 +155,24 @@ describe('useCreateAnalyticsForm', () => { .isValid ).toBe(false); }); + + test('validateAdvancedEditor(): check training percent validation', () => { + // valid training_percent value + expect( + validateAdvancedEditor(getMockState({ index: 'the-source-index', trainingPercent: 75 })) + .isValid + ).toBe(true); + // invalid training_percent numeric value + expect( + validateAdvancedEditor(getMockState({ index: 'the-source-index', trainingPercent: 102 })) + .isValid + ).toBe(false); + // invalid training_percent numeric value if 0 + expect( + validateAdvancedEditor(getMockState({ index: 'the-source-index', trainingPercent: 0 })) + .isValid + ).toBe(false); + }); }); describe('validateMinMML', () => { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts index d045749a1a0dd..28d8afbcd88cc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts @@ -11,6 +11,8 @@ import numeral from '@elastic/numeral'; import { isEmpty } from 'lodash'; import { isValidIndexName } from '../../../../../../../common/util/es_utils'; +import { collapseLiteralStrings } from '../../../../../../../../../../src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools'; + import { Action, ACTION } from './actions'; import { getInitialState, getJobConfigFromFormState, State } from './state'; import { @@ -29,9 +31,12 @@ import { } from '../../../../../../../common/constants/validation'; import { getDependentVar, + getTrainingPercent, isRegressionAnalysis, isClassificationAnalysis, ANALYSIS_CONFIG_TYPE, + TRAINING_PERCENT_MIN, + TRAINING_PERCENT_MAX, } from '../../../../common/analytics'; import { indexPatterns } from '../../../../../../../../../../src/plugins/data/public'; @@ -141,6 +146,7 @@ export const validateAdvancedEditor = (state: State): State => { let dependentVariableEmpty = false; let excludesValid = true; + let trainingPercentValid = true; if ( jobConfig.analysis === undefined && @@ -169,6 +175,30 @@ export const validateAdvancedEditor = (state: State): State => { message: '', }); } + + const trainingPercent = getTrainingPercent(jobConfig.analysis); + if ( + trainingPercent !== undefined && + (isNaN(trainingPercent) || + trainingPercent < TRAINING_PERCENT_MIN || + trainingPercent > TRAINING_PERCENT_MAX) + ) { + trainingPercentValid = false; + + state.advancedEditorMessages.push({ + error: i18n.translate( + 'xpack.ml.dataframe.analytics.create.advancedEditorMessage.trainingPercentInvalid', + { + defaultMessage: 'The training percent must be a value between {min} and {max}.', + values: { + min: TRAINING_PERCENT_MIN, + max: TRAINING_PERCENT_MAX, + }, + } + ), + message: '', + }); + } } if (sourceIndexNameEmpty) { @@ -249,6 +279,7 @@ export const validateAdvancedEditor = (state: State): State => { state.isValid = maxDistinctValuesError === undefined && excludesValid && + trainingPercentValid && state.form.modelMemoryLimitUnitValid && !jobIdEmpty && jobIdValid && @@ -365,7 +396,23 @@ export function reducer(state: State, action: Action): State { return getInitialState(); case ACTION.SET_ADVANCED_EDITOR_RAW_STRING: - return { ...state, advancedEditorRawString: action.advancedEditorRawString }; + let resultJobConfig; + try { + resultJobConfig = JSON.parse(collapseLiteralStrings(action.advancedEditorRawString)); + } catch (e) { + return { + ...state, + advancedEditorRawString: action.advancedEditorRawString, + isAdvancedEditorValidJson: false, + advancedEditorMessages: [], + }; + } + + return { + ...validateAdvancedEditor({ ...state, jobConfig: resultJobConfig }), + advancedEditorRawString: action.advancedEditorRawString, + isAdvancedEditorValidJson: true, + }; case ACTION.SET_FORM_STATE: const newFormState = { ...state.form, ...action.payload }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts index 719bb6c5b07c7..fe741fe9a92d4 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts @@ -82,6 +82,7 @@ export interface State { indexNames: EsIndexName[]; indexPatternsMap: SourceIndexMap; isAdvancedEditorEnabled: boolean; + isAdvancedEditorValidJson: boolean; isJobCreated: boolean; isJobStarted: boolean; isModalButtonDisabled: boolean; @@ -140,6 +141,7 @@ export const getInitialState = (): State => ({ indexNames: [], indexPatternsMap: {}, isAdvancedEditorEnabled: false, + isAdvancedEditorValidJson: true, isJobCreated: false, isJobStarted: false, isModalVisible: false, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx index 1a248f8559ffa..182e50a5d74d1 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx @@ -10,7 +10,8 @@ import { mountHook } from 'test_utils/enzyme_helpers'; import { MlContext } from '../../../../../contexts/ml'; import { kibanaContextValueMock } from '../../../../../contexts/ml/__mocks__/kibana_context_value'; -import { getErrorMessage, useCreateAnalyticsForm } from './use_create_analytics_form'; +import { useCreateAnalyticsForm } from './use_create_analytics_form'; +import { getErrorMessage } from '../../../../../../../common/util/errors'; const getMountedHook = () => mountHook( diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index 86c43b232738c..34f1d04264900 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -9,7 +9,7 @@ import { useReducer } from 'react'; import { i18n } from '@kbn/i18n'; import { SimpleSavedObject } from 'kibana/public'; -import { isErrorResponse } from '../../../../../../../common/types/errors'; +import { getErrorMessage } from '../../../../../../../common/util/errors'; import { DeepReadonly } from '../../../../../../../common/types/common'; import { ml } from '../../../../../services/ml_api_service'; import { useMlContext } from '../../../../../contexts/ml'; @@ -41,18 +41,6 @@ export interface CreateAnalyticsFormProps { state: State; } -export function getErrorMessage(error: any) { - if (isErrorResponse(error)) { - return `${error.body.error}: ${error.body.message}`; - } - - if (typeof error === 'object' && typeof error.message === 'string') { - return error.message; - } - - return JSON.stringify(error); -} - export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { const mlContext = useMlContext(); const [state, dispatch] = useReducer(reducer, getInitialState()); diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx index ef13fec3589fb..16004475eb44f 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx @@ -6,14 +6,22 @@ import React, { FC, useState } from 'react'; -import { EuiFlexItem, EuiFlexGroup, EuiIconTip, EuiSuperSelect, EuiText } from '@elastic/eui'; +import { + EuiCode, + EuiFlexItem, + EuiFlexGroup, + EuiIconTip, + EuiInputPopover, + EuiSuperSelect, + EuiText, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { IndexPattern } from '../../../../../../../../../src/plugins/data/public'; -import { SEARCH_QUERY_LANGUAGE } from '../../../../../../common/constants/search'; +import { SEARCH_QUERY_LANGUAGE, ErrorMessage } from '../../../../../../common/constants/search'; import { esKuery, @@ -22,8 +30,6 @@ import { QueryStringInput, } from '../../../../../../../../../src/plugins/data/public'; -import { getToastNotifications } from '../../../../util/dependency_cache'; - interface Props { indexPattern: IndexPattern; searchString: Query['query']; @@ -73,6 +79,7 @@ export const SearchPanel: FC = ({ query: searchString || '', language: searchQueryLanguage, }); + const [errorMessage, setErrorMessage] = useState(undefined); const searchHandler = (query: Query) => { let filterQuery; @@ -93,13 +100,7 @@ export const SearchPanel: FC = ({ setSearchQueryLanguage(query.language); } catch (e) { console.log('Invalid syntax', JSON.stringify(e, null, 2)); // eslint-disable-line no-console - const toastNotifications = getToastNotifications(); - toastNotifications.addError(e, { - title: i18n.translate('xpack.ml.datavisualizer.invalidSyntaxErrorMessage', { - defaultMessage: 'Invalid syntax in search bar', - }), - toastMessage: e.message ? e.message : e, - }); + setErrorMessage({ query: query.query as string, message: e.message }); } }; const searchChangeHandler = (query: Query) => setSearchInput(query); @@ -107,22 +108,40 @@ export const SearchPanel: FC = ({ return ( - + setErrorMessage(undefined)} + input={ + + } + isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} + > + + {i18n.translate( + 'xpack.ml.datavisualizer.searchPanel.invalidKuerySyntaxErrorMessageQueryBar', + { + defaultMessage: 'Invalid query', + } + )} + {': '} + {errorMessage?.message.split('\n')[0]} + + diff --git a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx index 0789a7f8ef5ff..0263ad08b03cf 100644 --- a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx +++ b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx @@ -5,6 +5,8 @@ */ import React, { FC, useState, useEffect } from 'react'; +import { EuiCode, EuiInputPopover } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { Query, esKuery, @@ -12,10 +14,10 @@ import { QueryStringInput, } from '../../../../../../../../src/plugins/data/public'; import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns'; -import { QUERY_LANGUAGE_KUERY, QUERY_LANGUAGE_LUCENE } from '../../explorer_constants'; +import { SEARCH_QUERY_LANGUAGE, ErrorMessage } from '../../../../../common/constants/search'; import { explorerService } from '../../explorer_dashboard_service'; -export const DEFAULT_QUERY_LANG = QUERY_LANGUAGE_KUERY; +export const DEFAULT_QUERY_LANG = SEARCH_QUERY_LANGUAGE.KUERY; export function getKqlQueryValues({ inputString, @@ -25,11 +27,11 @@ export function getKqlQueryValues({ inputString: string | { [key: string]: any }; queryLanguage: string; indexPattern: IIndexPattern; -}) { +}): { clearSettings: boolean; settings: any } { let influencersFilterQuery: any = {}; - const ast = esKuery.fromKueryExpression(inputString); - const isAndOperator = ast.function === 'and'; const filteredFields: string[] = []; + const ast = esKuery.fromKueryExpression(inputString); + const isAndOperator = ast && ast.function === 'and'; // if ast.type == 'function' then layout of ast.arguments: // [{ arguments: [ { type: 'literal', value: 'AAL' } ] },{ arguments: [ { type: 'literal', value: 'AAL' } ] }] if (ast && Array.isArray(ast.arguments)) { @@ -45,12 +47,12 @@ export function getKqlQueryValues({ } }); } - if (queryLanguage === QUERY_LANGUAGE_KUERY) { + if (queryLanguage === SEARCH_QUERY_LANGUAGE.KUERY) { influencersFilterQuery = esKuery.toElasticsearchQuery( esKuery.fromKueryExpression(inputString), indexPattern ); - } else if (queryLanguage === QUERY_LANGUAGE_LUCENE) { + } else if (queryLanguage === SEARCH_QUERY_LANGUAGE.LUCENE) { influencersFilterQuery = esQuery.luceneStringToDsl(inputString); } @@ -78,7 +80,7 @@ function getInitSearchInputState({ }) { if (queryString !== undefined && filterActive === true) { return { - language: QUERY_LANGUAGE_KUERY, + language: SEARCH_QUERY_LANGUAGE.KUERY, query: queryString, }; } else { @@ -110,6 +112,7 @@ export const ExplorerQueryBar: FC = ({ const [searchInput, setSearchInput] = useState( getInitSearchInputState({ filterActive, queryString }) ); + const [errorMessage, setErrorMessage] = useState(undefined); useEffect(() => { if (filterIconTriggeredQuery !== undefined) { @@ -127,30 +130,50 @@ export const ExplorerQueryBar: FC = ({ setSearchInput(query); }; const applyInfluencersFilterQuery = (query: Query) => { - const { clearSettings, settings } = getKqlQueryValues({ - inputString: query.query, - queryLanguage: query.language, - indexPattern, - }); + try { + const { clearSettings, settings } = getKqlQueryValues({ + inputString: query.query, + queryLanguage: query.language, + indexPattern, + }); - if (clearSettings === true) { - explorerService.clearInfluencerFilterSettings(); - } else { - explorerService.setInfluencerFilterSettings(settings); + if (clearSettings === true) { + explorerService.clearInfluencerFilterSettings(); + } else { + explorerService.setInfluencerFilterSettings(settings); + } + } catch (e) { + console.log('Invalid query syntax in search bar', e); // eslint-disable-line no-console + setErrorMessage({ query: query.query as string, message: e.message }); } }; return ( - + setErrorMessage(undefined)} + input={ + + } + isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} + > + + {i18n.translate('xpack.ml.explorer.invalidKuerySyntaxErrorMessageQueryBar', { + defaultMessage: 'Invalid query', + })} + {': '} + {errorMessage?.message.split('\n')[0]} + + ); }; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer.js b/x-pack/plugins/ml/public/application/explorer/explorer.js index 9b02150bae9bb..d61d56d07b644 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer.js @@ -58,13 +58,12 @@ import { DEFAULT_QUERY_LANG, } from './components/explorer_query_bar/explorer_query_bar'; import { + getDateFormatTz, removeFilterFromQueryString, getQueryPattern, escapeParens, escapeDoubleQuotes, -} from '../components/kql_filter_bar/utils'; - -import { getDateFormatTz } from './explorer_utils'; +} from './explorer_utils'; import { getSwimlaneContainerWidth } from './legacy_utils'; import { @@ -266,7 +265,7 @@ export class Explorer extends React.Component { explorerService.setInfluencerFilterSettings(settings); } } catch (e) { - console.log('Invalid kuery syntax', e); // eslint-disable-line no-console + console.log('Invalid query syntax from table', e); // eslint-disable-line no-console const toastNotifications = getToastNotifications(); toastNotifications.addDanger( diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_constants.ts b/x-pack/plugins/ml/public/application/explorer/explorer_constants.ts index afec50eae06aa..b084f503272cc 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_constants.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_constants.ts @@ -25,7 +25,6 @@ export const EXPLORER_ACTION = { SET_EXPLORER_DATA: 'setExplorerData', SET_FILTER_DATA: 'setFilterData', SET_INFLUENCER_FILTER_SETTINGS: 'setInfluencerFilterSettings', - SET_SEARCH_INPUT: 'setSearchInput', SET_SELECTED_CELLS: 'setSelectedCells', SET_SWIMLANE_CONTAINER_WIDTH: 'setSwimlaneContainerWidth', SET_SWIMLANE_LIMIT: 'setSwimlaneLimit', @@ -56,7 +55,3 @@ export const MAX_INFLUENCER_FIELD_NAMES = 50; export const VIEW_BY_JOB_LABEL = i18n.translate('xpack.ml.explorer.jobIdLabel', { defaultMessage: 'job ID', }); - -export const QUERY_LANGUAGE_KUERY = 'kuery'; -export const QUERY_LANGUAGE_LUCENE = 'lucene'; -export type QUERY_LANGUAGE = 'kuery' | 'lucene'; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_dashboard_service.ts b/x-pack/plugins/ml/public/application/explorer/explorer_dashboard_service.ts index 277c1aa6f4566..89e1a908b1ecc 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_dashboard_service.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_dashboard_service.ts @@ -132,12 +132,6 @@ export const explorerService = { payload, }); }, - setSearchInput: (query: any) => { - explorerAction$.next({ - type: EXPLORER_ACTION.SET_SEARCH_INPUT, - payload: query, - }); - }, setSelectedCells: (payload: AppStateSelectedCells | undefined) => { explorerAction$.next({ type: EXPLORER_ACTION.SET_SELECTED_CELLS, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.js b/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.js index e8cb8377a656d..d7333f00c89cd 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.js @@ -25,6 +25,7 @@ import { mlChartTooltipService } from '../components/chart_tooltip/chart_tooltip import { ALLOW_CELL_RANGE_SELECTION, dragSelect$ } from './explorer_dashboard_service'; import { DRAG_SELECT_ACTION } from './explorer_constants'; import { i18n } from '@kbn/i18n'; +import { EMPTY_FIELD_VALUE_LABEL } from '../timeseriesexplorer/components/entity_control/entity_control'; const SCSS = { mlDragselectDragging: 'mlDragselectDragging', @@ -309,6 +310,7 @@ export class ExplorerSwimlane extends React.Component { return function(lane) { const bucketScore = getBucketScore(lane, time); if (bucketScore !== 0) { + lane = lane === '' ? EMPTY_FIELD_VALUE_LABEL : lane; cellMouseover(this, lane, bucketScore, i, time); } }; @@ -376,7 +378,7 @@ export class ExplorerSwimlane extends React.Component { values: { label: mlEscape(label) }, }); } else { - return mlEscape(label); + return label === '' ? `${EMPTY_FIELD_VALUE_LABEL}` : mlEscape(label); } }) .on('click', () => { @@ -393,7 +395,7 @@ export class ExplorerSwimlane extends React.Component { { skipHeader: true }, { label: swimlaneData.fieldName, - value, + value: value === '' ? EMPTY_FIELD_VALUE_LABEL : value, seriesIdentifier: { key: value }, valueAccessor: 'fieldName', }, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js index 0b41f789bb571..852b16ec581bb 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js @@ -883,3 +883,42 @@ export async function loadTopInfluencers( } }); } + +// Recommended by MDN for escaping user input to be treated as a literal string within a regular expression +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions +export function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +export function escapeParens(string) { + return string.replace(/[()]/g, '\\$&'); +} + +export function escapeDoubleQuotes(string) { + return string.replace(/\"/g, '\\$&'); +} + +export function getQueryPattern(fieldName, fieldValue) { + const sanitizedFieldName = escapeRegExp(fieldName); + const sanitizedFieldValue = escapeRegExp(fieldValue); + + return new RegExp(`(${sanitizedFieldName})\\s?:\\s?(")?(${sanitizedFieldValue})(")?`, 'i'); +} + +export function removeFilterFromQueryString(currentQueryString, fieldName, fieldValue) { + let newQueryString = ''; + // Remove the passed in fieldName and value from the existing filter + const queryPattern = getQueryPattern(fieldName, fieldValue); + newQueryString = currentQueryString.replace(queryPattern, ''); + // match 'and' or 'or' at the start/end of the string + const endPattern = /\s(and|or)\s*$/gi; + const startPattern = /^\s*(and|or)\s/gi; + // If string has a double operator (e.g. tag:thing or or tag:other) remove and replace with the first occurring operator + const invalidOperatorPattern = /\s+(and|or)\s+(and|or)\s+/gi; + newQueryString = newQueryString.replace(invalidOperatorPattern, ' $1 '); + // If string starts/ends with 'and' or 'or' remove that as that is illegal kuery syntax + newQueryString = newQueryString.replace(endPattern, ''); + newQueryString = newQueryString.replace(startPattern, ''); + + return newQueryString; +} diff --git a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts index ff659029e38d7..c31b26b7adb7b 100644 --- a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts +++ b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts @@ -68,13 +68,6 @@ export const explorerReducer = (state: ExplorerState, nextAction: Action): Explo nextState = setInfluencerFilterSettings(state, payload); break; - case EXPLORER_ACTION.SET_SEARCH_INPUT: - nextState = { - ...state, - searchInput: payload, - }; - break; - case EXPLORER_ACTION.SET_SELECTED_CELLS: const selectedCells = payload; nextState = { diff --git a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts index 44e1486508ea3..0a2dbf5bcff35 100644 --- a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts +++ b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts @@ -20,8 +20,6 @@ import { TimeRangeBounds, } from '../../explorer_utils'; -import { QUERY_LANGUAGE_KUERY, QUERY_LANGUAGE } from '../../explorer_constants'; // QUERY_LANGUAGE_LUCENE - export interface ExplorerState { annotationsData: any[]; bounds: TimeRangeBounds | undefined; @@ -39,10 +37,6 @@ export interface ExplorerState { noInfluencersConfigured: boolean; overallSwimlaneData: SwimlaneData; queryString: string; - searchInput: { - query: string; - language: QUERY_LANGUAGE; - }; selectedCells: AppStateSelectedCells | undefined; selectedJobs: ExplorerJob[] | null; swimlaneBucketInterval: any; @@ -79,10 +73,6 @@ export function getExplorerDefaultState(): ExplorerState { noInfluencersConfigured: true, overallSwimlaneData: getDefaultSwimlaneData(), queryString: '', - searchInput: { - query: '', - language: QUERY_LANGUAGE_KUERY, - }, selectedCells: undefined, selectedJobs: null, swimlaneBucketInterval: undefined, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js index 29c79458fe431..9066e41fb8f23 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js @@ -366,7 +366,7 @@ export class EditJobFlyoutUI extends Component { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js index 37b9fe5e1f2d0..1f2a57f999775 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js @@ -388,17 +388,23 @@ function getUrlVars(url) { } export function getSelectedJobIdFromUrl(url) { - if (typeof url === 'string' && url.includes('mlManagement') && url.includes('jobId')) { - const urlParams = getUrlVars(url); - const decodedJson = rison.decode(urlParams.mlManagement); - return decodedJson.jobId; + if (typeof url === 'string') { + url = decodeURIComponent(url); + if (url.includes('mlManagement') && url.includes('jobId')) { + const urlParams = getUrlVars(url); + const decodedJson = rison.decode(urlParams.mlManagement); + return decodedJson.jobId; + } } } export function clearSelectedJobIdFromUrl(url) { - if (typeof url === 'string' && url.includes('mlManagement') && url.includes('jobId')) { - const urlParams = getUrlVars(url); - const clearedParams = `ml#/jobs?_g=${urlParams._g}`; - window.history.replaceState({}, document.title, clearedParams); + if (typeof url === 'string') { + url = decodeURIComponent(url); + if (url.includes('mlManagement') && url.includes('jobId')) { + const urlParams = getUrlVars(url); + const clearedParams = `ml#/jobs?_g=${urlParams._g}`; + window.history.replaceState({}, document.title, clearedParams); + } } } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx index c24c018f50d75..2e7cc9c413a25 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { JobRunner } from '../../../../../common/job_runner'; import { useMlKibana } from '../../../../../../../contexts/kibana'; +import { getErrorMessage } from '../../../../../../../../../common/util/errors'; // @ts-ignore import { CreateWatchFlyout } from '../../../../../../jobs_list/components/create_watch_flyout/index'; @@ -69,7 +70,7 @@ export const PostSaveOptions: FC = ({ jobRunner }) => { defaultMessage: `Error starting job`, } ), - text: error.message, + text: getErrorMessage(error), }); } } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx index 75994b5358899..d8cd0f5e4f1f0 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx @@ -24,6 +24,7 @@ import { mlJobService } from '../../../../../services/job_service'; import { JsonEditorFlyout, EDITOR_MODE } from '../common/json_editor_flyout'; import { DatafeedPreviewFlyout } from '../common/datafeed_preview_flyout'; import { JOB_TYPE } from '../../../../../../../common/constants/new_job'; +import { getErrorMessage } from '../../../../../../../common/util/errors'; import { isSingleMetricJobCreator, isAdvancedJobCreator } from '../../../common/job_creator'; import { JobDetails } from './components/job_details'; import { DatafeedDetails } from './components/datafeed_details'; @@ -75,7 +76,7 @@ export const SummaryStep: FC = ({ setCurrentStep, isCurrentStep }) => title: i18n.translate('xpack.ml.newJob.wizard.summaryStep.createJobError', { defaultMessage: `Job creation error`, }), - text: error.message, + text: getErrorMessage(error), }); setCreatingJob(false); } @@ -94,7 +95,7 @@ export const SummaryStep: FC = ({ setCurrentStep, isCurrentStep }) => title: i18n.translate('xpack.ml.newJob.wizard.summaryStep.createJobError', { defaultMessage: `Job creation error`, }), - text: error.message, + text: getErrorMessage(error), }); setCreatingJob(false); } diff --git a/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx b/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx index e00ff0333bb73..2dde5426ec9a0 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx @@ -13,7 +13,6 @@ import { useResolver } from '../../use_resolver'; import { basicResolvers } from '../../resolvers'; import { Page } from '../../../data_frame_analytics/pages/analytics_exploration'; import { ANALYSIS_CONFIG_TYPE } from '../../../data_frame_analytics/common/analytics'; -import { DATA_FRAME_TASK_STATE } from '../../../data_frame_analytics/pages/analytics_management/components/analytics_list/common'; import { ML_BREADCRUMB } from '../../breadcrumbs'; const breadcrumbs = [ @@ -46,11 +45,10 @@ const PageWrapper: FC = ({ location, deps }) => { } const jobId: string = globalState.ml.jobId; const analysisType: ANALYSIS_CONFIG_TYPE = globalState.ml.analysisType; - const jobStatus: DATA_FRAME_TASK_STATE = globalState.ml.jobStatus; return ( - + ); }; diff --git a/x-pack/plugins/ml/public/application/routing/use_resolver.ts b/x-pack/plugins/ml/public/application/routing/use_resolver.ts index 6df7eee3d64a6..8e94f8d77fbb2 100644 --- a/x-pack/plugins/ml/public/application/routing/use_resolver.ts +++ b/x-pack/plugins/ml/public/application/routing/use_resolver.ts @@ -6,6 +6,7 @@ import { useEffect, useState } from 'react'; import { IUiSettingsClient } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; import { getIndexPatternById, getIndexPatternsContract, @@ -14,6 +15,7 @@ import { import { createSearchItems } from '../jobs/new_job/utils/new_job_utils'; import { ResolverResults, Resolvers } from './resolvers'; import { MlContextValue } from '../contexts/ml'; +import { useNotifications } from '../contexts/kibana'; export const useResolver = ( indexPatternId: string | undefined, @@ -21,6 +23,8 @@ export const useResolver = ( config: IUiSettingsClient, resolvers: Resolvers ): { context: MlContextValue; results: ResolverResults } => { + const notifications = useNotifications(); + const funcNames = Object.keys(resolvers); // Object.entries gets this wrong?! const funcs = Object.values(resolvers); // Object.entries gets this wrong?! const tempResults = funcNames.reduce((p, c) => { @@ -37,8 +41,14 @@ export const useResolver = ( const res = await Promise.all(funcs.map(r => r())); res.forEach((r, i) => (tempResults[funcNames[i]] = r)); setResults(tempResults); + } catch (error) { + // quietly fail. Errors being thrown here are expected as a way to handle privilege or license check failures. + // The user will be redirected by the failed resolver. + return; + } - if (indexPatternId !== undefined || savedSearchId !== undefined) { + if (indexPatternId !== undefined || savedSearchId !== undefined) { + try { // note, currently we're using our own kibana context that requires a current index pattern to be set // this means, if the page uses this context, useResolver must be passed a string for the index pattern id // and loadIndexPatterns must be part of the resolvers. @@ -56,11 +66,17 @@ export const useResolver = ( indexPatterns: getIndexPatternsContract()!, kibanaConfig: config, }); - } else { - setContext({}); + } catch (error) { + // an unexpected error has occurred. This could be caused by an incorrect index pattern or saved search ID + notifications.toasts.addError(new Error(error), { + title: i18n.translate('xpack.ml.useResolver.errorTitle', { + defaultMessage: 'An error has occurred', + }), + }); + window.location.href = '#/'; } - } catch (error) { - // quietly fail. Let the resolvers handle the redirection if any fail to resolve + } else { + setContext({}); } })(); }, []); diff --git a/x-pack/plugins/ml/public/application/services/job_service.js b/x-pack/plugins/ml/public/application/services/job_service.js index a84400f236134..bbfec49ac1388 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -169,12 +169,12 @@ class JobService { function error(err) { console.log('jobService error getting list of jobs:', err); - msgs.error( + msgs.notify.error( i18n.translate('xpack.ml.jobService.jobsListCouldNotBeRetrievedErrorMessage', { defaultMessage: 'Jobs list could not be retrieved', }) ); - msgs.error('', err); + msgs.notify.error('', err); reject({ jobs, err }); } }); @@ -256,12 +256,12 @@ class JobService { function error(err) { console.log('JobService error getting list of jobs:', err); - msgs.error( + msgs.notify.error( i18n.translate('xpack.ml.jobService.jobsListCouldNotBeRetrievedErrorMessage', { defaultMessage: 'Jobs list could not be retrieved', }) ); - msgs.error('', err); + msgs.notify.error('', err); reject({ jobs, err }); } }); @@ -302,12 +302,12 @@ class JobService { function error(err) { console.log('loadDatafeeds error getting list of datafeeds:', err); - msgs.error( + msgs.notify.error( i18n.translate('xpack.ml.jobService.datafeedsListCouldNotBeRetrievedErrorMessage', { defaultMessage: 'datafeeds list could not be retrieved', }) ); - msgs.error('', err); + msgs.notify.error('', err); reject({ jobs, err }); } }); @@ -586,6 +586,7 @@ class JobService { const data = { index: job.datafeed_config.indices, body, + ...(job.datafeed_config.indices_options || {}), }; ml.esSearch(data) diff --git a/x-pack/plugins/ml/public/application/services/results_service/results_service.js b/x-pack/plugins/ml/public/application/services/results_service/results_service.js index 4dec066a7f325..b7aa5edc88638 100644 --- a/x-pack/plugins/ml/public/application/services/results_service/results_service.js +++ b/x-pack/plugins/ml/public/application/services/results_service/results_service.js @@ -1259,39 +1259,13 @@ export function getRecordMaxScoreByTime(jobId, criteriaFields, earliestMs, lates }, { term: { job_id: jobId } }, ]; - const shouldCriteria = []; _.each(criteriaFields, criteria => { - if (criteria.fieldValue.length !== 0) { - mustCriteria.push({ - term: { - [criteria.fieldName]: criteria.fieldValue, - }, - }); - } else { - // Add special handling for blank entity field values, checking for either - // an empty string or the field not existing. - const emptyFieldCondition = { - bool: { - must: [ - { - term: {}, - }, - ], - }, - }; - emptyFieldCondition.bool.must[0].term[criteria.fieldName] = ''; - shouldCriteria.push(emptyFieldCondition); - shouldCriteria.push({ - bool: { - must_not: [ - { - exists: { field: criteria.fieldName }, - }, - ], - }, - }); - } + mustCriteria.push({ + term: { + [criteria.fieldName]: criteria.fieldValue, + }, + }); }); ml.esSearch({ diff --git a/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js b/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js index f06812b2a9128..50777485903d2 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js @@ -7,6 +7,7 @@ import { getToastNotifications } from '../../../util/dependency_cache'; import { ml } from '../../../services/ml_api_service'; import { i18n } from '@kbn/i18n'; +import { getErrorMessage } from '../../../../../common/util/errors'; export async function deleteCalendars(calendarsToDelete, callback) { if (calendarsToDelete === undefined || calendarsToDelete.length === 0) { @@ -36,17 +37,18 @@ export async function deleteCalendars(calendarsToDelete, callback) { await ml.deleteCalendar({ calendarId }); } catch (error) { console.log('Error deleting calendar:', error); - const errorMessage = i18n.translate( - 'xpack.ml.calendarsList.deleteCalendars.deletingCalendarErrorMessage', - { - defaultMessage: 'An error occurred deleting calendar {calendarId}{errorMessage}', - values: { - calendarId: calendar.calendar_id, - errorMessage: error.message ? ` : ${error.message}` : '', - }, - } - ); - toastNotifications.addDanger(errorMessage); + toastNotifications.addDanger({ + title: i18n.translate( + 'xpack.ml.calendarsList.deleteCalendars.deletingCalendarErrorMessage', + { + defaultMessage: 'An error occurred deleting calendar {calendarId}', + values: { + calendarId: calendar.calendar_id, + }, + } + ), + text: getErrorMessage(error), + }); } } diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_control.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_control.tsx index 8911ed53e74d0..7bb0b27472c88 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_control.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_control.tsx @@ -16,6 +16,7 @@ import { EuiFormRow, EuiToolTip, } from '@elastic/eui'; +import { EuiSelectableOption } from '@elastic/eui/src/components/selectable/selectable_option'; export interface Entity { fieldName: string; @@ -29,15 +30,22 @@ interface EntityControlProps { isLoading: boolean; onSearchChange: (entity: Entity, queryTerm: string) => void; forceSelection: boolean; - options: EuiComboBoxOptionOption[]; + options: Array>; } interface EntityControlState { - selectedOptions: EuiComboBoxOptionOption[] | undefined; + selectedOptions: Array> | undefined; isLoading: boolean; - options: EuiComboBoxOptionOption[] | undefined; + options: Array> | undefined; } +export const EMPTY_FIELD_VALUE_LABEL = i18n.translate( + 'xpack.ml.timeSeriesExplorer.emptyPartitionFieldLabel.', + { + defaultMessage: '"" (empty string)', + } +); + export class EntityControl extends Component { inputRef: any; @@ -53,16 +61,18 @@ export class EntityControl extends Component> | undefined = selectedOptions; if ( - (selectedOptions === undefined && fieldValue.length > 0) || + (selectedOptions === undefined && fieldValue !== null) || (Array.isArray(selectedOptions) && // @ts-ignore - selectedOptions[0].label !== fieldValue && - fieldValue.length > 0) + selectedOptions[0].value !== fieldValue && + fieldValue !== null) ) { - selectedOptionsUpdate = [{ label: fieldValue }]; - } else if (Array.isArray(selectedOptions) && fieldValue.length === 0) { + selectedOptionsUpdate = [ + { label: fieldValue === '' ? EMPTY_FIELD_VALUE_LABEL : fieldValue, value: fieldValue }, + ]; + } else if (Array.isArray(selectedOptions) && fieldValue === null) { selectedOptionsUpdate = undefined; } @@ -84,14 +94,14 @@ export class EntityControl extends Component { + onChange = (selectedOptions: Array>) => { const options = selectedOptions.length > 0 ? selectedOptions : undefined; this.setState({ selectedOptions: options, }); const fieldValue = - Array.isArray(options) && options[0].label.length > 0 ? options[0].label : ''; + Array.isArray(options) && options[0].value !== null ? options[0].value : null; this.props.entityFieldValueChanged(this.props.entity, fieldValue); }; @@ -103,6 +113,11 @@ export class EntityControl extends Component { + const { label } = option; + return label === EMPTY_FIELD_VALUE_LABEL ? {label} : label; + }; + render() { const { entity, forceSelection } = this.props; const { isLoading, options, selectedOptions } = this.state; @@ -126,6 +141,7 @@ export class EntityControl extends Component ); diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts index db5ff2ad91910..f973d41ad7754 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts @@ -128,7 +128,7 @@ function getChartDetails( obj.results.functionLabel = functionLabel; const blankEntityFields = _.filter(entityFields, entity => { - return entity.fieldValue.length === 0; + return entity.fieldValue === null; }); // Look to see if any of the entity fields have defined values diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index 1a26540709f34..5e505757dd2aa 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -78,6 +78,7 @@ import { processRecordScoreResults, getFocusData, } from './timeseriesexplorer_utils'; +import { EMPTY_FIELD_VALUE_LABEL } from './components/entity_control/entity_control'; // Used to indicate the chart is being plotted across // all partition field values, where the cardinality of the field cannot be @@ -94,7 +95,7 @@ function getEntityControlOptions(fieldValues) { fieldValues.sort(); return fieldValues.map(value => { - return { label: value }; + return { label: value === '' ? EMPTY_FIELD_VALUE_LABEL : value, value }; }); } @@ -192,7 +193,7 @@ export class TimeSeriesExplorer extends React.Component { getFieldNamesWithEmptyValues = () => { const latestEntityControls = this.getControlsForDetector(); return latestEntityControls - .filter(({ fieldValue }) => !fieldValue) + .filter(({ fieldValue }) => fieldValue === null) .map(({ fieldName }) => fieldName); }; @@ -249,7 +250,7 @@ export class TimeSeriesExplorer extends React.Component { if (operator === '+' && entity.fieldValue !== value) { resultValue = value; } else if (operator === '-' && entity.fieldValue === value) { - resultValue = ''; + resultValue = null; } else { return; } @@ -302,7 +303,7 @@ export class TimeSeriesExplorer extends React.Component { focusAggregationInterval, selectedForecastId, modelPlotEnabled, - entityControls.filter(entity => entity.fieldValue.length > 0), + entityControls.filter(entity => entity.fieldValue !== null), searchBounds, selectedJob, TIME_FIELD_NAME @@ -576,7 +577,7 @@ export class TimeSeriesExplorer extends React.Component { }; const nonBlankEntities = entityControls.filter(entity => { - return entity.fieldValue.length > 0; + return entity.fieldValue !== null; }); if ( @@ -739,7 +740,7 @@ export class TimeSeriesExplorer extends React.Component { const overFieldName = get(detector, 'over_field_name'); const byFieldName = get(detector, 'by_field_name'); if (partitionFieldName !== undefined) { - const partitionFieldValue = get(entitiesState, partitionFieldName, ''); + const partitionFieldValue = get(entitiesState, partitionFieldName, null); entities.push({ fieldType: 'partition_field', fieldName: partitionFieldName, @@ -747,7 +748,7 @@ export class TimeSeriesExplorer extends React.Component { }); } if (overFieldName !== undefined) { - const overFieldValue = get(entitiesState, overFieldName, ''); + const overFieldValue = get(entitiesState, overFieldName, null); entities.push({ fieldType: 'over_field', fieldName: overFieldName, @@ -761,7 +762,7 @@ export class TimeSeriesExplorer extends React.Component { // TODO - metric data can be filtered by this field, so should only exclude // from filter for the anomaly records. if (byFieldName !== undefined && overFieldName === undefined) { - const byFieldValue = get(entitiesState, byFieldName, ''); + const byFieldValue = get(entitiesState, byFieldName, null); entities.push({ fieldType: 'by_field', fieldName: byFieldName, fieldValue: byFieldValue }); } @@ -775,7 +776,7 @@ export class TimeSeriesExplorer extends React.Component { */ getCriteriaFields(detectorIndex, entities) { // Only filter on the entity if the field has a value. - const nonBlankEntities = entities.filter(entity => entity.fieldValue.length > 0); + const nonBlankEntities = entities.filter(entity => entity.fieldValue !== null); return [ { fieldName: 'detector_index', @@ -1150,7 +1151,7 @@ export class TimeSeriesExplorer extends React.Component { {entityControls.map(entity => { const entityKey = `${entity.fieldName}`; - const forceSelection = !hasEmptyFieldValues && !entity.fieldValue; + const forceSelection = !hasEmptyFieldValues && entity.fieldValue === null; hasEmptyFieldValues = !hasEmptyFieldValues && forceSelection; return ( { } as never) as SavedObjectsClientContract ); - const moduleIds = [ - 'apache_ecs', - 'apm_jsbase', - 'apm_nodejs', - 'apm_transaction', - 'auditbeat_process_docker_ecs', - 'auditbeat_process_hosts_ecs', - 'logs_ui_analysis', - 'logs_ui_categories', - 'metricbeat_system_ecs', - 'nginx_ecs', - 'sample_data_ecommerce', - 'sample_data_weblogs', - 'siem_auditbeat', - 'siem_auditbeat_auth', - 'siem_packetbeat', - 'siem_winlogbeat', - 'siem_winlogbeat_auth', - 'uptime_heartbeat', - ]; - - // check all module IDs are the same as the list above - it('listModules - check all module IDs', async () => { - const modules = await dr.listModules(); - const ids = modules.map(m => m.id); - expect(ids.join()).toEqual(moduleIds.join()); - }); - - it('getModule - load a single module', async () => { - const module = await dr.getModule(moduleIds[0]); - expect(module.id).toEqual(moduleIds[0]); - }); - describe('jobOverrides', () => { it('should apply job overrides correctly', () => { // arrange diff --git a/x-pack/plugins/ml/server/models/fields_service/fields_service.ts b/x-pack/plugins/ml/server/models/fields_service/fields_service.ts index 567c5d2afb7de..6558d0d48ded9 100644 --- a/x-pack/plugins/ml/server/models/fields_service/fields_service.ts +++ b/x-pack/plugins/ml/server/models/fields_service/fields_service.ts @@ -6,6 +6,7 @@ import Boom from 'boom'; import { APICaller } from 'kibana/server'; +import { duration } from 'moment'; import { parseInterval } from '../../../common/util/parse_interval'; import { initCardinalityFieldsCache } from './fields_aggs_cache'; @@ -16,6 +17,19 @@ import { initCardinalityFieldsCache } from './fields_aggs_cache'; export function fieldsServiceProvider(callAsCurrentUser: APICaller) { const fieldsAggsCache = initCardinalityFieldsCache(); + /** + * Caps the time range to the last 90 days if necessary + */ + function getSafeTimeRange(earliestMs: number, latestMs: number): { start: number; end: number } { + const capOffsetMs = duration(3, 'months').asMilliseconds(); + const capRangeStart = latestMs - capOffsetMs; + + return { + start: Math.max(earliestMs, capRangeStart), + end: latestMs, + }; + } + /** * Gets aggregatable fields. */ @@ -61,12 +75,14 @@ export function fieldsServiceProvider(callAsCurrentUser: APICaller) { return {}; } + const { start, end } = getSafeTimeRange(earliestMs, latestMs); + const cachedValues = fieldsAggsCache.getValues( index, timeFieldName, - earliestMs, - latestMs, + start, + end, 'overallCardinality', fieldNames ) ?? {}; @@ -84,8 +100,8 @@ export function fieldsServiceProvider(callAsCurrentUser: APICaller) { { range: { [timeFieldName]: { - gte: earliestMs, - lte: latestMs, + gte: start, + lte: end, format: 'epoch_millis', }, }, @@ -130,7 +146,7 @@ export function fieldsServiceProvider(callAsCurrentUser: APICaller) { return obj; }, {} as { [field: string]: number }); - fieldsAggsCache.updateValues(index, timeFieldName, earliestMs, latestMs, { + fieldsAggsCache.updateValues(index, timeFieldName, start, end, { overallCardinality: aggResult, }); @@ -185,15 +201,16 @@ export function fieldsServiceProvider(callAsCurrentUser: APICaller) { } /** - * Caps provided time boundaries based on the interval. - * @param earliestMs - * @param latestMs - * @param interval + * Caps provided time boundaries based on the interval */ - function getSafeTimeRange( + function getSafeTimeRangeForInterval( + interval: string, + ...timeRange: number[] + ): { start: number; end: number }; + function getSafeTimeRangeForInterval( + interval: string, earliestMs: number, - latestMs: number, - interval: string + latestMs: number ): { start: number; end: number } { const maxNumberOfBuckets = 1000; const end = latestMs; @@ -234,7 +251,7 @@ export function fieldsServiceProvider(callAsCurrentUser: APICaller) { interval: string | undefined ): Promise<{ [key: string]: number }> { if (!interval) { - throw new Error('Interval is required to retrieve max bucket cardinalities.'); + throw Boom.badRequest('Interval is required to retrieve max bucket cardinalities.'); } const aggregatableFields = await getAggregatableFields(index, fieldNames); @@ -243,12 +260,17 @@ export function fieldsServiceProvider(callAsCurrentUser: APICaller) { return {}; } + const { start, end } = getSafeTimeRangeForInterval( + interval, + ...Object.values(getSafeTimeRange(earliestMs, latestMs)) + ); + const cachedValues = fieldsAggsCache.getValues( index, timeFieldName, - earliestMs, - latestMs, + start, + end, 'maxBucketCardinality', fieldNames ) ?? {}; @@ -260,8 +282,6 @@ export function fieldsServiceProvider(callAsCurrentUser: APICaller) { return cachedValues; } - const { start, end } = getSafeTimeRange(earliestMs, latestMs, interval); - const mustCriteria = [ { range: { @@ -334,6 +354,10 @@ export function fieldsServiceProvider(callAsCurrentUser: APICaller) { return obj; }, {} as { [field: string]: number }); + fieldsAggsCache.updateValues(index, timeFieldName, start, end, { + maxBucketCardinality: aggResult, + }); + return { ...cachedValues, ...aggResult, diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 674c3886c12f8..7d3ef116e67ab 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -11,6 +11,7 @@ import { IScopedClusterClient, Logger, PluginInitializerContext, + ICustomClusterClient, } from 'kibana/server'; import { PluginsSetup, RouteInitialization } from './types'; import { PLUGIN_ID, PLUGIN_ICON } from '../common/constants/app'; @@ -49,7 +50,9 @@ declare module 'kibana/server' { } } -export type MlPluginSetup = SharedServices; +export interface MlPluginSetup extends SharedServices { + mlClient: ICustomClusterClient; +} export type MlPluginStart = void; export class MlServerPlugin implements Plugin { @@ -135,7 +138,10 @@ export class MlServerPlugin implements Plugin { }, 'localhost:9300' ) + ).toEqual({ + name: 'test_cluster', + proxyAddress: 'localhost:9300', + mode: 'proxy', + hasDeprecatedProxySetting: true, + isConnected: true, + connectedNodesCount: 1, + maxConnectionsPerCluster: 3, + initialConnectTimeout: '30s', + skipUnavailable: false, + transportPingSchedule: '-1', + transportCompress: false, + }); + }); + + it('should deserialize a cluster that contains a deprecated proxy address and is in cloud', () => { + expect( + deserializeCluster( + 'test_cluster', + { + seeds: ['localhost:9300'], + connected: true, + num_nodes_connected: 1, + max_connections_per_cluster: 3, + initial_connect_timeout: '30s', + skip_unavailable: false, + transport: { + ping_schedule: '-1', + compress: false, + }, + }, + 'localhost:9300', + true + ) ).toEqual({ name: 'test_cluster', proxyAddress: 'localhost:9300', diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts index 3d8ffa13b8218..07dbe8da28d8a 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts @@ -68,7 +68,8 @@ export interface ClusterSettingsPayloadEs { export function deserializeCluster( name: string, esClusterObject: ClusterInfoEs, - deprecatedProxyAddress?: string | undefined + deprecatedProxyAddress?: string | undefined, + isCloudEnabled?: boolean | undefined ): Cluster { if (!name || !esClusterObject || typeof esClusterObject !== 'object') { throw new Error('Unable to deserialize cluster'); @@ -117,7 +118,7 @@ export function deserializeCluster( // If a user has a remote cluster with the deprecated proxy setting, // we transform the data to support the new implementation and also flag the deprecation if (deprecatedProxyAddress) { - // Create server name (address, without port), since field doesn't exist in deprecated implementation + // Cloud-specific logic: Create default server name, since field doesn't exist in deprecated implementation const defaultServerName = deprecatedProxyAddress.split(':')[0]; deserializedClusterObject = { @@ -126,7 +127,7 @@ export function deserializeCluster( seeds: undefined, hasDeprecatedProxySetting: true, mode: PROXY_MODE, - serverName: defaultServerName, + serverName: isCloudEnabled ? defaultServerName : undefined, }; } diff --git a/x-pack/plugins/remote_clusters/kibana.json b/x-pack/plugins/remote_clusters/kibana.json index 8922bf621aa03..f1b9d20f762d3 100644 --- a/x-pack/plugins/remote_clusters/kibana.json +++ b/x-pack/plugins/remote_clusters/kibana.json @@ -11,7 +11,8 @@ "indexManagement" ], "optionalPlugins": [ - "usageCollection" + "usageCollection", + "cloud" ], "server": true, "ui": true diff --git a/x-pack/plugins/remote_clusters/public/application/app_context.tsx b/x-pack/plugins/remote_clusters/public/application/app_context.tsx new file mode 100644 index 0000000000000..86c0b401d416d --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/app_context.tsx @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { createContext } from 'react'; + +export interface Context { + isCloudEnabled: boolean; +} + +export const AppContext = createContext({} as any); + +export const AppContextProvider = ({ + children, + context, +}: { + children: React.ReactNode; + context: Context; +}) => { + return {children}; +}; diff --git a/x-pack/plugins/remote_clusters/public/application/index.d.ts b/x-pack/plugins/remote_clusters/public/application/index.d.ts index b5c5ad5522134..b021dca51bacd 100644 --- a/x-pack/plugins/remote_clusters/public/application/index.d.ts +++ b/x-pack/plugins/remote_clusters/public/application/index.d.ts @@ -8,5 +8,8 @@ import { RegisterManagementAppArgs, I18nStart } from '../types'; export declare const renderApp: ( elem: HTMLElement | null, - I18nContext: I18nStart['Context'] + I18nContext: I18nStart['Context'], + appDependencies: { + isCloudEnabled?: boolean; + } ) => ReturnType; diff --git a/x-pack/plugins/remote_clusters/public/application/index.js b/x-pack/plugins/remote_clusters/public/application/index.js index 0b8b26ace5daa..f2d788c741342 100644 --- a/x-pack/plugins/remote_clusters/public/application/index.js +++ b/x-pack/plugins/remote_clusters/public/application/index.js @@ -11,14 +11,17 @@ import { Provider } from 'react-redux'; import { App } from './app'; import { remoteClustersStore } from './store'; +import { AppContextProvider } from './app_context'; -export const renderApp = (elem, I18nContext) => { +export const renderApp = (elem, I18nContext, appDependencies) => { render( - - - + + + + + , elem diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap index 6ff8c538ca89c..4c109c557fdb0 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap @@ -5,7 +5,6 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u disabledFields={Object {}} fields={ Object { - "mode": "sniff", "name": "", "nodeConnections": 3, "proxyAddress": "", @@ -805,6 +804,7 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u data-test-subj="remoteClusterFormServerNameFormRow" describedByIds={Array []} display="row" + error={null} fullWidth={true} hasChildLabel={true} hasEmptyLabelSpace={false} @@ -827,10 +827,11 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u } /> } + isInvalid={false} label={ } @@ -845,18 +846,21 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u className="euiFormRow__labelWrapper" >