mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Saved Queries] Rework saved query privileges (#202863)
## Summary This PR reworks saved query privileges to rely solely on a single global `savedQueryManagement` privilege, and eliminates app-specific overrides. This change simplifies the security model for users, fixes bugginess in the saved query management UI, and reduces code complexity associated with maintaining two separate security mechanisms (app-specific overrides and global saved query management privileges). ### Background Saved queries allow users to store a combination of KQL or Lucene queries, filters, and time filters to use across various applications in Kibana. Access to saved query saved objects are currently granted by the following feature privileges: ```json [ "feature_discover.all", "feature_dashboard.all", "feature_savedQueryManagement.all", "feature_maps.all", "feature_savedObjectsManagement.all", "feature_visualize.all" ] ``` There is also a saved query management UI within the Unified Search bar shared by applications across Kibana: <img src="https://github.com/user-attachments/assets/e4a7539b-3dd4-4d47-9ff8-205281ef50e3" width="500" /> The way access to this UI is managed in Kibana is currently confusing and buggy: - If a user has `feature_discover.all` and `feature_dashboard.all` they will be able to load and save queries in Discover and Dashboard. - If a user has `feature_discover.all` and `feature_dashboard.read` they will be able to load queries in both Discover and Dashboard, but only save queries in Discover (even though they have write access to the SO, and API access). Instead they have to navigate to Discover to save a query before navigating back to Dashboard to load it, making for a confusing and frustrating UX. - Access to the UI is even more confusing in apps not listed in the above feature privileges (e.g. alerting, SLOs). Some of them chose to check one of the above feature privileges, meaning users who otherwise should have saved query access won't see the management UI if they don't also have the exact feature privilege being checked. Other apps just always show the management UI, leading to bugs and failures when users without one of the above feature privileges attempt to save queries. ### Existing improvements In v8.11.0, we introduced a new ["Saved Query Management"](https://github.com/elastic/kibana/pull/166937) privilege, allowing users to access saved queries across all of Kibana with a single global privilege: <img src="https://github.com/user-attachments/assets/ccbe79a4-bd0b-4ed6-89c9-117cc1f99ee2" width="600" /> When this privilege is added to a role, it solves the `feature_discover.all` and `feature_dashboard.read` issue mentioned above. However, it does not fix any of the mentioned issues for roles without the new privilege. We have so far postponed further improvements to avoid a breaking change. ### Approach To fully resolve these issues and migrate to a single global privilege, these changes have been made: - Remove saved query SO access from all application feature privileges and instead only allow access through the global saved query management privilege. - Stop relying on application feature privileges for toggling the saved query management UI, and instead rely on the global privilege. To implement this with minimal breaking changes, we've used the Kibana privilege migration framework. This allows us to seamlessly migrate existing roles containing feature privileges that currently provide access to saved queries, ensuring they are assigned the global saved query management privilege on upgrade. As a result, we had to deprecate the following feature privileges, replacing them with V2 privileges without saved query SO access: ```json [ "feature_discover.all", "feature_dashboard.all", "feature_maps.all", "feature_visualize.all" ] ``` Each area of code that currently relies on any of these feature privileges had to be updated to instead access `feature_X_V2` instead (as well as future code). This PR still introduces a minor breaking change, since users who have `feature_discover.all` and `feature_dashboard.read` are now able to save queries in Dashboard after upgrade, but we believe this is a better UX (and likely the expected one) and worth a small breaking change. ### Testing - All existing privileges should continue to work as they do now, including deprecated V1 feature privileges and customized serverless privileges. There should be no changes for existing user roles apart from the minor breaking change outlined above. - Check that code changes in your area don't introduce breaking changes to existing behaviour. Many of the changes are just updating client UI capabilities code from `feature.privilege` to `feature_v2.privilege`, which is backward compatible. - The `savedQueryManagement` feature should now globally control access to saved query management in Unified Search for all new user roles. Regardless of privileges for Discover, Dashboard, Maps, or Visualize, new user roles should follow this behaviour: - If `savedQueryManagement` is `none`, the user cannot see or access the saved query management UI or APIs. - If `savedQueryManagement` is `read`, the user can load queries from the UI and access read APIs, but cannot save queries from the UI or make changes to queries through APIs. - If `savedQueryManagement` is `all`, the user can both load and save queries from the UI and through APIs. ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [x] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Identify risks This PR risks introducing unintended breaking changes to user privileges related to saved queries if the deprecated features have not been properly migrated, and users could gain or lose access to saved query management on upgrade. This would be bad if it happened, but not overly severe since it wouldn't grant them access to any ES data they couldn't previously access (only query saved objects). We have automated testing in place to help ensure features have been migrated correctly, but the scope of these changes are broad and touch many places in the codebase. Additionally, the UI capabilities types are not very strict, and are referenced with string paths in many places, which makes changing them riskier than changing strictly typed code. A combination of regex searches and temporarily modifying the `Capabilities` type to cause type errors for deprecated privileges was used to identify references in code. Reviewers should consider if there are any other ways that UI capabilities can be referenced which were not addressed in this PR. Our automated tests already help mitigate the risk, but it's important that code owners thoroughly review the changes in their area and consider if they could have unintended consequences. The Platform Security team should also review this PR thoroughly, especially since some changes were made to platform code around privilege handling. The Data Discovery team will also manually test the behaviour when upgrading existing user roles with deprecated feature privileges as part of 9.0 upgrade testing. --------- Co-authored-by: Matthias Wilhelm <matthias.wilhelm@elastic.co> Co-authored-by: Matthias Wilhelm <ankertal@gmail.com> Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: “jeramysoucy” <jeramy.soucy@elastic.co>
This commit is contained in:
parent
f3da71672c
commit
b53d3990a2
187 changed files with 8466 additions and 1319 deletions
|
@ -48,11 +48,11 @@ xpack.fleet.internal.registry.excludePackages: [
|
|||
## Fine-tune the search solution feature privileges. Also, refer to `serverless.yml` for the project-agnostic overrides.
|
||||
xpack.features.overrides:
|
||||
### Dashboards feature is moved from Analytics category to the Search one.
|
||||
dashboard.category: "enterpriseSearch"
|
||||
dashboard_v2.category: "enterpriseSearch"
|
||||
### Dev Tools feature is moved from Analytics category to the Search one.
|
||||
dev_tools.category: "enterpriseSearch"
|
||||
### Discover feature is moved from Analytics category to the Search one.
|
||||
discover.category: "enterpriseSearch"
|
||||
discover_v2.category: "enterpriseSearch"
|
||||
### Machine Learning feature is moved from Analytics category to the Management one.
|
||||
ml.category: "management"
|
||||
### Stack Alerts feature is moved from Analytics category to the Search one renamed to simply `Alerts`.
|
||||
|
@ -130,4 +130,4 @@ xpack.observabilityAIAssistant.scope: "search"
|
|||
aiAssistantManagementSelection.preferredAIAssistantType: "observability"
|
||||
xpack.observabilityAiAssistantManagement.logSourcesEnabled: false
|
||||
xpack.observabilityAiAssistantManagement.spacesEnabled: false
|
||||
xpack.observabilityAiAssistantManagement.visibilityEnabled: false
|
||||
xpack.observabilityAiAssistantManagement.visibilityEnabled: false
|
|
@ -32,10 +32,20 @@ xpack.features.overrides:
|
|||
- feature: "observability"
|
||||
privileges: [ "read" ]
|
||||
### Dashboards feature should be moved from Analytics category to the Observability one.
|
||||
dashboard.category: "observability"
|
||||
dashboard_v2.category: "observability"
|
||||
### Discover feature should be moved from Analytics category to the Observability one and its privileges are
|
||||
### fine-tuned to grant access to Observability app.
|
||||
discover:
|
||||
privileges:
|
||||
# Discover `All` feature privilege should implicitly grant `All` access to Observability app.
|
||||
all.composedOf:
|
||||
- feature: "observability"
|
||||
privileges: [ "all" ]
|
||||
# Discover `Read` feature privilege should implicitly grant `Read` access to Observability app.
|
||||
read.composedOf:
|
||||
- feature: "observability"
|
||||
privileges: [ "read" ]
|
||||
discover_v2:
|
||||
category: "observability"
|
||||
privileges:
|
||||
# Discover `All` feature privilege should implicitly grant `All` access to Observability app.
|
||||
|
@ -226,4 +236,4 @@ xpack.ml.compatibleModuleType: 'observability'
|
|||
console.ui.embeddedEnabled: false
|
||||
|
||||
# Disable role management (custom roles)
|
||||
xpack.security.roleManagementEnabled: false
|
||||
xpack.security.roleManagementEnabled: false
|
|
@ -15,10 +15,15 @@ xpack.searchIndices.enabled: false
|
|||
|
||||
## Fine-tune the security solution feature privileges. Also, refer to `serverless.yml` for the project-agnostic overrides.
|
||||
xpack.features.overrides:
|
||||
### Dashboard feature is hidden in Role management since it's automatically granted by SIEM feature.
|
||||
dashboard.hidden: true
|
||||
### Discover feature is hidden in Role management since it's automatically granted by SIEM feature.
|
||||
### The following features are hidden in Role management since they're automatically granted by SIEM feature.
|
||||
discover.hidden: true
|
||||
discover_v2.hidden: true
|
||||
dashboard.hidden: true
|
||||
dashboard_v2.hidden: true
|
||||
visualize.hidden: true
|
||||
visualize_v2.hidden: true
|
||||
maps.hidden: true
|
||||
maps_v2.hidden: true
|
||||
### Machine Learning feature is moved from Analytics category to the Security one as the last item.
|
||||
ml:
|
||||
category: "security"
|
||||
|
@ -29,25 +34,29 @@ xpack.features.overrides:
|
|||
### Security's `All` feature privilege should implicitly grant `All` access to Discover, Dashboard, Maps, and
|
||||
### Visualize features.
|
||||
all.composedOf:
|
||||
- feature: "discover"
|
||||
- feature: "discover_v2"
|
||||
privileges: [ "all" ]
|
||||
- feature: "dashboard"
|
||||
- feature: "dashboard_v2"
|
||||
privileges: [ "all" ]
|
||||
- feature: "visualize"
|
||||
- feature: "visualize_v2"
|
||||
privileges: [ "all" ]
|
||||
- feature: "maps"
|
||||
- feature: "maps_v2"
|
||||
privileges: [ "all" ]
|
||||
- feature: "savedQueryManagement"
|
||||
privileges: [ "all" ]
|
||||
# Security's `Read` feature privilege should implicitly grant `Read` access to Discover, Dashboard, Maps, and
|
||||
# Visualize features. Additionally, it should implicitly grant privilege to create short URLs in Discover,
|
||||
### Dashboard, and Visualize apps.
|
||||
read.composedOf:
|
||||
- feature: "discover"
|
||||
- feature: "discover_v2"
|
||||
privileges: [ "read" ]
|
||||
- feature: "dashboard"
|
||||
- feature: "dashboard_v2"
|
||||
privileges: [ "read" ]
|
||||
- feature: "visualize"
|
||||
- feature: "visualize_v2"
|
||||
privileges: [ "read" ]
|
||||
- feature: "maps"
|
||||
- feature: "maps_v2"
|
||||
privileges: [ "read" ]
|
||||
- feature: "savedQueryManagement"
|
||||
privileges: [ "read" ]
|
||||
|
||||
### Security's feature privileges are fine-tuned to grant access to Discover, Dashboard, Maps, and Visualize apps.
|
||||
|
|
|
@ -36,6 +36,30 @@ xpack.features.overrides:
|
|||
url_create:
|
||||
disabled: true
|
||||
includeIn: "read"
|
||||
dashboard_v2:
|
||||
privileges:
|
||||
### Dashboard's `All` feature privilege should implicitly grant `All` access to Maps and Visualize features.
|
||||
all.composedOf:
|
||||
- feature: "maps_v2"
|
||||
privileges: [ "all" ]
|
||||
- feature: "visualize_v2"
|
||||
privileges: [ "all" ]
|
||||
### Dashboard's `Read` feature privilege should implicitly grant `Read` access to Maps and Visualize features.
|
||||
### Additionally, it should implicitly grant privilege to create short URLs in Visualize app.
|
||||
read.composedOf:
|
||||
- feature: "maps_v2"
|
||||
privileges: [ "read" ]
|
||||
- feature: "visualize_v2"
|
||||
privileges: [ "read" ]
|
||||
### All Dashboard sub-feature privileges should be hidden: reporting capabilities will be granted via dedicated
|
||||
### Reporting feature and short URL sub-feature privilege should be granted for both `All` and `Read`.
|
||||
subFeatures.privileges:
|
||||
download_csv_report.disabled: true
|
||||
generate_report.disabled: true
|
||||
store_search_session.disabled: true
|
||||
url_create:
|
||||
disabled: true
|
||||
includeIn: "read"
|
||||
discover:
|
||||
### All Discover sub-feature privileges should be hidden: reporting capabilities will be granted via dedicated
|
||||
### Reporting feature and short URL sub-feature privilege should be granted for both `All` and `Read`.
|
||||
|
@ -45,20 +69,32 @@ xpack.features.overrides:
|
|||
url_create:
|
||||
disabled: true
|
||||
includeIn: "read"
|
||||
discover_v2:
|
||||
### All Discover sub-feature privileges should be hidden: reporting capabilities will be granted via dedicated
|
||||
### Reporting feature and short URL sub-feature privilege should be granted for both `All` and `Read`.
|
||||
subFeatures.privileges:
|
||||
generate_report.disabled: true
|
||||
store_search_session.disabled: true
|
||||
url_create:
|
||||
disabled: true
|
||||
includeIn: "read"
|
||||
### Shared images feature is hidden in Role management since it's not needed.
|
||||
filesSharedImage.hidden: true
|
||||
### Maps feature is hidden in Role management since it's automatically granted by Dashboard feature.
|
||||
maps.hidden: true
|
||||
maps_v2.hidden: true
|
||||
### Reporting feature is supposed to give access to reporting capabilities across different features.
|
||||
reporting:
|
||||
privileges:
|
||||
all.composedOf:
|
||||
- feature: "dashboard"
|
||||
- feature: "dashboard_v2"
|
||||
privileges: [ "download_csv_report" ]
|
||||
- feature: "discover"
|
||||
- feature: "discover_v2"
|
||||
privileges: [ "generate_report" ]
|
||||
### Visualize feature is hidden in Role management since it's automatically granted by Dashboard feature.
|
||||
visualize:
|
||||
### The short URL sub-feature privilege should be always granted.
|
||||
subFeatures.privileges.url_create.includeIn: "read"
|
||||
visualize_v2:
|
||||
hidden: true
|
||||
### The short URL sub-feature privilege should be always granted.
|
||||
subFeatures.privileges.url_create.includeIn: "read"
|
||||
|
@ -236,4 +272,4 @@ xpack.dataUsage.enabled: true
|
|||
xpack.dataUsage.enableExperimental: ['dataUsageDisabled']
|
||||
|
||||
# Ensure Serverless is using the Amsterdam theme
|
||||
uiSettings.experimental.defaultTheme: "amsterdam"
|
||||
uiSettings.experimental.defaultTheme: "amsterdam"
|
|
@ -17,9 +17,9 @@ which include the *Discover* configuration—selected columns in the documen
|
|||
Discover sessions are primarily used for adding search results to a dashboard.
|
||||
|
||||
[role="xpack"]
|
||||
==== Read-only access
|
||||
If you have insufficient privileges to save queries,
|
||||
the *Save* button isn't visible in the saved query management popover.
|
||||
==== Saved query access
|
||||
If you have insufficient privileges to manage saved queries,
|
||||
you will be unable to load or save queries from the saved query management popover.
|
||||
For more information, see <<xpack-security-authorization, Granting access to Kibana>>
|
||||
|
||||
==== Save a query
|
||||
|
|
|
@ -110,11 +110,11 @@ PUT <kibana host>:<port>/api/security/role/custom_reporting_user
|
|||
"spaces": ["*"],
|
||||
"base": [],
|
||||
"feature": {
|
||||
"dashboard": ["generate_report", <1>
|
||||
"dashboard_v2": ["generate_report", <1>
|
||||
"download_csv_report"], <2>
|
||||
"discover": ["generate_report"], <3>
|
||||
"discover_v2": ["generate_report"], <3>
|
||||
"canvas": ["generate_report"], <4>
|
||||
"visualize": ["generate_report"] <5>
|
||||
"visualize_v2": ["generate_report"] <5>
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
@ -147,8 +147,8 @@ PUT localhost:5601/api/security/role/custom_reporting_user
|
|||
{
|
||||
"base": [],
|
||||
"feature": {
|
||||
"dashboard": [ "all" ], <1>
|
||||
"discover": [ "all" ], <2>
|
||||
"dashboard_v2": [ "all" ], <1>
|
||||
"discover_v2": [ "all" ], <2>
|
||||
},
|
||||
"spaces": [ "*" ]
|
||||
}
|
||||
|
|
|
@ -271,6 +271,23 @@ Use Kibana feature privileges to control access to reporting features. For more
|
|||
- The `xpack.reporting.roles.allow` setting is no longer supported. If you have a `xpack.reporting.roles.allow` value in your `kibana.yml`, you should remove this setting and assign privileges to reporting features using Kibana feature privileges.
|
||||
====
|
||||
|
||||
[discrete]
|
||||
[[breaking-202863]]
|
||||
.Saved query privileges have been reworked (9.0.0)
|
||||
[%collapsible]
|
||||
====
|
||||
*Details* +
|
||||
Saved query privileges have been reworked to rely solely on a single global `savedQueryManagement` privilege, and eliminate app-specific overrides (e.g. implicit access with `all` privilege for Discover, Dashboard, Maps, and Visualize apps). This change simplifies the security model and ensures consistency in the saved query management UI across Kibana, but results in different handling of saved query privileges for new user roles, and minor breaking changes to the existing management UX.
|
||||
For more information, refer to {kibana-pull}202863[#202863].
|
||||
*Impact* +
|
||||
The `savedQueryManagement` feature privilege now globally controls access to saved query management for all new user roles. Regardless of privileges for Discover, Dashboard, Maps, or Visualize, new user roles follow this behaviour:
|
||||
. If `savedQueryManagement` is `none`, the user cannot see or access the saved query management UI or APIs.
|
||||
. If `savedQueryManagement` is `read`, the user can load queries from the UI and access read APIs, but cannot save queries from the UI or make changes to queries through APIs.
|
||||
. If `savedQueryManagement` is `all`, the user can both load and save queries from the UI and through APIs.
|
||||
*Action* +
|
||||
Existing user roles that were previously implicitly granted access to saved queries through the dashboard, discover, visualize, or maps feature privileges will retain that access to prevent breaking changes. While no action is required for existing roles, it’s still advisable to audit relevant roles and re-save them to migrate to the latest privileges model. For new roles, ensure that the savedQueryManagement privilege is set as needed.
|
||||
====
|
||||
|
||||
[float]
|
||||
=== Deprecation notices
|
||||
|
||||
|
|
|
@ -65,8 +65,8 @@ PUT /api/security/role/my_kibana_role
|
|||
{
|
||||
"base": [],
|
||||
"feature": {
|
||||
"visualize": ["all"],
|
||||
"dashboard": ["read", "url_create"]
|
||||
"visualize_v2": ["all"],
|
||||
"dashboard_v2": ["read", "url_create"]
|
||||
},
|
||||
"spaces": ["marketing"]
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ export const App = ({
|
|||
showSearchBar={true}
|
||||
indexPatterns={[dataView]}
|
||||
useDefaultBehaviors={true}
|
||||
saveQueryMenuVisibility="allowed_by_app_privilege" // allowed only for this example app, use `globally_managed` by default
|
||||
allowSavingQueries
|
||||
/>
|
||||
<EuiPageTemplate.Section>
|
||||
<EuiText>
|
||||
|
|
|
@ -54,10 +54,11 @@ viewer:
|
|||
- feature_actions.read
|
||||
- feature_builtInAlerts.read
|
||||
- feature_osquery.read
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
- feature_dataQuality.all
|
||||
resources: '*'
|
||||
run_as: []
|
||||
|
@ -137,10 +138,11 @@ editor:
|
|||
- feature_actions.endpoint_security_execute
|
||||
- feature_builtInAlerts.all
|
||||
- feature_osquery.all
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
resources: '*'
|
||||
run_as: []
|
||||
|
||||
|
@ -191,10 +193,11 @@ t1_analyst:
|
|||
- feature_builtInAlerts.read
|
||||
- feature_osquery.read
|
||||
- feature_osquery.run_saved_queries
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
resources: '*'
|
||||
|
||||
t2_analyst:
|
||||
|
@ -248,10 +251,11 @@ t2_analyst:
|
|||
- feature_builtInAlerts.read
|
||||
- feature_osquery.read
|
||||
- feature_osquery.run_saved_queries
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
resources: '*'
|
||||
|
||||
t3_analyst:
|
||||
|
@ -322,10 +326,11 @@ t3_analyst:
|
|||
- feature_actions.endpoint_security_execute
|
||||
- feature_builtInAlerts.all
|
||||
- feature_osquery.all
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
resources: '*'
|
||||
|
||||
threat_intelligence_analyst:
|
||||
|
@ -386,10 +391,11 @@ threat_intelligence_analyst:
|
|||
- feature_actions.read
|
||||
- feature_builtInAlerts.read
|
||||
- feature_osquery.all
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
resources: '*'
|
||||
|
||||
rule_author:
|
||||
|
@ -457,10 +463,11 @@ rule_author:
|
|||
- feature_actions.read
|
||||
- feature_builtInAlerts.all
|
||||
- feature_osquery.all
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
resources: '*'
|
||||
|
||||
soc_manager:
|
||||
|
@ -535,10 +542,11 @@ soc_manager:
|
|||
- feature_builtInAlerts.all
|
||||
- feature_osquery.all
|
||||
- feature_indexPatterns.all
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
resources: '*'
|
||||
|
||||
detections_admin:
|
||||
|
@ -596,10 +604,11 @@ detections_admin:
|
|||
- feature_actions.all
|
||||
- feature_builtInAlerts.all
|
||||
- feature_dev_tools.all
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
resources: '*'
|
||||
|
||||
platform_engineer:
|
||||
|
@ -661,10 +670,11 @@ platform_engineer:
|
|||
- feature_fleetv2.all
|
||||
- feature_osquery.all
|
||||
- feature_indexPatterns.all
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
resources: '*'
|
||||
|
||||
endpoint_operations_analyst:
|
||||
|
@ -737,10 +747,11 @@ endpoint_operations_analyst:
|
|||
- feature_osquery.all
|
||||
- feature_fleet.all
|
||||
- feature_fleetv2.all
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
resources: '*'
|
||||
|
||||
endpoint_policy_manager:
|
||||
|
@ -815,10 +826,11 @@ endpoint_policy_manager:
|
|||
- feature_osquery.all
|
||||
- feature_fleet.all
|
||||
- feature_fleetv2.all
|
||||
- feature_discover.all
|
||||
- feature_dashboard.all
|
||||
- feature_maps.all
|
||||
- feature_visualize.all
|
||||
- feature_discover_v2.all
|
||||
- feature_dashboard_v2.all
|
||||
- feature_maps_v2.all
|
||||
- feature_visualize_v2.all
|
||||
- feature_savedQueryManagement.all
|
||||
resources: '*'
|
||||
|
||||
# admin role defined in elasticsearch controller
|
||||
|
|
|
@ -126,7 +126,8 @@
|
|||
{
|
||||
"names": [
|
||||
"metrics-endpoint.metadata_current_*",
|
||||
".fleet-agents*", ".fleet-actions*",
|
||||
".fleet-agents*",
|
||||
".fleet-actions*",
|
||||
"risk-score.risk-score-*",
|
||||
".entities.v1.latest.security_*"
|
||||
],
|
||||
|
@ -162,10 +163,11 @@
|
|||
"actions": ["read"],
|
||||
"builtInAlerts": ["all"],
|
||||
"osquery": ["all"],
|
||||
"discover": ["all"],
|
||||
"dashboard": ["all"],
|
||||
"maps": ["all"],
|
||||
"visualize": ["all"]
|
||||
"discover_v2": ["all"],
|
||||
"dashboard_v2": ["all"],
|
||||
"maps_v2": ["all"],
|
||||
"visualize_v2": ["all"],
|
||||
"savedQueryManagement": ["all"]
|
||||
},
|
||||
"spaces": ["*"],
|
||||
"base": []
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
*
|
||||
* @public
|
||||
*/
|
||||
export interface Capabilities {
|
||||
export type Capabilities = {
|
||||
/** Navigation link capabilities. */
|
||||
navLinks: Record<string, boolean>;
|
||||
|
||||
|
@ -28,4 +28,11 @@ export interface Capabilities {
|
|||
|
||||
/** Custom capabilities, registered by plugins. */
|
||||
[key: string]: Record<string, boolean | Record<string, boolean>>;
|
||||
}
|
||||
} & {
|
||||
// These capabilities have been replaced by discover_v2, dashboard_v2 etc.
|
||||
// The purpose of these types is to avoid anyone accidentally depending on the removed capabilities.
|
||||
discover?: {};
|
||||
dashboard?: {};
|
||||
maps?: {};
|
||||
visualize?: {};
|
||||
};
|
||||
|
|
|
@ -71,7 +71,7 @@ describe('GetCsvReportPanelAction', () => {
|
|||
mockStartServicesPayload = [
|
||||
{
|
||||
...core,
|
||||
application: { capabilities: { dashboard: { downloadCsv: true } } },
|
||||
application: { capabilities: { dashboard_v2: { downloadCsv: true } } },
|
||||
} as unknown as CoreStart,
|
||||
{
|
||||
data: dataPluginMock.createStartContract(),
|
||||
|
|
|
@ -137,7 +137,7 @@ export class ReportingCsvPanelAction implements ActionDefinition<EmbeddableApiCo
|
|||
const licenseHasCsvReporting = checkLicense(license.check('reporting', 'basic')).showLinks;
|
||||
|
||||
// NOTE: For historical reasons capability identifier is called `downloadCsv. It can not be renamed.
|
||||
const capabilityHasCsvReporting = application.capabilities.dashboard?.downloadCsv === true;
|
||||
const capabilityHasCsvReporting = application.capabilities.dashboard_v2?.downloadCsv === true;
|
||||
if (!licenseHasCsvReporting || !capabilityHasCsvReporting) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ export const reportingCsvShareProvider = ({
|
|||
const licenseHasCsvReporting = licenseCheck.showLinks;
|
||||
const licenseDisabled = !licenseCheck.enableLinks;
|
||||
|
||||
const capabilityHasCsvReporting = application.capabilities.discover?.generateCsv === true;
|
||||
const capabilityHasCsvReporting = application.capabilities.discover_v2?.generateCsv === true;
|
||||
|
||||
const generateReportingJobCSV = ({ intl }: { intl: InjectedIntl }) => {
|
||||
const decoratedJobParams = apiClient.getDecoratedJobParams(getJobParams());
|
||||
|
|
|
@ -64,9 +64,9 @@ export const reportingExportModalProvider = ({
|
|||
const licenseDisabled = !enableLinks;
|
||||
|
||||
const capabilityHasDashboardScreenshotReporting =
|
||||
application.capabilities.dashboard?.generateScreenshot === true;
|
||||
application.capabilities.dashboard_v2?.generateScreenshot === true;
|
||||
const capabilityHasVisualizeScreenshotReporting =
|
||||
application.capabilities.visualize?.generateScreenshot === true;
|
||||
application.capabilities.visualize_v2?.generateScreenshot === true;
|
||||
|
||||
if (!licenseHasScreenshotReporting) {
|
||||
return [];
|
||||
|
|
|
@ -109,7 +109,7 @@ export const AlertsSearchBar = ({
|
|||
onRefresh,
|
||||
showDatePicker,
|
||||
showQueryInput: true,
|
||||
saveQueryMenuVisibility: 'allowed_by_app_privilege',
|
||||
allowSavingQueries: true,
|
||||
showSubmitButton,
|
||||
submitOnBlur,
|
||||
onQueryChange: onSearchQueryChange,
|
||||
|
|
|
@ -16,7 +16,7 @@ import { Capabilities } from '@kbn/core/types';
|
|||
|
||||
describe('useColumns', () => {
|
||||
const defaultProps = {
|
||||
capabilities: { discover: { save: true } } as unknown as Capabilities,
|
||||
capabilities: { discover_v2: { save: true } } as unknown as Capabilities,
|
||||
config: configMock,
|
||||
dataView: dataViewMock,
|
||||
dataViews: dataViewsMock,
|
||||
|
|
|
@ -35,7 +35,7 @@ export function setupSavedObjects(coreSetup: CoreSetup) {
|
|||
getInAppUrl: (obj: { id: string }) => ({
|
||||
// TODO link to specific object
|
||||
path: `/app/${VISUALIZE_APP_NAME}#/${ANNOTATIONS_LISTING_VIEW_ID}`,
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
}),
|
||||
},
|
||||
migrations: () => {
|
||||
|
|
|
@ -50,7 +50,7 @@ export const getTableList = (
|
|||
savedObjectsTagging={services.savedObjectsTagging}
|
||||
uiSettings={services.core.uiSettings}
|
||||
eventAnnotationService={services.eventAnnotationService}
|
||||
visualizeCapabilities={services.core.application.capabilities.visualize}
|
||||
visualizeCapabilities={services.core.application.capabilities.visualize_v2}
|
||||
parentProps={parentProps}
|
||||
dataViews={services.dataViews}
|
||||
createDataView={services.createDataView}
|
||||
|
|
|
@ -14,7 +14,6 @@ import type { DashboardAttributes, DashboardPanel } from '../server/content_mana
|
|||
|
||||
export interface DashboardCapabilities {
|
||||
showWriteControls: boolean;
|
||||
saveQuery: boolean;
|
||||
createNew: boolean;
|
||||
show: boolean;
|
||||
[key: string]: boolean;
|
||||
|
|
|
@ -16,7 +16,7 @@ import { showPublicUrlSwitch, ShowShareModal, ShowShareModalProps } from './show
|
|||
import { getDashboardBackupService } from '../../../services/dashboard_backup_service';
|
||||
|
||||
describe('showPublicUrlSwitch', () => {
|
||||
test('returns false if "dashboard" app is not available', () => {
|
||||
test('returns false if "dashboard_v2" app is not available', () => {
|
||||
const anonymousUserCapabilities: Capabilities = {
|
||||
catalogue: {},
|
||||
management: {},
|
||||
|
@ -27,12 +27,12 @@ describe('showPublicUrlSwitch', () => {
|
|||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('returns false if "dashboard" app is not accessible', () => {
|
||||
test('returns false if "dashboard_v2" app is not accessible', () => {
|
||||
const anonymousUserCapabilities: Capabilities = {
|
||||
catalogue: {},
|
||||
management: {},
|
||||
navLinks: {},
|
||||
dashboard: {
|
||||
dashboard_v2: {
|
||||
show: false,
|
||||
},
|
||||
};
|
||||
|
@ -41,12 +41,12 @@ describe('showPublicUrlSwitch', () => {
|
|||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('returns true if "dashboard" app is not available an accessible', () => {
|
||||
test('returns true if "dashboard_v2" app is not available an accessible', () => {
|
||||
const anonymousUserCapabilities: Capabilities = {
|
||||
catalogue: {},
|
||||
management: {},
|
||||
navLinks: {},
|
||||
dashboard: {
|
||||
dashboard_v2: {
|
||||
show: true,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -41,9 +41,9 @@ export interface ShowShareModalProps {
|
|||
}
|
||||
|
||||
export const showPublicUrlSwitch = (anonymousUserCapabilities: Capabilities) => {
|
||||
if (!anonymousUserCapabilities.dashboard) return false;
|
||||
if (!anonymousUserCapabilities.dashboard_v2) return false;
|
||||
|
||||
const dashboard = anonymousUserCapabilities.dashboard;
|
||||
const dashboard = anonymousUserCapabilities.dashboard_v2;
|
||||
|
||||
return !!dashboard.show;
|
||||
};
|
||||
|
|
|
@ -57,7 +57,7 @@ describe('DashboardEmptyScreen', () => {
|
|||
});
|
||||
|
||||
test('renders correctly with readonly mode', () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = false;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = false;
|
||||
|
||||
const component = mountComponent('view');
|
||||
expect(component.render()).toMatchSnapshot();
|
||||
|
@ -72,7 +72,7 @@ describe('DashboardEmptyScreen', () => {
|
|||
|
||||
// even when in edit mode, readonly users should not have access to the editing buttons in the empty prompt.
|
||||
test('renders correctly with readonly and edit mode', () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = false;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = false;
|
||||
|
||||
const component = mountComponent('edit');
|
||||
expect(component.render()).toMatchSnapshot();
|
||||
|
|
|
@ -65,7 +65,7 @@ function mountWith({ props: incomingProps }: { props?: Partial<DashboardListingP
|
|||
}
|
||||
|
||||
test('initial filter is passed through', async () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = false;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = false;
|
||||
|
||||
let component: ReactWrapper;
|
||||
|
||||
|
@ -80,7 +80,7 @@ test('initial filter is passed through', async () => {
|
|||
});
|
||||
|
||||
test('when showWriteControls is true, table list view is passed editing functions', async () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = true;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = true;
|
||||
|
||||
let component: ReactWrapper;
|
||||
|
||||
|
@ -99,7 +99,7 @@ test('when showWriteControls is true, table list view is passed editing function
|
|||
});
|
||||
|
||||
test('when showWriteControls is false, table list view is not passed editing functions', async () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = false;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = false;
|
||||
|
||||
let component: ReactWrapper;
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ function mountWith({
|
|||
}
|
||||
|
||||
test('renders readonly empty prompt when showWriteControls is off', async () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = false;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = false;
|
||||
|
||||
let component: ReactWrapper;
|
||||
await act(async () => {
|
||||
|
@ -68,7 +68,7 @@ test('renders readonly empty prompt when showWriteControls is off', async () =>
|
|||
});
|
||||
|
||||
test('renders empty prompt with link when showWriteControls is on', async () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = true;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = true;
|
||||
|
||||
let component: ReactWrapper;
|
||||
await act(async () => {
|
||||
|
@ -80,7 +80,7 @@ test('renders empty prompt with link when showWriteControls is on', async () =>
|
|||
});
|
||||
|
||||
test('renders disabled action button when disableCreateDashboardButton is true', async () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = true;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = true;
|
||||
|
||||
let component: ReactWrapper;
|
||||
await act(async () => {
|
||||
|
@ -95,7 +95,7 @@ test('renders disabled action button when disableCreateDashboardButton is true',
|
|||
});
|
||||
|
||||
test('renders continue button when no dashboards exist but one is in progress', async () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = true;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = true;
|
||||
let component: ReactWrapper;
|
||||
let props: DashboardListingEmptyPromptProps;
|
||||
await act(async () => {
|
||||
|
@ -114,7 +114,7 @@ test('renders continue button when no dashboards exist but one is in progress',
|
|||
});
|
||||
|
||||
test('renders discard button when no dashboards exist but one is in progress', async () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = true;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = true;
|
||||
let component: ReactWrapper;
|
||||
await act(async () => {
|
||||
({ component } = mountWith({
|
||||
|
|
|
@ -236,7 +236,7 @@ describe('useDashboardListingTable', () => {
|
|||
test('createItem should be undefined when showWriteControls equals false', () => {
|
||||
coreServices.application.capabilities = {
|
||||
...coreServices.application.capabilities,
|
||||
dashboard: {
|
||||
dashboard_v2: {
|
||||
showWriteControls: false,
|
||||
},
|
||||
};
|
||||
|
@ -251,7 +251,7 @@ describe('useDashboardListingTable', () => {
|
|||
});
|
||||
|
||||
test('deleteItems should be undefined when showWriteControls equals false', () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = false;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = false;
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useDashboardListingTable({
|
||||
|
@ -264,7 +264,7 @@ describe('useDashboardListingTable', () => {
|
|||
});
|
||||
|
||||
test('editItem should be undefined when showWriteControls equals false', () => {
|
||||
(coreServices.application.capabilities as any).dashboard.showWriteControls = false;
|
||||
(coreServices.application.capabilities as any).dashboard_v2.showWriteControls = false;
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useDashboardListingTable({
|
||||
|
|
|
@ -339,9 +339,7 @@ export function InternalDashboardTopNav({
|
|||
useDefaultBehaviors={true}
|
||||
savedQueryId={savedQueryId}
|
||||
indexPatterns={allDataViews ?? []}
|
||||
saveQueryMenuVisibility={
|
||||
getDashboardCapabilities().saveQuery ? 'allowed_by_app_privilege' : 'globally_managed'
|
||||
}
|
||||
allowSavingQueries
|
||||
appName={LEGACY_DASHBOARD_APP_ID}
|
||||
visible={viewMode !== 'print'}
|
||||
setMenuMountPoint={
|
||||
|
|
|
@ -41,7 +41,6 @@ import { SearchDashboardsResponse } from './dashboard_content_management_service
|
|||
const defaultDashboardCapabilities: DashboardCapabilities = {
|
||||
show: true,
|
||||
createNew: true,
|
||||
saveQuery: true,
|
||||
createShortUrl: true,
|
||||
showWriteControls: true,
|
||||
storeSearchSession: true,
|
||||
|
@ -49,7 +48,7 @@ const defaultDashboardCapabilities: DashboardCapabilities = {
|
|||
|
||||
export const setStubKibanaServices = () => {
|
||||
const core = coreMock.createStart();
|
||||
(core.application.capabilities as any).dashboard = defaultDashboardCapabilities;
|
||||
(core.application.capabilities as any).dashboard_v2 = defaultDashboardCapabilities;
|
||||
|
||||
setKibanaServices(core, {
|
||||
contentManagement: contentManagementMock.createStartContract(),
|
||||
|
|
|
@ -13,13 +13,12 @@ import { coreServices } from '../services/kibana_services';
|
|||
export const getDashboardCapabilities = (): DashboardCapabilities => {
|
||||
const {
|
||||
application: {
|
||||
capabilities: { dashboard },
|
||||
capabilities: { dashboard_v2: dashboard },
|
||||
},
|
||||
} = coreServices;
|
||||
|
||||
return {
|
||||
show: Boolean(dashboard.show),
|
||||
saveQuery: Boolean(dashboard.saveQuery),
|
||||
createNew: Boolean(dashboard.createNew),
|
||||
createShortUrl: Boolean(dashboard.createShortUrl),
|
||||
showWriteControls: Boolean(dashboard.showWriteControls),
|
||||
|
|
|
@ -10,12 +10,11 @@
|
|||
import { DashboardCapabilities } from '../common/types';
|
||||
|
||||
export const capabilitiesProvider = (): {
|
||||
dashboard: DashboardCapabilities;
|
||||
dashboard_v2: DashboardCapabilities;
|
||||
} => ({
|
||||
dashboard: {
|
||||
dashboard_v2: {
|
||||
createNew: true,
|
||||
show: true,
|
||||
showWriteControls: true,
|
||||
saveQuery: true,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -39,7 +39,7 @@ export const createDashboardSavedObjectType = ({
|
|||
getInAppUrl(obj) {
|
||||
return {
|
||||
path: `/app/dashboards#/view/${encodeURIComponent(obj.id)}`,
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
uiCapabilitiesPath: 'dashboard_v2.show',
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
@ -32,7 +32,7 @@ export const querySavedObjectType: SavedObjectsType = {
|
|||
getInAppUrl(obj) {
|
||||
return {
|
||||
path: `/app/discover#/?_a=(savedQuery:'${encodeURIComponent(obj.id)}')`,
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
@ -174,10 +174,10 @@ export function createDiscoverServicesMock(): DiscoverServices {
|
|||
docLinks: docLinksServiceMock.createStartContract(),
|
||||
embeddable: embeddablePluginMock.createStartContract(),
|
||||
capabilities: {
|
||||
visualize: {
|
||||
visualize_v2: {
|
||||
show: true,
|
||||
},
|
||||
discover: {
|
||||
discover_v2: {
|
||||
save: false,
|
||||
},
|
||||
advancedSettings: {
|
||||
|
|
|
@ -15,7 +15,7 @@ const capabilities = deepFreeze({
|
|||
catalogue: {},
|
||||
management: {},
|
||||
navLinks: {},
|
||||
discover: {
|
||||
discover_v2: {
|
||||
show: true,
|
||||
edit: false,
|
||||
},
|
||||
|
|
|
@ -88,7 +88,6 @@ describe('ContextApp test', () => {
|
|||
showSearchBar: true,
|
||||
showQueryInput: false,
|
||||
showFilterBar: true,
|
||||
saveQueryMenuVisibility: 'hidden' as const,
|
||||
showDatePicker: false,
|
||||
indexPatterns: [dataViewMock],
|
||||
useDefaultBehaviors: true,
|
||||
|
|
|
@ -230,7 +230,6 @@ export const ContextApp = ({ dataView, anchorId, referrer }: ContextAppProps) =>
|
|||
showSearchBar: true,
|
||||
showQueryInput: false,
|
||||
showFilterBar: true,
|
||||
saveQueryMenuVisibility: 'hidden' as const,
|
||||
showDatePicker: false,
|
||||
indexPatterns: [dataView],
|
||||
useDefaultBehaviors: true,
|
||||
|
|
|
@ -30,7 +30,7 @@ export const renderApp = ({
|
|||
}: RenderAppProps) => {
|
||||
const { history, capabilities, chrome, data, core } = services;
|
||||
|
||||
if (!capabilities.discover.save) {
|
||||
if (!capabilities.discover_v2.save) {
|
||||
chrome.setBadge({
|
||||
text: i18n.translate('discover.badge.readOnly.text', {
|
||||
defaultMessage: 'Read only',
|
||||
|
|
|
@ -100,7 +100,7 @@ export const getShareAppMenuItem = ({
|
|||
services.share.toggleShareContextMenu({
|
||||
anchorElement,
|
||||
allowEmbed: false,
|
||||
allowShortUrl: !!services.capabilities.discover.createShortUrl,
|
||||
allowShortUrl: !!services.capabilities.discover_v2.createShortUrl,
|
||||
shareableUrl,
|
||||
shareableUrlForSavedObject,
|
||||
shareableUrlLocatorParams: { locator, params },
|
||||
|
|
|
@ -55,7 +55,7 @@ jest.mock('../../../../customizations', () => ({
|
|||
}));
|
||||
|
||||
const mockDefaultCapabilities = {
|
||||
discover: { save: true },
|
||||
discover_v2: { save: true },
|
||||
} as unknown as typeof mockDiscoverService.capabilities;
|
||||
|
||||
function getProps(
|
||||
|
@ -107,7 +107,7 @@ describe('Discover topnav component', () => {
|
|||
});
|
||||
|
||||
test('generated config of TopNavMenu config is correct when discover save permissions are assigned', () => {
|
||||
const props = getProps({ capabilities: { discover: { save: true } } });
|
||||
const props = getProps({ capabilities: { discover_v2: { save: true } } });
|
||||
const component = mountWithIntl(
|
||||
<DiscoverMainProvider value={props.stateContainer}>
|
||||
<DiscoverTopNav {...props} />
|
||||
|
@ -119,7 +119,7 @@ describe('Discover topnav component', () => {
|
|||
});
|
||||
|
||||
test('generated config of TopNavMenu config is correct when no discover save permissions are assigned', () => {
|
||||
const props = getProps({ capabilities: { discover: { save: false } } });
|
||||
const props = getProps({ capabilities: { discover_v2: { save: false } } });
|
||||
const component = mountWithIntl(
|
||||
<DiscoverMainProvider value={props.stateContainer}>
|
||||
<DiscoverTopNav {...props} />
|
||||
|
@ -130,32 +130,6 @@ describe('Discover topnav component', () => {
|
|||
expect(topMenuConfig).toEqual(['inspect', 'new', 'open', 'share']);
|
||||
});
|
||||
|
||||
test('top nav is correct when discover saveQuery permission is granted', () => {
|
||||
const props = getProps({ capabilities: { discover: { saveQuery: true } } });
|
||||
const component = mountWithIntl(
|
||||
<DiscoverMainProvider value={props.stateContainer}>
|
||||
<DiscoverTopNav {...props} />
|
||||
</DiscoverMainProvider>
|
||||
);
|
||||
const statefulSearchBar = component.find(
|
||||
mockDiscoverService.navigation.ui.AggregateQueryTopNavMenu
|
||||
);
|
||||
expect(statefulSearchBar.props().saveQueryMenuVisibility).toBe('allowed_by_app_privilege');
|
||||
});
|
||||
|
||||
test('top nav is correct when discover saveQuery permission is not granted', () => {
|
||||
const props = getProps({ capabilities: { discover: { saveQuery: false } } });
|
||||
const component = mountWithIntl(
|
||||
<DiscoverMainProvider value={props.stateContainer}>
|
||||
<DiscoverTopNav {...props} />
|
||||
</DiscoverMainProvider>
|
||||
);
|
||||
const statefulSearchBar = component.find(
|
||||
mockDiscoverService.navigation.ui.AggregateQueryTopNavMenu
|
||||
);
|
||||
expect(statefulSearchBar.props().saveQueryMenuVisibility).toBe('globally_managed');
|
||||
});
|
||||
|
||||
describe('top nav customization', () => {
|
||||
it('should allow disabling default menu items', () => {
|
||||
mockUseCustomizations = true;
|
||||
|
|
|
@ -235,9 +235,7 @@ export const DiscoverTopNav = ({
|
|||
savedQueryId={savedQuery}
|
||||
screenTitle={savedSearch.title}
|
||||
showDatePicker={showDatePicker}
|
||||
saveQueryMenuVisibility={
|
||||
services.capabilities.discover.saveQuery ? 'allowed_by_app_privilege' : 'globally_managed'
|
||||
}
|
||||
allowSavingQueries
|
||||
showSearchBar={true}
|
||||
useDefaultBehaviors={true}
|
||||
dataViewPickerOverride={
|
||||
|
|
|
@ -17,7 +17,7 @@ import { spacesPluginMock } from '@kbn/spaces-plugin/public/mocks';
|
|||
|
||||
const stateContainer = getDiscoverStateMock({ isTimeBased: true });
|
||||
const discoverServiceMock = createDiscoverServicesMock();
|
||||
discoverServiceMock.capabilities.discover.save = true;
|
||||
discoverServiceMock.capabilities.discover_v2.save = true;
|
||||
|
||||
describe('getTopNavBadges()', function () {
|
||||
test('should not return the unsaved changes badge if no changes', () => {
|
||||
|
@ -59,7 +59,7 @@ describe('getTopNavBadges()', function () {
|
|||
|
||||
test('should not show save in unsaved changed badge for read-only user', async () => {
|
||||
const discoverServiceMockReadOnly = createDiscoverServicesMock();
|
||||
discoverServiceMockReadOnly.capabilities.discover.save = false;
|
||||
discoverServiceMockReadOnly.capabilities.discover_v2.save = false;
|
||||
const topNavBadges = getTopNavBadges({
|
||||
hasUnsavedChanges: true,
|
||||
services: discoverServiceMockReadOnly,
|
||||
|
@ -132,7 +132,7 @@ describe('getTopNavBadges()', function () {
|
|||
|
||||
describe('solutions view badge', () => {
|
||||
const discoverServiceWithSpacesMock = createDiscoverServicesMock();
|
||||
discoverServiceWithSpacesMock.capabilities.discover.save = true;
|
||||
discoverServiceWithSpacesMock.capabilities.discover_v2.save = true;
|
||||
discoverServiceWithSpacesMock.spaces = spacesPluginMock.createStartContract();
|
||||
|
||||
test('should return the solutions view badge when spaces is enabled', () => {
|
||||
|
|
|
@ -63,12 +63,12 @@ export const getTopNavBadges = ({
|
|||
await stateContainer.actions.undoSavedSearchChanges();
|
||||
},
|
||||
onSave:
|
||||
services.capabilities.discover.save && !isManaged
|
||||
services.capabilities.discover_v2.save && !isManaged
|
||||
? async () => {
|
||||
await saveSearch();
|
||||
}
|
||||
: undefined,
|
||||
onSaveAs: services.capabilities.discover.save
|
||||
onSaveAs: services.capabilities.discover_v2.save
|
||||
? async () => {
|
||||
await saveSearch(true);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ describe('useTopNavLinks', () => {
|
|||
const services = {
|
||||
...createDiscoverServicesMock(),
|
||||
capabilities: {
|
||||
discover: {
|
||||
discover_v2: {
|
||||
save: true,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -185,7 +185,7 @@ export const useTopNavLinks = ({
|
|||
entries.unshift(esqLDataViewTransitionToggle);
|
||||
}
|
||||
|
||||
if (services.capabilities.discover.save && !defaultMenu?.saveItem?.disabled) {
|
||||
if (services.capabilities.discover_v2.save && !defaultMenu?.saveItem?.disabled) {
|
||||
const saveSearch = {
|
||||
id: 'save',
|
||||
label: i18n.translate('discover.localMenu.saveTitle', {
|
||||
|
|
|
@ -498,7 +498,7 @@ export function getDiscoverStateContainer({
|
|||
}),
|
||||
{
|
||||
isDisabled: () =>
|
||||
services.capabilities.discover.storeSearchSession
|
||||
services.capabilities.discover_v2.storeSearchSession
|
||||
? { disabled: false }
|
||||
: {
|
||||
disabled: true,
|
||||
|
|
|
@ -50,7 +50,7 @@ export class ViewSavedSearchAction implements Action<EmbeddableApiContext> {
|
|||
async isCompatible({ embeddable }: EmbeddableApiContext) {
|
||||
const { capabilities } = this.application;
|
||||
const hasDiscoverPermissions =
|
||||
(capabilities.discover.show as boolean) || (capabilities.discover.save as boolean);
|
||||
(capabilities.discover_v2.show as boolean) || (capabilities.discover_v2.save as boolean);
|
||||
|
||||
if (!hasDiscoverPermissions) return false; // early return to delay async import until absolutely necessary
|
||||
const { compatibilityCheck } = await import('./view_saved_search_compatibility_check');
|
||||
|
|
|
@ -156,7 +156,7 @@ export const getSearchEmbeddableFactory = ({
|
|||
}),
|
||||
canLinkToLibrary: async () => {
|
||||
return (
|
||||
discoverServices.capabilities.discover.save && !Boolean(savedObjectId$.getValue())
|
||||
discoverServices.capabilities.discover_v2.save && !Boolean(savedObjectId$.getValue())
|
||||
);
|
||||
},
|
||||
canUnlinkFromLibrary: async () => Boolean(savedObjectId$.getValue()),
|
||||
|
|
|
@ -372,7 +372,7 @@ export class DiscoverPlugin
|
|||
const [coreStart, deps] = await core.getStartServices();
|
||||
return {
|
||||
executeTriggerActions: deps.uiActions.executeTriggerActions,
|
||||
isEditable: () => coreStart.application.capabilities.discover.save as boolean,
|
||||
isEditable: () => coreStart.application.capabilities.discover_v2.save as boolean,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -362,7 +362,7 @@ describe('getSharingData', () => {
|
|||
});
|
||||
|
||||
describe('showPublicUrlSwitch', () => {
|
||||
test('returns false if "discover" app is not available', () => {
|
||||
test('returns false if "discover_v2" app is not available', () => {
|
||||
const anonymousUserCapabilities: Capabilities = {
|
||||
catalogue: {},
|
||||
management: {},
|
||||
|
@ -373,12 +373,12 @@ describe('showPublicUrlSwitch', () => {
|
|||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('returns false if "discover" app is not accessible', () => {
|
||||
test('returns false if "discover_v2" app is not accessible', () => {
|
||||
const anonymousUserCapabilities: Capabilities = {
|
||||
catalogue: {},
|
||||
management: {},
|
||||
navLinks: {},
|
||||
discover: {
|
||||
discover_v2: {
|
||||
show: false,
|
||||
},
|
||||
};
|
||||
|
@ -387,12 +387,12 @@ describe('showPublicUrlSwitch', () => {
|
|||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('returns true if "discover" app is not available an accessible', () => {
|
||||
test('returns true if "discover_v2" app is not available an accessible', () => {
|
||||
const anonymousUserCapabilities: Capabilities = {
|
||||
catalogue: {},
|
||||
management: {},
|
||||
navLinks: {},
|
||||
discover: {
|
||||
discover_v2: {
|
||||
show: true,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -137,15 +137,14 @@ export async function getSharingData(
|
|||
export interface DiscoverCapabilities {
|
||||
createShortUrl?: boolean;
|
||||
save?: boolean;
|
||||
saveQuery?: boolean;
|
||||
show?: boolean;
|
||||
storeSearchSession?: boolean;
|
||||
}
|
||||
|
||||
export const showPublicUrlSwitch = (anonymousUserCapabilities: Capabilities) => {
|
||||
if (!anonymousUserCapabilities.discover) return false;
|
||||
if (!anonymousUserCapabilities.discover_v2) return false;
|
||||
|
||||
const discover = anonymousUserCapabilities.discover as unknown as DiscoverCapabilities;
|
||||
const discover = anonymousUserCapabilities.discover_v2 as unknown as DiscoverCapabilities;
|
||||
|
||||
return !!discover.show;
|
||||
};
|
||||
|
|
|
@ -8,10 +8,9 @@
|
|||
*/
|
||||
|
||||
export const capabilitiesProvider = () => ({
|
||||
discover: {
|
||||
discover_v2: {
|
||||
show: true,
|
||||
createShortUrl: true,
|
||||
save: true,
|
||||
saveQuery: true,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -18,11 +18,11 @@ import { setKibanaServices } from './kibana_services';
|
|||
const setDefaultPresentationUtilCapabilities = (core: CoreStart) => {
|
||||
core.application.capabilities = {
|
||||
...core.application.capabilities,
|
||||
dashboard: {
|
||||
dashboard_v2: {
|
||||
show: true,
|
||||
createNew: true,
|
||||
},
|
||||
visualize: {
|
||||
visualize_v2: {
|
||||
save: true,
|
||||
},
|
||||
advancedSettings: {
|
||||
|
|
|
@ -17,7 +17,11 @@ interface PresentationCapabilities {
|
|||
}
|
||||
|
||||
export const getPresentationCapabilities = (): PresentationCapabilities => {
|
||||
const { dashboard, visualize, advancedSettings } = coreServices.application.capabilities;
|
||||
const {
|
||||
dashboard_v2: dashboard,
|
||||
visualize_v2: visualize,
|
||||
advancedSettings,
|
||||
} = coreServices.application.capabilities;
|
||||
|
||||
return {
|
||||
canAccessDashboards: Boolean(dashboard.show),
|
||||
|
|
|
@ -282,7 +282,7 @@ exports[`SavedObjectsTable should render normally 1`] = `
|
|||
"icon": "search",
|
||||
"inAppUrl": Object {
|
||||
"path": "/discover/2",
|
||||
"uiCapabilitiesPath": "discover.show",
|
||||
"uiCapabilitiesPath": "discover_v2.show",
|
||||
},
|
||||
"title": "MySearch",
|
||||
},
|
||||
|
@ -294,7 +294,7 @@ exports[`SavedObjectsTable should render normally 1`] = `
|
|||
"icon": "dashboardApp",
|
||||
"inAppUrl": Object {
|
||||
"path": "/dashboard/3",
|
||||
"uiCapabilitiesPath": "dashboard.show",
|
||||
"uiCapabilitiesPath": "dashboard_v2.show",
|
||||
},
|
||||
"title": "MyDashboard",
|
||||
},
|
||||
|
@ -306,7 +306,7 @@ exports[`SavedObjectsTable should render normally 1`] = `
|
|||
"icon": "visualizeApp",
|
||||
"inAppUrl": Object {
|
||||
"path": "/edit/4",
|
||||
"uiCapabilitiesPath": "visualize.show",
|
||||
"uiCapabilitiesPath": "visualize_v2.show",
|
||||
},
|
||||
"title": "MyViz",
|
||||
},
|
||||
|
|
|
@ -88,7 +88,7 @@ exports[`Relationships should render dashboards normally 1`] = `
|
|||
"icon": "visualizeApp",
|
||||
"inAppUrl": Object {
|
||||
"path": "/app/visualize#/edit/1",
|
||||
"uiCapabilitiesPath": "visualize.show",
|
||||
"uiCapabilitiesPath": "visualize_v2.show",
|
||||
},
|
||||
"title": "My Visualization Title 1",
|
||||
},
|
||||
|
@ -101,7 +101,7 @@ exports[`Relationships should render dashboards normally 1`] = `
|
|||
"icon": "visualizeApp",
|
||||
"inAppUrl": Object {
|
||||
"path": "/app/visualize#/edit/2",
|
||||
"uiCapabilitiesPath": "visualize.show",
|
||||
"uiCapabilitiesPath": "visualize_v2.show",
|
||||
},
|
||||
"title": "My Visualization Title 2",
|
||||
},
|
||||
|
@ -293,7 +293,7 @@ exports[`Relationships should render index patterns normally 1`] = `
|
|||
"icon": "search",
|
||||
"inAppUrl": Object {
|
||||
"path": "/app/discover#//1",
|
||||
"uiCapabilitiesPath": "discover.show",
|
||||
"uiCapabilitiesPath": "discover_v2.show",
|
||||
},
|
||||
"title": "My Search Title",
|
||||
},
|
||||
|
@ -306,7 +306,7 @@ exports[`Relationships should render index patterns normally 1`] = `
|
|||
"icon": "visualizeApp",
|
||||
"inAppUrl": Object {
|
||||
"path": "/app/visualize#/edit/2",
|
||||
"uiCapabilitiesPath": "visualize.show",
|
||||
"uiCapabilitiesPath": "visualize_v2.show",
|
||||
},
|
||||
"title": "My Visualization Title",
|
||||
},
|
||||
|
@ -658,7 +658,7 @@ exports[`Relationships should render searches normally 1`] = `
|
|||
"icon": "visualizeApp",
|
||||
"inAppUrl": Object {
|
||||
"path": "/app/visualize#/edit/2",
|
||||
"uiCapabilitiesPath": "visualize.show",
|
||||
"uiCapabilitiesPath": "visualize_v2.show",
|
||||
},
|
||||
"title": "My Visualization Title",
|
||||
},
|
||||
|
@ -811,7 +811,7 @@ exports[`Relationships should render visualizations normally 1`] = `
|
|||
"icon": "dashboardApp",
|
||||
"inAppUrl": Object {
|
||||
"path": "/app/kibana#/dashboard/1",
|
||||
"uiCapabilitiesPath": "dashboard.show",
|
||||
"uiCapabilitiesPath": "dashboard_v2.show",
|
||||
},
|
||||
"title": "My Dashboard 1",
|
||||
},
|
||||
|
@ -824,7 +824,7 @@ exports[`Relationships should render visualizations normally 1`] = `
|
|||
"icon": "dashboardApp",
|
||||
"inAppUrl": Object {
|
||||
"path": "/app/kibana#/dashboard/2",
|
||||
"uiCapabilitiesPath": "dashboard.show",
|
||||
"uiCapabilitiesPath": "dashboard_v2.show",
|
||||
},
|
||||
"title": "My Dashboard 2",
|
||||
},
|
||||
|
|
|
@ -54,7 +54,7 @@ describe('Relationships', () => {
|
|||
icon: 'search',
|
||||
inAppUrl: {
|
||||
path: '/app/discover#//1',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
},
|
||||
title: 'My Search Title',
|
||||
},
|
||||
|
@ -67,7 +67,7 @@ describe('Relationships', () => {
|
|||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/2',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
title: 'My Visualization Title',
|
||||
},
|
||||
|
@ -137,7 +137,7 @@ describe('Relationships', () => {
|
|||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/2',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
title: 'My Visualization Title',
|
||||
},
|
||||
|
@ -155,7 +155,7 @@ describe('Relationships', () => {
|
|||
icon: 'search',
|
||||
inAppUrl: {
|
||||
path: '/discover/1',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -192,7 +192,7 @@ describe('Relationships', () => {
|
|||
icon: 'dashboardApp',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/dashboard/1',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
uiCapabilitiesPath: 'dashboard_v2.show',
|
||||
},
|
||||
title: 'My Dashboard 1',
|
||||
},
|
||||
|
@ -205,7 +205,7 @@ describe('Relationships', () => {
|
|||
icon: 'dashboardApp',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/dashboard/2',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
uiCapabilitiesPath: 'dashboard_v2.show',
|
||||
},
|
||||
title: 'My Dashboard 2',
|
||||
},
|
||||
|
@ -223,7 +223,7 @@ describe('Relationships', () => {
|
|||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/edit/1',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -260,7 +260,7 @@ describe('Relationships', () => {
|
|||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/1',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
title: 'My Visualization Title 1',
|
||||
},
|
||||
|
@ -273,7 +273,7 @@ describe('Relationships', () => {
|
|||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/2',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
title: 'My Visualization Title 2',
|
||||
},
|
||||
|
@ -291,7 +291,7 @@ describe('Relationships', () => {
|
|||
icon: 'dashboardApp',
|
||||
inAppUrl: {
|
||||
path: '/dashboard/1',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
uiCapabilitiesPath: 'dashboard_v2.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -332,7 +332,7 @@ describe('Relationships', () => {
|
|||
icon: 'dashboardApp',
|
||||
inAppUrl: {
|
||||
path: '/dashboard/1',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
uiCapabilitiesPath: 'dashboard_v2.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -181,7 +181,7 @@ describe('SavedObjectsTable', () => {
|
|||
icon: 'search',
|
||||
inAppUrl: {
|
||||
path: '/discover/2',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -193,7 +193,7 @@ describe('SavedObjectsTable', () => {
|
|||
icon: 'dashboardApp',
|
||||
inAppUrl: {
|
||||
path: '/dashboard/3',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
uiCapabilitiesPath: 'dashboard_v2.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -205,7 +205,7 @@ describe('SavedObjectsTable', () => {
|
|||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/edit/4',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -490,7 +490,7 @@ describe('SavedObjectsTable', () => {
|
|||
editUrl: '/management/kibana/objects/savedSearches/2',
|
||||
inAppUrl: {
|
||||
path: '/discover/2',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
},
|
||||
},
|
||||
} as SavedObjectWithMetadata);
|
||||
|
@ -506,7 +506,7 @@ describe('SavedObjectsTable', () => {
|
|||
icon: 'search',
|
||||
inAppUrl: {
|
||||
path: '/discover/2',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -41,7 +41,7 @@ export function getSavedSearchObjectType(
|
|||
getInAppUrl(obj) {
|
||||
return {
|
||||
path: `/app/discover#/view/${encodeURIComponent(obj.id)}`,
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
@ -66,7 +66,7 @@ async function mountComponent({
|
|||
const services = {
|
||||
...unifiedHistogramServicesMock,
|
||||
capabilities: {
|
||||
dashboard: {
|
||||
dashboard_v2: {
|
||||
showWriteControls: hasDashboardPermissions ?? true,
|
||||
},
|
||||
} as unknown as Capabilities,
|
||||
|
|
|
@ -276,7 +276,7 @@ export function Chart({
|
|||
|
||||
const canEditVisualizationOnTheFly = canCustomizeVisualization && chartVisible;
|
||||
const canSaveVisualization =
|
||||
canEditVisualizationOnTheFly && services.capabilities.dashboard?.showWriteControls;
|
||||
canEditVisualizationOnTheFly && services.capabilities.dashboard_v2?.showWriteControls;
|
||||
|
||||
const actions: IconButtonGroupProps['buttons'] = [];
|
||||
|
||||
|
|
|
@ -77,6 +77,16 @@ describe('Querybar Menu component', () => {
|
|||
},
|
||||
};
|
||||
const services = {
|
||||
application: {
|
||||
...startMock.application,
|
||||
capabilities: {
|
||||
...startMock.application.capabilities,
|
||||
savedQueryManagement: {
|
||||
showQueries: true,
|
||||
saveQuery: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
data: dataMock,
|
||||
storage: getStorage(storageValue),
|
||||
uiSettings: startMock.uiSettings,
|
||||
|
|
|
@ -217,8 +217,10 @@ export function useQueryBarMenuPanels({
|
|||
setRenderedComponent,
|
||||
}: QueryBarMenuPanelsProps) {
|
||||
const kibana = useKibana<IUnifiedSearchPluginServices>();
|
||||
const { appName, usageCollection, uiSettings, http, storage } = kibana.services;
|
||||
const { appName, usageCollection, uiSettings, http, storage, application } = kibana.services;
|
||||
const reportUiCounter = usageCollection?.reportUiCounter.bind(usageCollection, appName);
|
||||
const showSavedQueries =
|
||||
showQueryInput && showFilterBar && application.capabilities.savedQueryManagement?.showQueries;
|
||||
const cancelPendingListingRequest = useRef<() => void>(() => {});
|
||||
|
||||
const [hasSavedQueries, setHasSavedQueries] = useState(false);
|
||||
|
@ -246,10 +248,10 @@ export function useQueryBarMenuPanels({
|
|||
|
||||
setHasSavedQueries(queryCount > 0);
|
||||
};
|
||||
if (showQueryInput && showFilterBar) {
|
||||
if (showSavedQueries) {
|
||||
fetchSavedQueries();
|
||||
}
|
||||
}, [savedQueryService, showQueryInput, showFilterBar]);
|
||||
}, [savedQueryService, showSavedQueries]);
|
||||
|
||||
useEffect(() => {
|
||||
if (savedQuery) {
|
||||
|
@ -430,7 +432,7 @@ export function useQueryBarMenuPanels({
|
|||
}
|
||||
|
||||
// saved queries actions are only shown when the showQueryInput and showFilterBar is true
|
||||
if (showQueryInput && showFilterBar) {
|
||||
if (showSavedQueries) {
|
||||
items.push(...queryAndFiltersRelatedPanels);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import { useFilterManager } from './lib/use_filter_manager';
|
|||
import { useTimefilter } from './lib/use_timefilter';
|
||||
import { useSavedQuery } from './lib/use_saved_query';
|
||||
import { useQueryStringManager } from './lib/use_query_string_manager';
|
||||
import { type SavedQueryMenuVisibility, canShowSavedQuery } from './lib/can_show_saved_query';
|
||||
import { canShowSavedQuery } from './lib/can_show_saved_query';
|
||||
import type { UnifiedSearchPublicPluginStart } from '../types';
|
||||
|
||||
export interface StatefulSearchBarDeps {
|
||||
|
@ -41,7 +41,12 @@ export type StatefulSearchBarProps<QT extends Query | AggregateQuery = Query> =
|
|||
appName: string;
|
||||
useDefaultBehaviors?: boolean;
|
||||
savedQueryId?: string;
|
||||
saveQueryMenuVisibility?: SavedQueryMenuVisibility;
|
||||
/**
|
||||
* Determines if saving queries is allowed within the saved query management popover (still requires privileges).
|
||||
* This does not impact if queries can be loaded, which is determined by the saved query management read privilege.
|
||||
* Defaults to false.
|
||||
*/
|
||||
allowSavingQueries?: boolean;
|
||||
onSavedQueryIdChange?: (savedQueryId?: string) => void;
|
||||
onFiltersUpdated?: (filters: Filter[]) => void;
|
||||
};
|
||||
|
@ -155,7 +160,7 @@ export function createSearchBar({
|
|||
// App name should come from the core application service.
|
||||
// Until it's available, we'll ask the user to provide it for the pre-wired component.
|
||||
return <QT extends AggregateQuery | Query = Query>(props: StatefulSearchBarProps<QT>) => {
|
||||
const { useDefaultBehaviors } = props;
|
||||
const { useDefaultBehaviors, allowSavingQueries } = props;
|
||||
// Handle queries
|
||||
const onQuerySubmitRef = useRef(props.onQuerySubmit);
|
||||
|
||||
|
@ -200,7 +205,7 @@ export function createSearchBar({
|
|||
}, [query, timeRange, useDefaultBehaviors]);
|
||||
|
||||
const showSaveQuery = canShowSavedQuery({
|
||||
saveQueryMenuVisibility: props.saveQueryMenuVisibility,
|
||||
allowSavingQueries,
|
||||
query,
|
||||
core,
|
||||
});
|
||||
|
|
|
@ -12,21 +12,21 @@ import { canShowSavedQuery } from './can_show_saved_query';
|
|||
|
||||
const core = coreMock.createStart();
|
||||
|
||||
function getCore(saveQueryGloballyAllowed: boolean): typeof core {
|
||||
function getCore(saveQueryManagementAllowed: boolean): typeof core {
|
||||
return {
|
||||
...core,
|
||||
application: {
|
||||
...core.application,
|
||||
capabilities: {
|
||||
...core.application.capabilities,
|
||||
savedQueryManagement: { saveQuery: saveQueryGloballyAllowed },
|
||||
savedQueryManagement: { saveQuery: saveQueryManagementAllowed },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const coreWithoutGlobalPrivilege = getCore(false);
|
||||
const coreWithGlobalPrivilege = getCore(true);
|
||||
const coreWithoutSavedQueryManagement = getCore(false);
|
||||
const coreWithSavedQueryManagement = getCore(true);
|
||||
|
||||
const kqlQuery = {
|
||||
query: 'response:200',
|
||||
|
@ -38,70 +38,41 @@ const esqlQuery = {
|
|||
};
|
||||
|
||||
describe('canShowSaveQuery', () => {
|
||||
it('should allow when allowed_by_app_privilege', async () => {
|
||||
it('should return false if allowSavingQueries is not true', async () => {
|
||||
expect(
|
||||
canShowSavedQuery({
|
||||
core: coreWithoutGlobalPrivilege,
|
||||
core: coreWithSavedQueryManagement,
|
||||
query: kqlQuery,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should return true with saved query management privilege', async () => {
|
||||
expect(
|
||||
canShowSavedQuery({
|
||||
allowSavingQueries: true,
|
||||
core: coreWithSavedQueryManagement,
|
||||
query: kqlQuery,
|
||||
saveQueryMenuVisibility: 'allowed_by_app_privilege',
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should not allow for text-based queries when allowed_by_app_privilege', async () => {
|
||||
it('should return false without saved query management privilege', async () => {
|
||||
expect(
|
||||
canShowSavedQuery({
|
||||
core: coreWithoutGlobalPrivilege,
|
||||
allowSavingQueries: true,
|
||||
core: coreWithoutSavedQueryManagement,
|
||||
query: kqlQuery,
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for ES|QL queries', async () => {
|
||||
expect(
|
||||
canShowSavedQuery({
|
||||
allowSavingQueries: true,
|
||||
core: coreWithSavedQueryManagement,
|
||||
query: esqlQuery,
|
||||
saveQueryMenuVisibility: 'allowed_by_app_privilege',
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should not allow for text-based queries when globally_managed', async () => {
|
||||
expect(
|
||||
canShowSavedQuery({
|
||||
core: coreWithGlobalPrivilege,
|
||||
query: esqlQuery,
|
||||
saveQueryMenuVisibility: 'globally_managed',
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should allow when globally allowed', async () => {
|
||||
expect(
|
||||
canShowSavedQuery({
|
||||
core: coreWithGlobalPrivilege,
|
||||
query: kqlQuery,
|
||||
saveQueryMenuVisibility: 'globally_managed',
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should not allow when globally disallowed', async () => {
|
||||
expect(
|
||||
canShowSavedQuery({
|
||||
core: coreWithoutGlobalPrivilege,
|
||||
query: kqlQuery,
|
||||
saveQueryMenuVisibility: 'globally_managed',
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should not allow when hidden', async () => {
|
||||
expect(
|
||||
canShowSavedQuery({
|
||||
core: coreWithGlobalPrivilege,
|
||||
query: kqlQuery,
|
||||
saveQueryMenuVisibility: 'hidden',
|
||||
})
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
canShowSavedQuery({
|
||||
core: coreWithGlobalPrivilege,
|
||||
query: kqlQuery,
|
||||
saveQueryMenuVisibility: undefined,
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
|
|
@ -11,36 +11,28 @@ import type { AggregateQuery, Query } from '@kbn/es-query';
|
|||
import type { CoreStart } from '@kbn/core-lifecycle-browser';
|
||||
import { isOfAggregateQueryType } from '@kbn/es-query';
|
||||
|
||||
export type SavedQueryMenuVisibility =
|
||||
| 'hidden'
|
||||
| 'globally_managed' // managed by "Saved Query Management" global privilege
|
||||
| 'allowed_by_app_privilege'; // use only if your Kibana app grants this privilege, otherwise default to `globally_managed`
|
||||
|
||||
/**
|
||||
* Determines if saving queries is allowed within the saved query management popover (still requires privileges).
|
||||
* This does not impact if queries can be loaded, which is determined by the saved query management read privilege.
|
||||
*/
|
||||
export const canShowSavedQuery = ({
|
||||
saveQueryMenuVisibility = 'hidden',
|
||||
allowSavingQueries = false,
|
||||
query,
|
||||
core,
|
||||
}: {
|
||||
saveQueryMenuVisibility?: SavedQueryMenuVisibility;
|
||||
allowSavingQueries?: boolean;
|
||||
query: AggregateQuery | Query | { [key: string]: any };
|
||||
core: CoreStart;
|
||||
}): boolean => {
|
||||
// don't show Saved Query menu by default
|
||||
if (!saveQueryMenuVisibility || saveQueryMenuVisibility === 'hidden') {
|
||||
// Don't allow saving queries by default
|
||||
if (!allowSavingQueries) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Saved Queries are not supported for text-based languages (only Saved Searches)
|
||||
// Saved Queries are not supported for ES|QL (only Saved Searches)
|
||||
if (isOfAggregateQueryType(query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isAllowedGlobally = Boolean(core.application.capabilities.savedQueryManagement?.saveQuery);
|
||||
|
||||
// users can allow saving queries globally or grant permission per app
|
||||
if (saveQueryMenuVisibility === 'allowed_by_app_privilege') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isAllowedGlobally;
|
||||
return Boolean(core.application.capabilities.savedQueryManagement?.saveQuery);
|
||||
};
|
||||
|
|
|
@ -74,6 +74,16 @@ function wrapSearchBarInContext(testProps: any) {
|
|||
(dataViewEditorMock.userPermissions.editDataView as jest.Mock).mockReturnValue(true);
|
||||
|
||||
const services = {
|
||||
application: {
|
||||
...startMock.application,
|
||||
capabilities: {
|
||||
...startMock.application.capabilities,
|
||||
savedQueryManagement: {
|
||||
showQueries: true,
|
||||
saveQuery: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
uiSettings: startMock.uiSettings,
|
||||
settings: startMock.settings,
|
||||
savedObjects: startMock.savedObjects,
|
||||
|
|
|
@ -145,7 +145,7 @@ export class EditInLensAction implements Action<EmbeddableApiContext> {
|
|||
return false;
|
||||
|
||||
const vis = embeddable.getVis();
|
||||
const { visualize } = getCapabilities();
|
||||
const { visualize_v2: visualize } = getCapabilities();
|
||||
if (!vis || !visualize.show) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -77,9 +77,9 @@ export function initializeEditApi({
|
|||
const isByValue = !savedObjectId$.getValue();
|
||||
if (isByValue)
|
||||
return Boolean(
|
||||
capabilities.dashboard?.showWriteControls && capabilities.visualize?.show
|
||||
capabilities.dashboard_v2?.showWriteControls && capabilities.visualize_v2?.show
|
||||
);
|
||||
else return Boolean(capabilities.visualize?.save);
|
||||
else return Boolean(capabilities.visualize_v2?.save);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -58,9 +58,9 @@ export const createVisEmbeddableFromObject =
|
|||
}
|
||||
|
||||
const capabilities = {
|
||||
visualizeSave: Boolean(getCapabilities().visualize.save),
|
||||
dashboardSave: Boolean(getCapabilities().dashboard?.showWriteControls),
|
||||
visualizeOpen: Boolean(getCapabilities().visualize?.show),
|
||||
visualizeSave: Boolean(getCapabilities().visualize_v2.save),
|
||||
dashboardSave: Boolean(getCapabilities().dashboard_v2?.showWriteControls),
|
||||
visualizeOpen: Boolean(getCapabilities().visualize_v2?.show),
|
||||
};
|
||||
|
||||
return createVisualizeEmbeddableAsync(
|
||||
|
|
|
@ -326,8 +326,8 @@ export class VisualizationsPlugin
|
|||
navigation: pluginsStart.navigation,
|
||||
share: pluginsStart.share,
|
||||
toastNotifications: coreStart.notifications.toasts,
|
||||
visualizeCapabilities: coreStart.application.capabilities.visualize,
|
||||
dashboardCapabilities: coreStart.application.capabilities.dashboard,
|
||||
visualizeCapabilities: coreStart.application.capabilities.visualize_v2,
|
||||
dashboardCapabilities: coreStart.application.capabilities.dashboard_v2,
|
||||
embeddable: pluginsStart.embeddable,
|
||||
stateTransferService: pluginsStart.embeddable.getStateTransfer(),
|
||||
setActiveUrl,
|
||||
|
|
|
@ -334,9 +334,7 @@ const TopNav = ({
|
|||
]
|
||||
: undefined
|
||||
}
|
||||
saveQueryMenuVisibility={
|
||||
services.visualizeCapabilities.saveQuery ? 'allowed_by_app_privilege' : 'globally_managed'
|
||||
}
|
||||
allowSavingQueries
|
||||
dataViewPickerComponentProps={
|
||||
shouldShowDataViewPicker && vis.data.indexPattern
|
||||
? {
|
||||
|
@ -375,7 +373,6 @@ const TopNav = ({
|
|||
setMenuMountPoint={setHeaderActionMenu}
|
||||
indexPatterns={indexPatterns}
|
||||
showSearchBar
|
||||
saveQueryMenuVisibility="hidden"
|
||||
showDatePicker={false}
|
||||
showQueryInput={false}
|
||||
/>
|
||||
|
|
|
@ -21,7 +21,7 @@ import { createEmbeddableStateTransferMock } from '@kbn/embeddable-plugin/public
|
|||
import { visualizeAppStateStub } from './stubs';
|
||||
|
||||
describe('showPublicUrlSwitch', () => {
|
||||
test('returns false if "visualize" app is not available', () => {
|
||||
test('returns false if "visualize_v2" app is not available', () => {
|
||||
const anonymousUserCapabilities: Capabilities = {
|
||||
catalogue: {},
|
||||
management: {},
|
||||
|
@ -32,12 +32,12 @@ describe('showPublicUrlSwitch', () => {
|
|||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('returns false if "visualize" app is not accessible', () => {
|
||||
test('returns false if "visualize_v2" app is not accessible', () => {
|
||||
const anonymousUserCapabilities: Capabilities = {
|
||||
catalogue: {},
|
||||
management: {},
|
||||
navLinks: {},
|
||||
visualize: {
|
||||
visualize_v2: {
|
||||
show: false,
|
||||
},
|
||||
};
|
||||
|
@ -46,12 +46,12 @@ describe('showPublicUrlSwitch', () => {
|
|||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('returns true if "visualize" app is not available an accessible', () => {
|
||||
test('returns true if "visualize_v2" app is not available an accessible', () => {
|
||||
const anonymousUserCapabilities: Capabilities = {
|
||||
catalogue: {},
|
||||
management: {},
|
||||
navLinks: {},
|
||||
visualize: {
|
||||
visualize_v2: {
|
||||
show: true,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -49,7 +49,6 @@ interface VisualizeCapabilities {
|
|||
createShortUrl: boolean;
|
||||
delete: boolean;
|
||||
save: boolean;
|
||||
saveQuery: boolean;
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
|
@ -76,9 +75,9 @@ export interface TopNavConfigParams {
|
|||
const SavedObjectSaveModalDashboard = withSuspense(LazySavedObjectSaveModalDashboard);
|
||||
|
||||
export const showPublicUrlSwitch = (anonymousUserCapabilities: Capabilities) => {
|
||||
if (!anonymousUserCapabilities.visualize) return false;
|
||||
if (!anonymousUserCapabilities.visualize_v2) return false;
|
||||
|
||||
const visualize = anonymousUserCapabilities.visualize as unknown as VisualizeCapabilities;
|
||||
const visualize = anonymousUserCapabilities.visualize_v2 as unknown as VisualizeCapabilities;
|
||||
|
||||
return !!visualize.show;
|
||||
};
|
||||
|
|
|
@ -8,11 +8,10 @@
|
|||
*/
|
||||
|
||||
export const capabilitiesProvider = () => ({
|
||||
visualize: {
|
||||
visualize_v2: {
|
||||
show: true,
|
||||
createShortUrl: true,
|
||||
delete: true,
|
||||
save: true,
|
||||
saveQuery: true,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -22,7 +22,7 @@ test('should return visualize edit url', () => {
|
|||
} as unknown as VisualizationSavedObject;
|
||||
expect(getInAppUrl(obj)).toEqual({
|
||||
path: '/app/visualize#/edit/1',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -25,6 +25,6 @@ export function getInAppUrl(obj: VisualizationSavedObject) {
|
|||
? undefined
|
||||
: {
|
||||
path: `/app/visualize#/edit/${encodeURIComponent(obj.id)}`,
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -230,7 +230,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
hiddenType: false,
|
||||
inAppUrl: {
|
||||
path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
});
|
||||
|
@ -249,7 +249,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
hiddenType: false,
|
||||
inAppUrl: {
|
||||
path: '/app/dashboards#/view/b70c7ae0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
uiCapabilitiesPath: 'dashboard_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
});
|
||||
|
@ -268,7 +268,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
hiddenType: false,
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
});
|
||||
|
@ -278,7 +278,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
hiddenType: false,
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
});
|
||||
|
|
|
@ -108,7 +108,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -150,7 +150,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
title: 'VisualizationFromSavedSearch',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -196,7 +196,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
title: 'Visualization',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -211,7 +211,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
title: 'VisualizationFromSavedSearch',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -235,7 +235,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
title: 'Visualization',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -250,7 +250,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
title: 'VisualizationFromSavedSearch',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -296,7 +296,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
title: 'OneRecord',
|
||||
inAppUrl: {
|
||||
path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -311,7 +311,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
title: 'Dashboard',
|
||||
inAppUrl: {
|
||||
path: '/app/dashboards#/view/b70c7ae0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
uiCapabilitiesPath: 'dashboard_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -337,7 +337,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
title: 'OneRecord',
|
||||
inAppUrl: {
|
||||
path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -383,7 +383,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
title: 'OneRecord',
|
||||
inAppUrl: {
|
||||
path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -398,7 +398,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
title: 'Visualization',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -424,7 +424,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
title: 'OneRecord',
|
||||
inAppUrl: {
|
||||
path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
uiCapabilitiesPath: 'discover_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
@ -476,7 +476,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
uiCapabilitiesPath: 'visualize_v2.show',
|
||||
},
|
||||
namespaceType: 'multiple-isolated',
|
||||
hiddenType: false,
|
||||
|
|
|
@ -192,6 +192,14 @@ export class SavedQueryManagementComponentService extends FtrService {
|
|||
await this.closeSavedQueryManagementComponent();
|
||||
}
|
||||
|
||||
async savedQueryLoadButtonMissingOrFail() {
|
||||
await this.retry.try(async () => {
|
||||
await this.openSavedQueryManagementComponent();
|
||||
await this.testSubjects.missingOrFail('saved-query-management-load-button');
|
||||
});
|
||||
await this.closeSavedQueryManagementComponent();
|
||||
}
|
||||
|
||||
async openSavedQueryManagementComponent() {
|
||||
const isOpenAlready = await this.testSubjects.exists('queryBarMenuPanel');
|
||||
if (isOpenAlready) return;
|
||||
|
@ -220,11 +228,16 @@ export class SavedQueryManagementComponentService extends FtrService {
|
|||
});
|
||||
}
|
||||
|
||||
async saveNewQueryMissingOrFail() {
|
||||
async saveNewQueryMissingOrFail(expectedButtonState: 'disabled' | 'hidden' = 'disabled') {
|
||||
await this.openSavedQueryManagementComponent();
|
||||
const saveFilterSetBtn = await this.testSubjects.find('saved-query-management-save-button');
|
||||
const isDisabled = await saveFilterSetBtn.getAttribute('disabled');
|
||||
expect(isDisabled).to.equal('true');
|
||||
|
||||
if (expectedButtonState === 'disabled') {
|
||||
const saveFilterSetBtn = await this.testSubjects.find('saved-query-management-save-button');
|
||||
const isDisabled = await saveFilterSetBtn.getAttribute('disabled');
|
||||
expect(isDisabled).to.equal('true');
|
||||
} else {
|
||||
await this.testSubjects.missingOrFail('saved-query-management-save-button');
|
||||
}
|
||||
}
|
||||
|
||||
async updateCurrentlyLoadedQueryMissingOrFail() {
|
||||
|
|
|
@ -40,7 +40,7 @@ export function getActions(
|
|||
mlTimefilterRefresh$.next(refresh);
|
||||
};
|
||||
// Navigate to Lens with prefilled chart for data field
|
||||
if (services.application?.capabilities?.visualize?.show === true && lensPlugin !== undefined) {
|
||||
if (services.application?.capabilities?.visualize_v2?.show === true && lensPlugin !== undefined) {
|
||||
const canUseLensEditor = lensPlugin?.canUseEditor();
|
||||
actions.push({
|
||||
name: i18n.translate('xpack.dataVisualizer.index.dataGrid.exploreInLensTitle', {
|
||||
|
@ -69,7 +69,7 @@ export function getActions(
|
|||
if (
|
||||
services?.uiActions &&
|
||||
mapsPlugin &&
|
||||
services.application?.capabilities?.maps?.show === true
|
||||
services.application?.capabilities?.maps_v2?.show === true
|
||||
) {
|
||||
actions.push({
|
||||
name: i18n.translate('xpack.dataVisualizer.index.dataGrid.exploreInMapsTitle', {
|
||||
|
|
|
@ -98,7 +98,7 @@ export const ResultsLinks: FC<Props> = ({
|
|||
let unmounted = false;
|
||||
|
||||
const getDiscoverUrl = async (): Promise<void> => {
|
||||
const isDiscoverAvailable = capabilities.discover?.show ?? false;
|
||||
const isDiscoverAvailable = capabilities.discover_v2?.show ?? false;
|
||||
if (!isDiscoverAvailable) return;
|
||||
const discoverLocator = url.locators.get('DISCOVER_APP_LOCATOR');
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ export const ActionsPanel: FC<Props> = ({
|
|||
const dataViewId = dataView.id;
|
||||
const dataViewIndexPattern = dataView.getIndexPattern();
|
||||
const getDiscoverUrl = async (): Promise<void> => {
|
||||
const isDiscoverAvailable = capabilities.discover?.show ?? false;
|
||||
const isDiscoverAvailable = capabilities.discover_v2?.show ?? false;
|
||||
if (!isDiscoverAvailable) return;
|
||||
const discoverLocator = url?.locators.get('DISCOVER_APP_LOCATOR');
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ export abstract class AbstractExploreDataAction {
|
|||
const { core, plugins } = this.params.start();
|
||||
const { capabilities } = core.application;
|
||||
|
||||
if (capabilities.discover && !capabilities.discover.show) return false;
|
||||
if (capabilities.discover_v2 && !capabilities.discover_v2.show) return false;
|
||||
if (!plugins.discover.locator) return false;
|
||||
|
||||
return shared.hasExactlyOneDataView(embeddable);
|
||||
|
|
|
@ -177,7 +177,7 @@ describe('"Explore underlying data" panel action', () => {
|
|||
const { action, context, core } = setup();
|
||||
|
||||
core.application.capabilities = { ...core.application.capabilities };
|
||||
(core.application.capabilities as any).discover = {
|
||||
(core.application.capabilities as any).discover_v2 = {
|
||||
show: false,
|
||||
};
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ describe('"Explore underlying data" panel action', () => {
|
|||
const { action, context, core } = setup();
|
||||
|
||||
core.application.capabilities = { ...core.application.capabilities };
|
||||
(core.application.capabilities as any).discover = {
|
||||
(core.application.capabilities as any).discover_v2 = {
|
||||
show: false,
|
||||
};
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ jest.mock('../../legacy_shims', () => ({
|
|||
Legacy: {
|
||||
shims: {
|
||||
getBasePath: () => '',
|
||||
capabilities: { discover: { show: true } },
|
||||
capabilities: { discover_v2: { show: true } },
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
|
|
@ -209,7 +209,7 @@ export class LogsContent extends PureComponent<LogsContentProps> {
|
|||
|
||||
renderCallout() {
|
||||
const { capabilities: uiCapabilities, kibanaServices } = Legacy.shims;
|
||||
const show = uiCapabilities.discover && uiCapabilities.discover.show;
|
||||
const show = uiCapabilities.discover_v2 && uiCapabilities.discover_v2.show;
|
||||
|
||||
const {
|
||||
logs: { enabled },
|
||||
|
|
|
@ -85,7 +85,7 @@ export const StepCreateForm: FC<StepCreateFormProps> = React.memo(
|
|||
|
||||
const toastNotifications = useToastNotifications();
|
||||
const { application, share, ...startServices } = useAppDependencies();
|
||||
const isDiscoverAvailable = application.capabilities.discover?.show ?? false;
|
||||
const isDiscoverAvailable = application.capabilities.discover_v2?.show ?? false;
|
||||
|
||||
useEffect(() => {
|
||||
let unmounted = false;
|
||||
|
|
|
@ -25,7 +25,7 @@ export const useDiscoverAction = (forceDisable: boolean) => {
|
|||
share,
|
||||
application: { capabilities },
|
||||
} = useAppDependencies();
|
||||
const isDiscoverAvailable = !!capabilities.discover?.show;
|
||||
const isDiscoverAvailable = !!capabilities.discover_v2?.show;
|
||||
|
||||
const { data: dataViewsTitleIdMap } = useGetDataViewsTitleIdMap();
|
||||
|
||||
|
|
|
@ -17459,11 +17459,9 @@
|
|||
"xpack.exploratoryView.ux.addDataButtonLabel": "Ajouter des données d'expérience utilisateur",
|
||||
"xpack.exploratoryView.uxLabel": "Expérience utilisateur (RUM)",
|
||||
"xpack.features.advancedSettingsFeatureName": "Paramètres avancés",
|
||||
"xpack.features.dashboardFeatureName": "Dashboard",
|
||||
"xpack.features.dataViewFeatureName": "Gestion de la vue de données",
|
||||
"xpack.features.devToolsFeatureName": "Outils de développement",
|
||||
"xpack.features.devToolsPrivilegesTooltip": "L'utilisateur doit également recevoir les privilèges de cluster et d'index Elasticsearch appropriés.",
|
||||
"xpack.features.discoverFeatureName": "Discover",
|
||||
"xpack.features.filesManagementFeatureName": "Gestion des fichiers",
|
||||
"xpack.features.filesSharedImagesFeatureName": "Images partagées",
|
||||
"xpack.features.filesSharedImagesPrivilegesTooltip": "Requis pour accéder aux images stockées dans Kibana.",
|
||||
|
@ -17484,7 +17482,6 @@
|
|||
"xpack.features.savedObjectsManagementFeatureName": "Gestion des objets enregistrés",
|
||||
"xpack.features.savedQueryManagementFeatureName": "Gestion des requêtes enregistrées",
|
||||
"xpack.features.savedQueryManagementTooltip": "Si \"All\" (Tout) est défini, les requêtes enregistrées peuvent être gérées grâce à Kibana dans toutes les applications compatibles. Si \"None\" est défini, les privilèges relatifs aux requêtes enregistrées sont fixés indépendamment pour chaque application.",
|
||||
"xpack.features.visualizeFeatureName": "Bibliothèque Visualize",
|
||||
"xpack.fileUpload.dataViewAlreadyExistsErrorMessage": "La vue de données existe déjà.",
|
||||
"xpack.fileUpload.fileSizeError": "La taille du fichier {fileSize} dépasse la taille maximale de fichier de {maxFileSize}",
|
||||
"xpack.fileUpload.fileTypeError": "Le fichier ne fait pas partie des types acceptables :{types}",
|
||||
|
@ -24966,7 +24963,6 @@
|
|||
"xpack.maps.esSearch.topHitsSizeMsg": "Affichage des {topHitsSize} premiers documents par entité.",
|
||||
"xpack.maps.feature.appDescription": "Explorez les données géospatiales d'Elasticsearch et de l'Elastic Maps Service.",
|
||||
"xpack.maps.featureCatalogue.mapsSubtitle": "Tracez les données géographiques.",
|
||||
"xpack.maps.featureRegistry.mapsFeatureName": "Maps",
|
||||
"xpack.maps.fields.percentileMedianLabek": "médiane",
|
||||
"xpack.maps.fileUpload.trimmedResultsMsg": "Résultats limités à {numFeatures} fonctionnalités, {previewCoverage} % du fichier.",
|
||||
"xpack.maps.fileUploadWizard.configureDocumentLayerLabel": "Ajouter comme calque du document",
|
||||
|
|
|
@ -17318,11 +17318,9 @@
|
|||
"xpack.exploratoryView.ux.addDataButtonLabel": "UXデータを追加",
|
||||
"xpack.exploratoryView.uxLabel": "ユーザーエクスペリエンス(RUM)",
|
||||
"xpack.features.advancedSettingsFeatureName": "高度な設定",
|
||||
"xpack.features.dashboardFeatureName": "ダッシュボード",
|
||||
"xpack.features.dataViewFeatureName": "データビュー管理",
|
||||
"xpack.features.devToolsFeatureName": "開発ツール",
|
||||
"xpack.features.devToolsPrivilegesTooltip": "また、ユーザーに適切な Elasticsearch クラスターとインデックスの権限が与えられている必要があります。",
|
||||
"xpack.features.discoverFeatureName": "Discover",
|
||||
"xpack.features.filesManagementFeatureName": "ファイル管理",
|
||||
"xpack.features.filesSharedImagesFeatureName": "共有画像",
|
||||
"xpack.features.filesSharedImagesPrivilegesTooltip": "Kibanaで保存された画像にアクセスする必要があります。",
|
||||
|
@ -17343,7 +17341,6 @@
|
|||
"xpack.features.savedObjectsManagementFeatureName": "保存されたオブジェクトの管理",
|
||||
"xpack.features.savedQueryManagementFeatureName": "保存されたクエリー管理",
|
||||
"xpack.features.savedQueryManagementTooltip": "[すべて]に設定すると、保存されたクエリーは、クエリーをサポートするすべてのアプリケーションのKibana全体で管理できます。[なし]に設定すると、保存されたクエリー権限は各アプリケーションで独自に決定されます。",
|
||||
"xpack.features.visualizeFeatureName": "Visualizeライブラリ",
|
||||
"xpack.fileUpload.dataViewAlreadyExistsErrorMessage": "データビューはすでに存在します。",
|
||||
"xpack.fileUpload.fileSizeError": "ファイルサイズ{fileSize}は最大ファイルサイズの{maxFileSize}を超えています",
|
||||
"xpack.fileUpload.fileTypeError": "ファイルは使用可能なタイプのいずれかではありません。{types}",
|
||||
|
@ -24832,7 +24829,6 @@
|
|||
"xpack.maps.esSearch.topHitsSizeMsg": "エンティティごとに上位の{topHitsSize}ドキュメントを表示しています。",
|
||||
"xpack.maps.feature.appDescription": "ElasticsearchとElastic Maps Serviceの地理空間データを閲覧します。",
|
||||
"xpack.maps.featureCatalogue.mapsSubtitle": "地理的なデータをプロットします。",
|
||||
"xpack.maps.featureRegistry.mapsFeatureName": "マップ",
|
||||
"xpack.maps.fields.percentileMedianLabek": "中間",
|
||||
"xpack.maps.fileUpload.trimmedResultsMsg": "結果は{numFeatures}個の特徴量に制限されます。これはファイルの{previewCoverage}%です。",
|
||||
"xpack.maps.fileUploadWizard.configureDocumentLayerLabel": "ドキュメントレイヤーとして追加",
|
||||
|
|
|
@ -17040,11 +17040,9 @@
|
|||
"xpack.exploratoryView.ux.addDataButtonLabel": "添加 UX 数据",
|
||||
"xpack.exploratoryView.uxLabel": "用户体验 (RUM)",
|
||||
"xpack.features.advancedSettingsFeatureName": "高级设置",
|
||||
"xpack.features.dashboardFeatureName": "仪表板",
|
||||
"xpack.features.dataViewFeatureName": "数据视图管理",
|
||||
"xpack.features.devToolsFeatureName": "开发工具",
|
||||
"xpack.features.devToolsPrivilegesTooltip": "还应向用户授予适当的 Elasticsearch 集群和索引权限",
|
||||
"xpack.features.discoverFeatureName": "Discover",
|
||||
"xpack.features.filesManagementFeatureName": "文件管理",
|
||||
"xpack.features.filesSharedImagesFeatureName": "共享图像",
|
||||
"xpack.features.filesSharedImagesPrivilegesTooltip": "访问存储在 Kibana 中的图像时需要此项。",
|
||||
|
@ -17065,7 +17063,6 @@
|
|||
"xpack.features.savedObjectsManagementFeatureName": "已保存对象管理",
|
||||
"xpack.features.savedQueryManagementFeatureName": "已保存查询管理",
|
||||
"xpack.features.savedQueryManagementTooltip": "如果设置为'全部',可以在支持已保存查询的所有应用程序中管理整个 Kibana 中的已保存查询。如果设置为'无',将由每个应用程序单独确定已保存查询权限。",
|
||||
"xpack.features.visualizeFeatureName": "Visualize 库",
|
||||
"xpack.fileUpload.dataViewAlreadyExistsErrorMessage": "数据视图已存在。",
|
||||
"xpack.fileUpload.fileSizeError": "文件大小 {fileSize} 超过最大文件大小 {maxFileSize}",
|
||||
"xpack.fileUpload.fileTypeError": "文件不是可接受类型之一:{types}",
|
||||
|
@ -24409,7 +24406,6 @@
|
|||
"xpack.maps.esSearch.topHitsSizeMsg": "显示每个实体排名前 {topHitsSize} 的文档。",
|
||||
"xpack.maps.feature.appDescription": "从 Elasticsearch 和 Elastic Maps Service 浏览地理空间数据。",
|
||||
"xpack.maps.featureCatalogue.mapsSubtitle": "绘制地理数据。",
|
||||
"xpack.maps.featureRegistry.mapsFeatureName": "Maps",
|
||||
"xpack.maps.fields.percentileMedianLabek": "中值",
|
||||
"xpack.maps.fileUpload.trimmedResultsMsg": "结果仅限于 {numFeatures} 个特征、{previewCoverage}% 的文件。",
|
||||
"xpack.maps.fileUploadWizard.configureDocumentLayerLabel": "添加为文档层",
|
||||
|
|
|
@ -188,7 +188,7 @@ const FieldPanel: FC<FieldPanelProps> = ({
|
|||
const [isActionMenuOpen, setIsActionMenuOpen] = useState(false);
|
||||
const [isDashboardFormValid, setIsDashboardFormValid] = useState(true);
|
||||
|
||||
const canEditDashboards = capabilities.dashboard?.createNew ?? false;
|
||||
const canEditDashboards = capabilities.dashboard_v2?.createNew ?? false;
|
||||
const { create: canCreateCase, update: canUpdateCase } = cases?.helpers?.canUseCases() ?? {
|
||||
create: false,
|
||||
update: false,
|
||||
|
|
|
@ -74,7 +74,7 @@ export const AttachmentsMenu = ({
|
|||
|
||||
const timeRange = useTimeRangeUpdates();
|
||||
|
||||
const canEditDashboards = capabilities.dashboard.createNew;
|
||||
const canEditDashboards = capabilities.dashboard_v2.createNew;
|
||||
|
||||
const onSave: SaveModalDashboardProps['onSave'] = useCallback(
|
||||
({ dashboardId, newTitle, newDescription }) => {
|
||||
|
|
|
@ -66,7 +66,7 @@ export const LogRateAnalysisAttachmentsMenu = ({
|
|||
CASES_TOAST_MESSAGES_TITLES.LOG_RATE_ANALYSIS
|
||||
);
|
||||
|
||||
const canEditDashboards = capabilities.dashboard.createNew;
|
||||
const canEditDashboards = capabilities.dashboard_v2.createNew;
|
||||
|
||||
const { create: canCreateCase, update: canUpdateCase } = cases?.helpers?.canUseCases() ?? {
|
||||
create: false,
|
||||
|
|
|
@ -38,7 +38,7 @@ export const useViewInDiscoverAction = (dataViewId?: string): TableItemAction =>
|
|||
const { timeRange } = useFilterQueryUpdates();
|
||||
|
||||
const discoverUrlError = useMemo(() => {
|
||||
if (!application.capabilities.discover?.show) {
|
||||
if (!application.capabilities.discover_v2?.show) {
|
||||
const discoverNotEnabled = i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTable.discoverNotEnabledErrorMessage',
|
||||
{
|
||||
|
@ -68,7 +68,7 @@ export const useViewInDiscoverAction = (dataViewId?: string): TableItemAction =>
|
|||
|
||||
return autoGeneratedDiscoverLinkError;
|
||||
}
|
||||
}, [application.capabilities.discover?.show, dataViewId, discoverLocator]);
|
||||
}, [application.capabilities.discover_v2?.show, dataViewId, discoverLocator]);
|
||||
|
||||
const generateDiscoverUrl = async (groupTableItem: GroupTableItem | SignificantItem) => {
|
||||
if (discoverLocator !== undefined) {
|
||||
|
|
|
@ -196,19 +196,22 @@ export const useApplicationCapabilities = (): UseApplicationCapabilities => {
|
|||
reopenCase: permissions.reopenCase,
|
||||
createComment: permissions.createComment,
|
||||
},
|
||||
visualize: { crud: !!capabilities.visualize?.save, read: !!capabilities.visualize?.show },
|
||||
visualize: {
|
||||
crud: !!capabilities.visualize_v2?.save,
|
||||
read: !!capabilities.visualize_v2?.show,
|
||||
},
|
||||
dashboard: {
|
||||
crud: !!capabilities.dashboard?.createNew,
|
||||
read: !!capabilities.dashboard?.show,
|
||||
crud: !!capabilities.dashboard_v2?.createNew,
|
||||
read: !!capabilities.dashboard_v2?.show,
|
||||
},
|
||||
}),
|
||||
[
|
||||
capabilities.actions?.save,
|
||||
capabilities.actions?.show,
|
||||
capabilities.dashboard?.createNew,
|
||||
capabilities.dashboard?.show,
|
||||
capabilities.visualize?.save,
|
||||
capabilities.visualize?.show,
|
||||
capabilities.dashboard_v2?.createNew,
|
||||
capabilities.dashboard_v2?.show,
|
||||
capabilities.visualize_v2?.save,
|
||||
capabilities.visualize_v2?.show,
|
||||
permissions.all,
|
||||
permissions.create,
|
||||
permissions.read,
|
||||
|
|
|
@ -94,8 +94,8 @@ export const createStartServicesMock = ({ license }: StartServiceArgs = {}): Sta
|
|||
case_reopen: true,
|
||||
create_comment: true,
|
||||
},
|
||||
visualize: { save: true, show: true },
|
||||
dashboard: { show: true, createNew: true },
|
||||
visualize_v2: { save: true, show: true },
|
||||
dashboard_v2: { show: true, createNew: true },
|
||||
};
|
||||
|
||||
return services;
|
||||
|
|
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue