mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Infra] Remove enableInfrastructureAssetCustomDashboards
setting (#220210)
## Summary Part of #218501 This PR removes the advanced setting `observability:enableInfrastructureAssetCustomDashboards` but keeps the saved object, to be removed in a [follow-up issue](https://github.com/elastic/kibana/issues/220340). --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
cb67765b5e
commit
eb6a159317
52 changed files with 28 additions and 2783 deletions
|
@ -351,9 +351,6 @@ $$$observability-apm-enable-service-groups$$$`observability:enableServiceGroups`
|
|||
$$$observability-infrastructure-profiling-integration$$$`observability:enableInfrastructureProfilingIntegration`
|
||||
: [preview] Enables the Profiling view in Host details within Infrastructure.
|
||||
|
||||
$$$observability-infrastructure-asset-custom-dashboard$$$`observability:enableInfrastructureAssetCustomDashboards`
|
||||
: [preview] Enables option to link custom dashboards in the Asset Details view.
|
||||
|
||||
$$$observability-profiling-per-vcpu-watt-x86$$$`observability:profilingPervCPUWattX86`
|
||||
: The average amortized per-core power consumption (based on 100% CPU utilization) for x86 architecture.
|
||||
|
||||
|
|
|
@ -113,8 +113,6 @@ export const OBSERVABILITY_APM_SERVICE_GROUP_MAX_NUMBER_OF_SERVCIE_ID =
|
|||
'observability:apmServiceGroupMaxNumberOfServices';
|
||||
export const OBSERVABILITY_ENABLE_COMPARISON_BY_DEFAULT_ID =
|
||||
'observability:enableComparisonByDefault';
|
||||
export const OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID =
|
||||
'observability:enableInfrastructureAssetCustomDashboards';
|
||||
export const OBSERVABILITY_ENABLE_INSPECT_ES_QUERIES_ID = 'observability:enableInspectEsQueries';
|
||||
export const OBSERVABILITY_MAX_SUGGESTIONS_ID = 'observability:maxSuggestions';
|
||||
export const OBSERVABILITY_PROFILING_ELASTICSEARCH_PLUGIN_ID =
|
||||
|
|
|
@ -20,7 +20,6 @@ export const OBSERVABILITY_PROJECT_SETTINGS = [
|
|||
settings.OBSERVABILITY_APM_AWS_LAMBDA_PRICE_FACTOR_ID,
|
||||
settings.OBSERVABILITY_APM_AWS_LAMBDA_REQUEST_COST_PER_MILLION_ID,
|
||||
settings.OBSERVABILITY_APM_PROGRESSIVE_LOADING_ID,
|
||||
settings.OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID,
|
||||
settings.OBSERVABILITY_APM_ENABLE_TABLE_SEARCH_BAR,
|
||||
settings.OBSERVABILITY_APM_ENABLE_SERVICE_INVENTORY_TABLE_SEARCH_BAR,
|
||||
settings.OBSERVABILITY_ENTITY_CENTRIC_EXPERIENCE,
|
||||
|
|
|
@ -607,10 +607,6 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = {
|
|||
type: 'boolean',
|
||||
_meta: { description: 'Non-default value of setting.' },
|
||||
},
|
||||
'observability:enableInfrastructureAssetCustomDashboards': {
|
||||
type: 'boolean',
|
||||
_meta: { description: 'Non-default value of setting.' },
|
||||
},
|
||||
'securitySolution:enableGroupedNav': {
|
||||
type: 'boolean',
|
||||
_meta: { description: 'Non-default value of setting.' },
|
||||
|
|
|
@ -43,7 +43,6 @@ export interface UsageStats {
|
|||
'observability:apmAWSLambdaPriceFactor': string;
|
||||
'observability:apmAWSLambdaRequestCostPerMillion': number;
|
||||
'observability:enableInfrastructureProfilingIntegration': boolean;
|
||||
'observability:enableInfrastructureAssetCustomDashboards': boolean;
|
||||
'observability:apmEnableTableSearchBar': boolean;
|
||||
'observability:apmEnableServiceInventoryTableSearchBar': boolean;
|
||||
'observability:apmEnableServiceMapApiV2': boolean;
|
||||
|
|
|
@ -11469,12 +11469,6 @@
|
|||
"description": "Non-default value of setting."
|
||||
}
|
||||
},
|
||||
"observability:enableInfrastructureAssetCustomDashboards": {
|
||||
"type": "boolean",
|
||||
"_meta": {
|
||||
"description": "Non-default value of setting."
|
||||
}
|
||||
},
|
||||
"securitySolution:enableGroupedNav": {
|
||||
"type": "boolean",
|
||||
"_meta": {
|
||||
|
|
|
@ -22453,14 +22453,6 @@
|
|||
"xpack.infra.assetDetails.charts.kubernetes.showAllButton": "Afficher tout",
|
||||
"xpack.infra.assetDetails.charts.logErrorRate": "Taux d'erreur des logs",
|
||||
"xpack.infra.assetDetails.charts.logRate": "Taux de log",
|
||||
"xpack.infra.assetDetails.dashboards.contextMenu.moreLabel": "Plus",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody": "Les ajouter ou les retirer à tout moment",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody.first": "Lier votre propre tableau de bord à cet affichage",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody.getStarted": "Pour commencer, ajoutez votre tableau de bord",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody.second": "Fournir les meilleures visualisations pertinentes pour votre entreprise",
|
||||
"xpack.infra.assetDetails.dashboards.emptyTitle": "Voulez-vous votre propre affichage ?",
|
||||
"xpack.infra.assetDetails.dashboards.linkButtonLabel": "Lier un tableau de bord",
|
||||
"xpack.infra.assetDetails.dashboards.linkNewDashboardButtonLabel": "Lier un nouveau tableau de bord",
|
||||
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last15Minutes": "15 dernières minutes",
|
||||
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last1Hour": "Dernière heure",
|
||||
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last24Hours": "Dernières 24 heures",
|
||||
|
@ -22533,35 +22525,6 @@
|
|||
"xpack.infra.containerViewPage.tooltip.whatAreTheseMetricsLink": "Que sont ces indicateurs ?",
|
||||
"xpack.infra.cpuProfilingPrompt.profilingLinkLabel": "Profilage",
|
||||
"xpack.infra.cpuProfilingPrompt.promptText": "Voir la répartition du processeur avec",
|
||||
"xpack.infra.customDashboard.addDashboard.useContextFilterLabel": "Filtrer par nom d'hôte",
|
||||
"xpack.infra.customDashboard.addDashboard.useContextFilterLabel.tooltip": "L'activation de cette option applique des filtres au tableau de bord en fonction de l'hôte choisi.",
|
||||
"xpack.infra.customDashboards.addFailure.toast.title": "Erreur lors de l'ajout du tableau de bord \"{dashboardName}\"",
|
||||
"xpack.infra.customDashboards.contextMenu.goToDashboard": "Accéder au tableau de bord",
|
||||
"xpack.infra.customDashboards.editEmptyButtonLabel": "Modifier le lien du tableau de bord",
|
||||
"xpack.infra.customDashboards.editSuccess.toast.text": "Le lien de votre tableau de bord a été mis à jour",
|
||||
"xpack.infra.customDashboards.editSuccess.toast.title": "Tableau de bord \"{dashboardName}\" modifié",
|
||||
"xpack.infra.customDashboards.filteredByCurrentAssetExplanation.message": "Ce tableau de bord est filtré par le {assetType} actuel",
|
||||
"xpack.infra.customDashboards.filteredByCurrentAssetExplanation.tooltip": "Filtré par",
|
||||
"xpack.infra.customDashboards.linkSuccess.toast.text": "Votre tableau de bord est maintenant visible dans la page de détails de ressource.",
|
||||
"xpack.infra.customDashboards.linkSuccess.toast.title": "Tableau de bord \"{dashboardName}\" ajouté",
|
||||
"xpack.infra.customDashboards.loadingCustomDashboards": "Chargement du tableau de bord",
|
||||
"xpack.infra.customDashboards.notFilteredExplanation.message": "Ce tableau de bord n'est pas filtré par le {assetType} que vous visionnez",
|
||||
"xpack.infra.customDashboards.notFilteredExplanation.tooltip": "Vous pouvez changer ce tableau de bord pour qu'il filtre par le {assetType} en modifiant son lien",
|
||||
"xpack.infra.customDashboards.selectDashboard.add": "Lier un tableau de bord",
|
||||
"xpack.infra.customDashboards.selectDashboard.cancel": "Annuler",
|
||||
"xpack.infra.customDashboards.selectDashboard.edit": "Enregistrer",
|
||||
"xpack.infra.customDashboards.selectDashboard.modalTitle.edit": "Modifier le tableau de bord",
|
||||
"xpack.infra.customDashboards.selectDashboard.modalTitle.link": "Sélectionner le tableau de bord",
|
||||
"xpack.infra.customDashboards.selectDashboard.placeholder": "Sélectionner le tableau de bord",
|
||||
"xpack.infra.customDashboards.selectDashboard.prepend": "Dashboard",
|
||||
"xpack.infra.customDashboards.technicalPreviewBadgeDescription": "Cette fonctionnalité est en version d'évaluation technique et pourra être modifiée ou retirée complètement dans une future version. Elastic s'efforcera de corriger tout problème, mais les fonctionnalités des versions d'évaluation technique ne sont pas soumises aux SLA de support des fonctionnalités officielles en disponibilité générale.",
|
||||
"xpack.infra.customDashboards.technicalPreviewBadgeLabel": "Version d'évaluation technique",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel": "Dissocier le tableau de bord",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.body": "Vous êtes sur le point de dissocier le tableau de bord du contexte {assetType}",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.button": "Dissocier le tableau de bord",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.title": "Dissocier le Tableau de bord",
|
||||
"xpack.infra.customDashboards.unlinkFailure.toast.title": "Erreur lors de la dissociation du tableau de bord \"{dashboardName}\"",
|
||||
"xpack.infra.customDashboards.unlinkSuccess.toast.title": "Tableau de bord \"{dashboardName}\" dissocié",
|
||||
"xpack.infra.customErrorHandler.learnMoreLink": "En savoir plus",
|
||||
"xpack.infra.customThreshold.rule.threshold.above": "supérieur à",
|
||||
"xpack.infra.customThreshold.rule.threshold.aboveOrEqual": "supérieur ou égal à",
|
||||
|
@ -22569,7 +22532,6 @@
|
|||
"xpack.infra.customThreshold.rule.threshold.belowOrEqual": "inférieur ou égal à",
|
||||
"xpack.infra.customThreshold.rule.threshold.between": "entre",
|
||||
"xpack.infra.customThreshold.rule.threshold.notBetween": "pas entre",
|
||||
"xpack.infra.deletingLinkedDashboards.addFailure.toast.title": "Erreur lors de la suppression des tableaux de bord liés",
|
||||
"xpack.infra.deprecations.sourcesDefaultFieldsMessage.manualSteps1": "Retirez \"xpack.infra.sources.default.fields.message\" de kibana.yml.",
|
||||
"xpack.infra.deprecations.sourcesDefaultFieldsMessage.message": "Les fonctionnalités utilisant cette configuration seront supprimées dans la version 9, et cette configuration n'est plus utilisée.",
|
||||
"xpack.infra.deprecations.sourcesDefaultFieldsMessage.title": "Le paramètre \"xpack.infra.sources.default.fields.message\" est déclassé.",
|
||||
|
@ -22594,7 +22556,6 @@
|
|||
"xpack.infra.featureRegistry.linkInfrastructureTitle": "Infrastructure",
|
||||
"xpack.infra.featureRegistry.linkLogsTitle": "Logs",
|
||||
"xpack.infra.fetcher.error.status": "Erreur",
|
||||
"xpack.infra.fetchingDashboards.addFailure.toast.title": "Erreur lors de la récupération des tableaux de bord liés",
|
||||
"xpack.infra.flamegraph.profilingAppFlamegraphLink": "Accéder au flame-graph d'Universal Profiling",
|
||||
"xpack.infra.flamegraph.profilingAppThreadsLink": "Accéder aux threads d'Universal Profiling",
|
||||
"xpack.infra.flamegraph.profilingAppTopFunctionsLink": "Accéder aux fonctions d'Universal Profiling",
|
||||
|
@ -22709,8 +22670,6 @@
|
|||
"xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "Pourquoi des lignes pointillées apparaissent-elles ?",
|
||||
"xpack.infra.infra.alerts.createAlertLink": "Créer une règle",
|
||||
"xpack.infra.infra.nodeDetails.openAsPage": "Ouvrir en tant que page",
|
||||
"xpack.infra.infra.nodeDetails.tabs.dashboards": "Tableaux de bord",
|
||||
"xpack.infra.infraAssetCustomDashboards.": "Tableaux de bord personnalisé des ressources d'infrastructure",
|
||||
"xpack.infra.inventory.alerting.groupActionVariableDescription": "Nom des données de reporting du groupe",
|
||||
"xpack.infra.inventoryId.host.ipCodeLabel": "host.ip",
|
||||
"xpack.infra.inventoryTimeline.checkNewDataButtonLabel": "Rechercher de nouvelles données",
|
||||
|
@ -22744,8 +22703,6 @@
|
|||
"xpack.infra.legendControls.switchLabel": "Calculer automatiquement la plage",
|
||||
"xpack.infra.lens.customErrorHandler.description": "Pour afficher ce graphique, assurez-vous de collecter les champs suivants :",
|
||||
"xpack.infra.lens.customErrorHandler.title": "Résultat introuvable",
|
||||
"xpack.infra.linkDashboard.tooltip.youDoNotHavePermissionToUseThisFeature": "Vous ne disposez pas d'autorisation pour utiliser cette fonctionnalité. Veuillez contacter votre administrateur pour obtenir des droits d'accès.",
|
||||
"xpack.infra.linkDashboards.addFailure.toast.title": "Erreur lors de la liaison des tableaux de bord",
|
||||
"xpack.infra.linkTo.hostWithIp.error": "Hôte avec l'adresse IP \"{hostIp}\" introuvable.",
|
||||
"xpack.infra.linkTo.hostWithIp.loading": "Chargement de l'hôte avec l'adresse IP \"{hostIp}\" en cours.",
|
||||
"xpack.infra.logAnomalies.logEntryExamplesMenuLabel": "Afficher les actions de l'entrée du log",
|
||||
|
@ -23361,9 +23318,7 @@
|
|||
"xpack.infra.profiling.threadsInfoPopoverBody": "Visualisez les traces d'appel de profilage regroupées par noms de threads de processus. Cette vue vous permet d'identifier les threads qui consomment le plus de ressources de processeur et d'examiner la pile d'appels de chaque thread. Vous pouvez ainsi identifier rapidement les lignes de codes consommant beaucoup de ressources dans le thread.",
|
||||
"xpack.infra.registerFeatures.infraOpsDescription": "Explorez les indicateurs et logs d'infrastructure pour les serveurs, conteneurs et services courants.",
|
||||
"xpack.infra.registerFeatures.infraOpsTitle": "Indicateurs",
|
||||
"xpack.infra.routes.customDashboards": "Les tableaux de bord personnalisés ne sont pas activés",
|
||||
"xpack.infra.sampleDataLinkLabel": "Logs",
|
||||
"xpack.infra.saveDashboardModal.euiIcon.iconWithTooltipLabel": "Icône avec infobulle",
|
||||
"xpack.infra.savedView.defaultViewNameHosts": "Vue par défaut",
|
||||
"xpack.infra.savedView.errorOnCreate.title": "Une erreur s'est produite lors de l'enregistrement de l'affichage.",
|
||||
"xpack.infra.savedView.errorOnDelete.title": "Une erreur s'est produite lors de la suppression de l'affichage.",
|
||||
|
@ -23416,7 +23371,6 @@
|
|||
"xpack.infra.tabs.profiling.functionsTabName": "Les 10 meilleures fonctions",
|
||||
"xpack.infra.tabs.profiling.threadsTabName": "Threads",
|
||||
"xpack.infra.trialStatus.trialStatusNetworkErrorMessage": "Nous n'avons pas pu déterminer si la licence d'essai est disponible",
|
||||
"xpack.infra.updatingLinkedDashboards.addFailure.toast.title": "Erreur lors de la mise à jour des tableaux de bord liés",
|
||||
"xpack.infra.useHTTPRequest.error.body.message": "Message",
|
||||
"xpack.infra.useHTTPRequest.error.status": "Erreur",
|
||||
"xpack.infra.useHTTPRequest.error.title": "Erreur lors de la récupération des ressources",
|
||||
|
@ -32050,8 +32004,6 @@
|
|||
"xpack.observability.emptySection.apps.ux.title": "Expérience utilisateur",
|
||||
"xpack.observability.enableComparisonByDefault": "Fonctionnalité de comparaison",
|
||||
"xpack.observability.enableComparisonByDefaultDescription": "Déterminer si la fonctionnalité de comparaison est activée ou désactivée par défaut dans l'application APM.",
|
||||
"xpack.observability.enableInfrastructureAssetCustomDashboards": "Tableaux de bord personnalisés pour les détails de ressource dans Infrastructure",
|
||||
"xpack.observability.enableInfrastructureAssetCustomDashboardsDescription": "{technicalPreviewLabel} Activez l'option pour lier des tableaux de bord personnalisés dans l'affichage des détails de ressource.",
|
||||
"xpack.observability.enableInfrastructureProfilingIntegration": "Intégration Universal Profiling dans Infrastructure",
|
||||
"xpack.observability.enableInfrastructureProfilingIntegrationDescription": "Activez l'intégration Universal Profiling dans l'application Infrastructure.",
|
||||
"xpack.observability.enableInspectEsQueriesExperimentDescription": "Inspectez les recherches Elasticsearch dans les réponses API.",
|
||||
|
|
|
@ -22434,14 +22434,6 @@
|
|||
"xpack.infra.assetDetails.charts.kubernetes.showAllButton": "すべて表示",
|
||||
"xpack.infra.assetDetails.charts.logErrorRate": "ログエラー率",
|
||||
"xpack.infra.assetDetails.charts.logRate": "ログレート",
|
||||
"xpack.infra.assetDetails.dashboards.contextMenu.moreLabel": "詳細",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody": "いつでも追加または削除できます",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody.first": "独自のダッシュボードをこのビューにリンク",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody.getStarted": "始めるには、ダッシュボードを追加します",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody.second": "ビジネスに関連した最適なビジュアライゼーションを提供",
|
||||
"xpack.infra.assetDetails.dashboards.emptyTitle": "独自のビューが必要な場合",
|
||||
"xpack.infra.assetDetails.dashboards.linkButtonLabel": "ダッシュボードをリンク",
|
||||
"xpack.infra.assetDetails.dashboards.linkNewDashboardButtonLabel": "新規ダッシュボードをリンク",
|
||||
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last15Minutes": "過去15分間",
|
||||
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last1Hour": "過去1時間",
|
||||
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last24Hours": "過去 24 時間",
|
||||
|
@ -22515,35 +22507,6 @@
|
|||
"xpack.infra.containerViewPage.tooltip.whatAreTheseMetricsLink": "これらのメトリックは何か。",
|
||||
"xpack.infra.cpuProfilingPrompt.profilingLinkLabel": "プロファイリング",
|
||||
"xpack.infra.cpuProfilingPrompt.promptText": "CPU内訳を表示",
|
||||
"xpack.infra.customDashboard.addDashboard.useContextFilterLabel": "ホスト名でフィルタリング",
|
||||
"xpack.infra.customDashboard.addDashboard.useContextFilterLabel.tooltip": "このオプションを有効にすると、選択したホストに基づいてダッシュボードにフィルターが適用されます。",
|
||||
"xpack.infra.customDashboards.addFailure.toast.title": "\"{dashboardName}\"ダッシュボードの追加エラー",
|
||||
"xpack.infra.customDashboards.contextMenu.goToDashboard": "ダッシュボードに移動",
|
||||
"xpack.infra.customDashboards.editEmptyButtonLabel": "ダッシュボードリンクを編集",
|
||||
"xpack.infra.customDashboards.editSuccess.toast.text": "ダッシュボードリンクが更新されました",
|
||||
"xpack.infra.customDashboards.editSuccess.toast.title": "\"{dashboardName}\"ダッシュボードを編集しました",
|
||||
"xpack.infra.customDashboards.filteredByCurrentAssetExplanation.message": "このダッシュボードは、現在の{assetType}によってフィルタリングされています",
|
||||
"xpack.infra.customDashboards.filteredByCurrentAssetExplanation.tooltip": "フィルタリング条件",
|
||||
"xpack.infra.customDashboards.linkSuccess.toast.text": "ダッシュボードがアセット詳細ページに表示されるようになりました。",
|
||||
"xpack.infra.customDashboards.linkSuccess.toast.title": "\"{dashboardName}\"ダッシュボードを追加しました",
|
||||
"xpack.infra.customDashboards.loadingCustomDashboards": "ダッシュボードを読み込み中",
|
||||
"xpack.infra.customDashboards.notFilteredExplanation.message": "このダッシュボードは、表示している{assetType}によってフィルタリングされていません",
|
||||
"xpack.infra.customDashboards.notFilteredExplanation.tooltip": "リンクを編集して、{assetType}でフィルタリングするようにこのダッシュボードを変更できます",
|
||||
"xpack.infra.customDashboards.selectDashboard.add": "ダッシュボードをリンク",
|
||||
"xpack.infra.customDashboards.selectDashboard.cancel": "キャンセル",
|
||||
"xpack.infra.customDashboards.selectDashboard.edit": "保存",
|
||||
"xpack.infra.customDashboards.selectDashboard.modalTitle.edit": "ダッシュボードを編集",
|
||||
"xpack.infra.customDashboards.selectDashboard.modalTitle.link": "ダッシュボードを選択",
|
||||
"xpack.infra.customDashboards.selectDashboard.placeholder": "ダッシュボードを選択",
|
||||
"xpack.infra.customDashboards.selectDashboard.prepend": "ダッシュボード",
|
||||
"xpack.infra.customDashboards.technicalPreviewBadgeDescription": "この機能はテクニカルプレビュー中であり、将来のリリースでは変更されたり完全に削除されたりする場合があります。Elasticはすべての問題の修正に努めますが、テクニカルプレビュー中の機能には正式なGA機能のサポートSLAが適用されません。",
|
||||
"xpack.infra.customDashboards.technicalPreviewBadgeLabel": "テクニカルプレビュー",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel": "ダッシュボードをリンク解除",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.body": "{assetType}コンテキストからダッシュボードのリンクを解除しようとしています",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.button": "ダッシュボードをリンク解除",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.title": "ダッシュボードをリンク解除",
|
||||
"xpack.infra.customDashboards.unlinkFailure.toast.title": "\"{dashboardName}\"ダッシュボードのリンク解除エラー",
|
||||
"xpack.infra.customDashboards.unlinkSuccess.toast.title": "\"{dashboardName}\"ダッシュボードをリンク解除しました",
|
||||
"xpack.infra.customErrorHandler.learnMoreLink": "詳細",
|
||||
"xpack.infra.customThreshold.rule.threshold.above": "より大",
|
||||
"xpack.infra.customThreshold.rule.threshold.aboveOrEqual": "以上",
|
||||
|
@ -22551,7 +22514,6 @@
|
|||
"xpack.infra.customThreshold.rule.threshold.belowOrEqual": "以下",
|
||||
"xpack.infra.customThreshold.rule.threshold.between": "の間",
|
||||
"xpack.infra.customThreshold.rule.threshold.notBetween": "の間にない",
|
||||
"xpack.infra.deletingLinkedDashboards.addFailure.toast.title": "リンクされたダッシュボードの削除エラー",
|
||||
"xpack.infra.deprecations.sourcesDefaultFieldsMessage.manualSteps1": "kibana.ymlからxpack.infra.sources.default.fields.messageを削除します。",
|
||||
"xpack.infra.deprecations.sourcesDefaultFieldsMessage.message": "この構成を使用する機能はv9で削除される予定であり、今後は使用されません。",
|
||||
"xpack.infra.deprecations.sourcesDefaultFieldsMessage.title": "xpack.infra.sources.default.fields.message設定は廃止予定です。",
|
||||
|
@ -22576,7 +22538,6 @@
|
|||
"xpack.infra.featureRegistry.linkInfrastructureTitle": "インフラストラクチャー",
|
||||
"xpack.infra.featureRegistry.linkLogsTitle": "ログ",
|
||||
"xpack.infra.fetcher.error.status": "エラー",
|
||||
"xpack.infra.fetchingDashboards.addFailure.toast.title": "ダッシュボードの取得エラー",
|
||||
"xpack.infra.flamegraph.profilingAppFlamegraphLink": "ユニバーサルプロファイリングFlamegraphに移動",
|
||||
"xpack.infra.flamegraph.profilingAppThreadsLink": "ユニバーサルプロファイリングスレッドに移動",
|
||||
"xpack.infra.flamegraph.profilingAppTopFunctionsLink": "ユニバーサルプロファイリング機能に移動",
|
||||
|
@ -22692,8 +22653,6 @@
|
|||
"xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "点線が表示されている理由",
|
||||
"xpack.infra.infra.alerts.createAlertLink": "ルールを作成",
|
||||
"xpack.infra.infra.nodeDetails.openAsPage": "ページとして開く",
|
||||
"xpack.infra.infra.nodeDetails.tabs.dashboards": "ダッシュボード",
|
||||
"xpack.infra.infraAssetCustomDashboards.": "インフラアセットカスタムダッシュボード",
|
||||
"xpack.infra.inventory.alerting.groupActionVariableDescription": "データを報告するグループの名前",
|
||||
"xpack.infra.inventoryId.host.ipCodeLabel": "host.ip",
|
||||
"xpack.infra.inventoryTimeline.checkNewDataButtonLabel": "新規データを確認",
|
||||
|
@ -22727,8 +22686,6 @@
|
|||
"xpack.infra.legendControls.switchLabel": "自動計算範囲",
|
||||
"xpack.infra.lens.customErrorHandler.description": "このグラフを表示するには、次のフィールドを収集していることを確認してください。",
|
||||
"xpack.infra.lens.customErrorHandler.title": "結果が見つかりませんでした",
|
||||
"xpack.infra.linkDashboard.tooltip.youDoNotHavePermissionToUseThisFeature": "この機能を使用する権限がありません。アクセスについては、管理者に問い合わせてください。",
|
||||
"xpack.infra.linkDashboards.addFailure.toast.title": "ダッシュボードのリンクエラー",
|
||||
"xpack.infra.linkTo.hostWithIp.error": "IP アドレス「{hostIp}」でホストが見つかりません。",
|
||||
"xpack.infra.linkTo.hostWithIp.loading": "IP アドレス「{hostIp}」のホストを読み込み中です。",
|
||||
"xpack.infra.logAnomalies.logEntryExamplesMenuLabel": "ログエントリのアクションを表示",
|
||||
|
@ -23341,9 +23298,7 @@
|
|||
"xpack.infra.profiling.threadsInfoPopoverBody": "プロセススレッド名別にグループ化されたプロファイリングスタックトレースを可視化します。このビューでは、CPUリソースを消費している上位のスレッドを特定し、各スレッドのコールスタックにドリルダウンすることで、スレッド内でリソース消費が多いコードの行をすばやく特定できます。",
|
||||
"xpack.infra.registerFeatures.infraOpsDescription": "共通のサーバー、コンテナー、サービスのインフラストラクチャーメトリックとログを閲覧します。",
|
||||
"xpack.infra.registerFeatures.infraOpsTitle": "メトリック",
|
||||
"xpack.infra.routes.customDashboards": "カスタムダッシュボードは有効ではありません",
|
||||
"xpack.infra.sampleDataLinkLabel": "ログ",
|
||||
"xpack.infra.saveDashboardModal.euiIcon.iconWithTooltipLabel": "ツールチップが表示されるアイコン",
|
||||
"xpack.infra.savedView.defaultViewNameHosts": "デフォルトビュー",
|
||||
"xpack.infra.savedView.errorOnCreate.title": "ビューの保存中にエラーが発生しました。",
|
||||
"xpack.infra.savedView.errorOnDelete.title": "ビューの削除中にエラーが発生しました。",
|
||||
|
@ -23396,7 +23351,6 @@
|
|||
"xpack.infra.tabs.profiling.functionsTabName": "上位10機能",
|
||||
"xpack.infra.tabs.profiling.threadsTabName": "スレッド",
|
||||
"xpack.infra.trialStatus.trialStatusNetworkErrorMessage": "試用版ライセンスが利用可能かどうかを判断できませんでした",
|
||||
"xpack.infra.updatingLinkedDashboards.addFailure.toast.title": "リンクされたダッシュボードの更新エラー",
|
||||
"xpack.infra.useHTTPRequest.error.body.message": "メッセージ",
|
||||
"xpack.infra.useHTTPRequest.error.status": "エラー",
|
||||
"xpack.infra.useHTTPRequest.error.title": "リソースの取得中にエラーが発生しました",
|
||||
|
@ -32029,8 +31983,6 @@
|
|||
"xpack.observability.emptySection.apps.ux.title": "ユーザーエクスペリエンス",
|
||||
"xpack.observability.enableComparisonByDefault": "比較機能",
|
||||
"xpack.observability.enableComparisonByDefaultDescription": "APMアプリで比較機能がデフォルトで有効か無効かを決定します。",
|
||||
"xpack.observability.enableInfrastructureAssetCustomDashboards": "インフラのアセット詳細のカスタムダッシュボード",
|
||||
"xpack.observability.enableInfrastructureAssetCustomDashboardsDescription": "{technicalPreviewLabel}アセット詳細ビューでカスタムダッシュボードをリンクするオプションを有効化します。",
|
||||
"xpack.observability.enableInfrastructureProfilingIntegration": "インフラのユニバーサルプロファイリング統合",
|
||||
"xpack.observability.enableInfrastructureProfilingIntegrationDescription": "インフラアプリでユニバーサルプロファイリング統合を有効化します。",
|
||||
"xpack.observability.enableInspectEsQueriesExperimentDescription": "API応答でElasticsearchクエリーを調査します。",
|
||||
|
|
|
@ -22476,14 +22476,6 @@
|
|||
"xpack.infra.assetDetails.charts.kubernetes.showAllButton": "全部显示",
|
||||
"xpack.infra.assetDetails.charts.logErrorRate": "日志错误率",
|
||||
"xpack.infra.assetDetails.charts.logRate": "日志速率",
|
||||
"xpack.infra.assetDetails.dashboards.contextMenu.moreLabel": "更多",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody": "随时添加或将其移除",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody.first": "将您自己的仪表板链接到此视图",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody.getStarted": "要开始,请添加仪表板",
|
||||
"xpack.infra.assetDetails.dashboards.emptyBody.second": "提供与您的业务相关的最佳可视化",
|
||||
"xpack.infra.assetDetails.dashboards.emptyTitle": "需要自己的视图?",
|
||||
"xpack.infra.assetDetails.dashboards.linkButtonLabel": "链接仪表板",
|
||||
"xpack.infra.assetDetails.dashboards.linkNewDashboardButtonLabel": "链接新仪表板",
|
||||
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last15Minutes": "过去 15 分钟",
|
||||
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last1Hour": "过去 1 小时",
|
||||
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last24Hours": "过去 24 小时",
|
||||
|
@ -22557,35 +22549,6 @@
|
|||
"xpack.infra.containerViewPage.tooltip.whatAreTheseMetricsLink": "这些指标是什么?",
|
||||
"xpack.infra.cpuProfilingPrompt.profilingLinkLabel": "分析",
|
||||
"xpack.infra.cpuProfilingPrompt.promptText": "使用以下项查看 CPU 细目",
|
||||
"xpack.infra.customDashboard.addDashboard.useContextFilterLabel": "按主机名筛选",
|
||||
"xpack.infra.customDashboard.addDashboard.useContextFilterLabel.tooltip": "启用此选项会根据您选择的主机将筛选应用于仪表板。",
|
||||
"xpack.infra.customDashboards.addFailure.toast.title": "添加“{dashboardName}”仪表板时出错",
|
||||
"xpack.infra.customDashboards.contextMenu.goToDashboard": "前往仪表板",
|
||||
"xpack.infra.customDashboards.editEmptyButtonLabel": "编辑仪表板链接",
|
||||
"xpack.infra.customDashboards.editSuccess.toast.text": "已更新仪表板链接",
|
||||
"xpack.infra.customDashboards.editSuccess.toast.title": "已编辑“{dashboardName}”仪表板",
|
||||
"xpack.infra.customDashboards.filteredByCurrentAssetExplanation.message": "此仪表板将按当前 {assetType} 进行筛选",
|
||||
"xpack.infra.customDashboards.filteredByCurrentAssetExplanation.tooltip": "筛选依据",
|
||||
"xpack.infra.customDashboards.linkSuccess.toast.text": "现在可以在资产详情页面查看您的仪表板。",
|
||||
"xpack.infra.customDashboards.linkSuccess.toast.title": "已添加“{dashboardName}”仪表板",
|
||||
"xpack.infra.customDashboards.loadingCustomDashboards": "正在加载仪表板",
|
||||
"xpack.infra.customDashboards.notFilteredExplanation.message": "此仪表板不按您正查看的 {assetType} 进行筛选",
|
||||
"xpack.infra.customDashboards.notFilteredExplanation.tooltip": "您可以通过编辑其链接,将此仪表板更改为按 {assetType} 进行筛选",
|
||||
"xpack.infra.customDashboards.selectDashboard.add": "链接仪表板",
|
||||
"xpack.infra.customDashboards.selectDashboard.cancel": "取消",
|
||||
"xpack.infra.customDashboards.selectDashboard.edit": "保存",
|
||||
"xpack.infra.customDashboards.selectDashboard.modalTitle.edit": "编辑仪表板",
|
||||
"xpack.infra.customDashboards.selectDashboard.modalTitle.link": "选择仪表板",
|
||||
"xpack.infra.customDashboards.selectDashboard.placeholder": "选择仪表板",
|
||||
"xpack.infra.customDashboards.selectDashboard.prepend": "仪表板",
|
||||
"xpack.infra.customDashboards.technicalPreviewBadgeDescription": "此功能处于技术预览状态,在未来版本中可能会更改或完全移除。Elastic 将努力修复任何问题,但处于技术预览状态的功能不受正式 GA 功能支持 SLA 的约束。",
|
||||
"xpack.infra.customDashboards.technicalPreviewBadgeLabel": "技术预览",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel": "取消链接仪表板",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.body": "您即将从 {assetType} 上下文中取消链接仪表板",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.button": "取消链接仪表板",
|
||||
"xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.title": "取消链接仪表板",
|
||||
"xpack.infra.customDashboards.unlinkFailure.toast.title": "对“{dashboardName}”仪表板取消链接时出错",
|
||||
"xpack.infra.customDashboards.unlinkSuccess.toast.title": "已对“{dashboardName}”仪表板取消链接",
|
||||
"xpack.infra.customErrorHandler.learnMoreLink": "了解详情",
|
||||
"xpack.infra.customThreshold.rule.threshold.above": "高于",
|
||||
"xpack.infra.customThreshold.rule.threshold.aboveOrEqual": "大于或等于",
|
||||
|
@ -22593,7 +22556,6 @@
|
|||
"xpack.infra.customThreshold.rule.threshold.belowOrEqual": "小于或等于",
|
||||
"xpack.infra.customThreshold.rule.threshold.between": "介于",
|
||||
"xpack.infra.customThreshold.rule.threshold.notBetween": "不介于",
|
||||
"xpack.infra.deletingLinkedDashboards.addFailure.toast.title": "删除已链接仪表板时出错",
|
||||
"xpack.infra.deprecations.sourcesDefaultFieldsMessage.manualSteps1": "从 kibana.yml 中移除“xpack.infra.sources.default.fields.message”。",
|
||||
"xpack.infra.deprecations.sourcesDefaultFieldsMessage.message": "使用此配置的功能即将在 v9 中移除,并且不再使用该功能。",
|
||||
"xpack.infra.deprecations.sourcesDefaultFieldsMessage.title": "“xpack.infra.sources.default.fields.message”设置已过时。",
|
||||
|
@ -22618,7 +22580,6 @@
|
|||
"xpack.infra.featureRegistry.linkInfrastructureTitle": "基础设施",
|
||||
"xpack.infra.featureRegistry.linkLogsTitle": "日志",
|
||||
"xpack.infra.fetcher.error.status": "错误",
|
||||
"xpack.infra.fetchingDashboards.addFailure.toast.title": "提取仪表板时出错",
|
||||
"xpack.infra.flamegraph.profilingAppFlamegraphLink": "前往 Universal Profiling 火焰图",
|
||||
"xpack.infra.flamegraph.profilingAppThreadsLink": "前往 Universal Profiling 线程",
|
||||
"xpack.infra.flamegraph.profilingAppTopFunctionsLink": "前往 Universal Profiling 函数",
|
||||
|
@ -22734,8 +22695,6 @@
|
|||
"xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "为什么我看到的是虚线?",
|
||||
"xpack.infra.infra.alerts.createAlertLink": "创建规则",
|
||||
"xpack.infra.infra.nodeDetails.openAsPage": "以页面形式打开",
|
||||
"xpack.infra.infra.nodeDetails.tabs.dashboards": "仪表板",
|
||||
"xpack.infra.infraAssetCustomDashboards.": "基础设施资产定制仪表板",
|
||||
"xpack.infra.inventory.alerting.groupActionVariableDescription": "报告数据的组名称",
|
||||
"xpack.infra.inventoryId.host.ipCodeLabel": "host.ip",
|
||||
"xpack.infra.inventoryTimeline.checkNewDataButtonLabel": "检查新数据",
|
||||
|
@ -22769,8 +22728,6 @@
|
|||
"xpack.infra.legendControls.switchLabel": "自动计算范围",
|
||||
"xpack.infra.lens.customErrorHandler.description": "要显示此图表,请确保正在收集以下字段:",
|
||||
"xpack.infra.lens.customErrorHandler.title": "找不到结果",
|
||||
"xpack.infra.linkDashboard.tooltip.youDoNotHavePermissionToUseThisFeature": "您无权使用此功能。请联系管理员获取访问权限。",
|
||||
"xpack.infra.linkDashboards.addFailure.toast.title": "链接仪表板时出错",
|
||||
"xpack.infra.linkTo.hostWithIp.error": "未找到 IP 地址为“{hostIp}”的主机。",
|
||||
"xpack.infra.linkTo.hostWithIp.loading": "正在加载 IP 地址为“{hostIp}”的主机。",
|
||||
"xpack.infra.logAnomalies.logEntryExamplesMenuLabel": "查看日志条目的操作",
|
||||
|
@ -23386,9 +23343,7 @@
|
|||
"xpack.infra.profiling.threadsInfoPopoverBody": "可视化按进程线程名称分组的分析堆栈跟踪。此视图有助于您识别消耗 CPU 资源的排名靠前线程,并允许您向下钻取到每个线程的调用堆栈,以便您快速确定线程中的资源密集型代码行。",
|
||||
"xpack.infra.registerFeatures.infraOpsDescription": "浏览常用服务器、容器和服务的基础设施指标和日志。",
|
||||
"xpack.infra.registerFeatures.infraOpsTitle": "指标",
|
||||
"xpack.infra.routes.customDashboards": "未启用定制仪表板",
|
||||
"xpack.infra.sampleDataLinkLabel": "日志",
|
||||
"xpack.infra.saveDashboardModal.euiIcon.iconWithTooltipLabel": "带工具提示的图标",
|
||||
"xpack.infra.savedView.defaultViewNameHosts": "默认视图",
|
||||
"xpack.infra.savedView.errorOnCreate.title": "保存视图时出错。",
|
||||
"xpack.infra.savedView.errorOnDelete.title": "删除视图时发生错误。",
|
||||
|
@ -23441,7 +23396,6 @@
|
|||
"xpack.infra.tabs.profiling.functionsTabName": "排名前 10 函数",
|
||||
"xpack.infra.tabs.profiling.threadsTabName": "线程",
|
||||
"xpack.infra.trialStatus.trialStatusNetworkErrorMessage": "我们无法确定试用许可证是否可用",
|
||||
"xpack.infra.updatingLinkedDashboards.addFailure.toast.title": "更新已链接仪表板时出错",
|
||||
"xpack.infra.useHTTPRequest.error.body.message": "消息",
|
||||
"xpack.infra.useHTTPRequest.error.status": "错误",
|
||||
"xpack.infra.useHTTPRequest.error.title": "提取资源时出错",
|
||||
|
@ -32083,8 +32037,6 @@
|
|||
"xpack.observability.emptySection.apps.ux.title": "用户体验",
|
||||
"xpack.observability.enableComparisonByDefault": "对比功能",
|
||||
"xpack.observability.enableComparisonByDefaultDescription": "确定是在 APM 应用中默认启用还是禁用比较功能。",
|
||||
"xpack.observability.enableInfrastructureAssetCustomDashboards": "Infrastructure 中资产详情的定制仪表板",
|
||||
"xpack.observability.enableInfrastructureAssetCustomDashboardsDescription": "{technicalPreviewLabel} 启用选项以在资产详情视图中链接定制仪表板。",
|
||||
"xpack.observability.enableInfrastructureProfilingIntegration": "Infrastructure 中的 Universal Profiling 集成",
|
||||
"xpack.observability.enableInfrastructureProfilingIntegrationDescription": "在 Infrastructure 应用中启用 Universal Profiling 集成。",
|
||||
"xpack.observability.enableInspectEsQueriesExperimentDescription": "检查 API 响应中的 Elasticsearch 查询。",
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
|
||||
|
||||
export type InfraCustomDashboardAssetType = InventoryItemType;
|
||||
|
||||
export interface InfraCustomDashboard {
|
||||
dashboardSavedObjectId: string;
|
||||
assetType: InfraCustomDashboardAssetType;
|
||||
dashboardFilterAssetIdEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface InfraSavedCustomDashboard extends InfraCustomDashboard {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface DashboardItemWithTitle extends InfraSavedCustomDashboard {
|
||||
title: string;
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ItemTypeRT } from '@kbn/metrics-data-access-plugin/common';
|
||||
import * as rt from 'io-ts';
|
||||
|
||||
const AssetTypeRT = rt.type({
|
||||
assetType: ItemTypeRT,
|
||||
});
|
||||
|
||||
const PayloadRT = rt.type({
|
||||
dashboardSavedObjectId: rt.string,
|
||||
dashboardFilterAssetIdEnabled: rt.boolean,
|
||||
});
|
||||
|
||||
const SavedObjectIdRT = rt.type({
|
||||
id: rt.string,
|
||||
});
|
||||
|
||||
export const InfraCustomDashboardRT = rt.intersection([AssetTypeRT, PayloadRT, SavedObjectIdRT]);
|
||||
|
||||
/**
|
||||
GET endpoint
|
||||
*/
|
||||
export const InfraGetCustomDashboardsRequestPathParamsRT = AssetTypeRT;
|
||||
export const InfraGetCustomDashboardsResponseBodyRT = rt.array(InfraCustomDashboardRT);
|
||||
export type InfraGetCustomDashboardsResponseBody = rt.TypeOf<
|
||||
typeof InfraGetCustomDashboardsResponseBodyRT
|
||||
>;
|
||||
|
||||
/**
|
||||
* POST endpoint
|
||||
*/
|
||||
export const InfraSaveCustomDashboardsRequestPayloadRT = PayloadRT;
|
||||
export const InfraSaveCustomDashboardsResponseBodyRT = InfraCustomDashboardRT;
|
||||
export type InfraSaveCustomDashboardsRequestPayload = rt.TypeOf<
|
||||
typeof InfraSaveCustomDashboardsRequestPayloadRT
|
||||
>;
|
||||
export type InfraSaveCustomDashboardsResponseBody = rt.TypeOf<
|
||||
typeof InfraSaveCustomDashboardsResponseBodyRT
|
||||
>;
|
||||
|
||||
/**
|
||||
* PUT endpoint
|
||||
*/
|
||||
export const InfraUpdateCustomDashboardsRequestPathParamsRT = rt.intersection([
|
||||
AssetTypeRT,
|
||||
SavedObjectIdRT,
|
||||
]);
|
||||
|
||||
/**
|
||||
* DELETE endpoint
|
||||
*/
|
||||
export const InfraDeleteCustomDashboardsRequestParamsRT = rt.intersection([
|
||||
AssetTypeRT,
|
||||
SavedObjectIdRT,
|
||||
]);
|
||||
export const InfraDeleteCustomDashboardsResponseBodyRT = rt.string;
|
|
@ -5,9 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiBetaBadge } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { ContentTabIds, type Tab } from '../../components/asset_details/types';
|
||||
|
||||
const overviewTab: Tab = {
|
||||
|
@ -66,30 +64,6 @@ const osqueryTab: Tab = {
|
|||
}),
|
||||
};
|
||||
|
||||
const dashboardsTab: Tab = {
|
||||
id: ContentTabIds.DASHBOARDS,
|
||||
name: i18n.translate('xpack.infra.infra.nodeDetails.tabs.dashboards', {
|
||||
defaultMessage: 'Dashboards',
|
||||
}),
|
||||
append: (
|
||||
<EuiBetaBadge
|
||||
label={i18n.translate('xpack.infra.customDashboards.technicalPreviewBadgeLabel', {
|
||||
defaultMessage: 'Technical preview',
|
||||
})}
|
||||
tooltipContent={i18n.translate(
|
||||
'xpack.infra.customDashboards.technicalPreviewBadgeDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'This functionality is in technical preview and may be changed or removed completely in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.',
|
||||
}
|
||||
)}
|
||||
iconType="beaker"
|
||||
size="s"
|
||||
style={{ verticalAlign: 'middle' }}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
export const hostDetailsTabs: Tab[] = [
|
||||
overviewTab,
|
||||
metadataTab,
|
||||
|
@ -99,7 +73,6 @@ export const hostDetailsTabs: Tab[] = [
|
|||
logsTab,
|
||||
anomaliesTab,
|
||||
osqueryTab,
|
||||
dashboardsTab,
|
||||
];
|
||||
export const hostDetailsFlyoutTabs: Tab[] = [...hostDetailsTabs];
|
||||
|
|
@ -12,7 +12,6 @@ import { DatePicker } from '../date_picker/date_picker';
|
|||
import { useTabSwitcherContext } from '../hooks/use_tab_switcher';
|
||||
import {
|
||||
Anomalies,
|
||||
Dashboards,
|
||||
Logs,
|
||||
Metadata,
|
||||
Metrics,
|
||||
|
@ -41,7 +40,6 @@ export const Content = () => {
|
|||
ContentTabIds.METRICS,
|
||||
ContentTabIds.PROCESSES,
|
||||
ContentTabIds.ANOMALIES,
|
||||
ContentTabIds.DASHBOARDS,
|
||||
]}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
@ -72,9 +70,6 @@ export const Content = () => {
|
|||
<TabPanel activeWhen={ContentTabIds.PROFILING}>
|
||||
<Profiling />
|
||||
</TabPanel>
|
||||
<TabPanel activeWhen={ContentTabIds.DASHBOARDS}>
|
||||
<Dashboards />
|
||||
</TabPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -60,7 +60,6 @@ const TabIdRT = rt.union([
|
|||
rt.literal(ContentTabIds.LOGS),
|
||||
rt.literal(ContentTabIds.ANOMALIES),
|
||||
rt.literal(ContentTabIds.OSQUERY),
|
||||
rt.literal(ContentTabIds.DASHBOARDS),
|
||||
]);
|
||||
|
||||
const AlertStatusRT = rt.union([
|
||||
|
|
|
@ -1,193 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { decodeOrThrow } from '@kbn/io-ts-utils';
|
||||
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
|
||||
import { useTrackedPromise } from '../../../hooks/use_tracked_promise';
|
||||
import type {
|
||||
InfraCustomDashboard,
|
||||
InfraSavedCustomDashboard,
|
||||
InfraCustomDashboardAssetType,
|
||||
} from '../../../../common/custom_dashboards';
|
||||
import {
|
||||
InfraCustomDashboardRT,
|
||||
InfraDeleteCustomDashboardsResponseBodyRT,
|
||||
} from '../../../../common/http_api/custom_dashboards_api';
|
||||
|
||||
type ActionType = 'create' | 'update' | 'delete';
|
||||
const errorMessages: Record<ActionType, string> = {
|
||||
create: i18n.translate('xpack.infra.linkDashboards.addFailure.toast.title', {
|
||||
defaultMessage: 'Error while linking dashboards',
|
||||
}),
|
||||
update: i18n.translate('xpack.infra.updatingLinkedDashboards.addFailure.toast.title', {
|
||||
defaultMessage: 'Error while updating linked dashboards',
|
||||
}),
|
||||
delete: i18n.translate('xpack.infra.deletingLinkedDashboards.addFailure.toast.title', {
|
||||
defaultMessage: 'Error while deleting linked dashboards',
|
||||
}),
|
||||
};
|
||||
export const useUpdateCustomDashboard = () => {
|
||||
const { services } = useKibanaContextForPlugin();
|
||||
const { notifications } = useKibana();
|
||||
|
||||
const onError = useCallback(
|
||||
(errorMessage: string) => {
|
||||
if (errorMessage) {
|
||||
notifications.toasts.danger({
|
||||
title: errorMessages.update,
|
||||
body: errorMessage,
|
||||
});
|
||||
}
|
||||
},
|
||||
[notifications.toasts]
|
||||
);
|
||||
|
||||
const [updateCustomDashboardRequest, updateCustomDashboard] = useTrackedPromise(
|
||||
{
|
||||
cancelPreviousOn: 'resolution',
|
||||
createPromise: async ({
|
||||
assetType,
|
||||
id,
|
||||
dashboardSavedObjectId,
|
||||
dashboardFilterAssetIdEnabled,
|
||||
}: InfraSavedCustomDashboard) => {
|
||||
const rawResponse = await services.http.fetch(
|
||||
`/api/infra/${assetType}/custom-dashboards/${id}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({
|
||||
assetType,
|
||||
dashboardSavedObjectId,
|
||||
dashboardFilterAssetIdEnabled,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
return decodeOrThrow(InfraCustomDashboardRT)(rawResponse);
|
||||
},
|
||||
onResolve: (response) => response,
|
||||
onReject: (e: Error | unknown) => onError((e as Error)?.message),
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const isUpdateLoading = updateCustomDashboardRequest.state === 'pending';
|
||||
|
||||
const hasUpdateError = updateCustomDashboardRequest.state === 'rejected';
|
||||
|
||||
return {
|
||||
updateCustomDashboard,
|
||||
isUpdateLoading,
|
||||
hasUpdateError,
|
||||
};
|
||||
};
|
||||
|
||||
export const useDeleteCustomDashboard = () => {
|
||||
const { services } = useKibanaContextForPlugin();
|
||||
const { notifications } = useKibana();
|
||||
|
||||
const onError = useCallback(
|
||||
(errorMessage: string) => {
|
||||
if (errorMessage) {
|
||||
notifications.toasts.danger({
|
||||
title: errorMessages.delete,
|
||||
body: errorMessage,
|
||||
});
|
||||
}
|
||||
},
|
||||
[notifications.toasts]
|
||||
);
|
||||
|
||||
const [deleteCustomDashboardRequest, deleteCustomDashboard] = useTrackedPromise(
|
||||
{
|
||||
cancelPreviousOn: 'resolution',
|
||||
createPromise: async ({
|
||||
assetType,
|
||||
id,
|
||||
}: {
|
||||
assetType: InfraCustomDashboardAssetType;
|
||||
id: string;
|
||||
}) => {
|
||||
const rawResponse = await services.http.fetch(
|
||||
`/api/infra/${assetType}/custom-dashboards/${id}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
}
|
||||
);
|
||||
|
||||
return decodeOrThrow(InfraDeleteCustomDashboardsResponseBodyRT)(rawResponse);
|
||||
},
|
||||
onResolve: (response) => response,
|
||||
onReject: (e: Error | unknown) => onError((e as Error)?.message),
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const isDeleteLoading = deleteCustomDashboardRequest.state === 'pending';
|
||||
|
||||
const hasDeleteError = deleteCustomDashboardRequest.state === 'rejected';
|
||||
|
||||
return {
|
||||
deleteCustomDashboard,
|
||||
isDeleteLoading,
|
||||
hasDeleteError,
|
||||
};
|
||||
};
|
||||
|
||||
export const useCreateCustomDashboard = () => {
|
||||
const { services } = useKibanaContextForPlugin();
|
||||
const { notifications } = useKibana();
|
||||
|
||||
const onError = useCallback(
|
||||
(errorMessage: string) => {
|
||||
if (errorMessage) {
|
||||
notifications.toasts.danger({
|
||||
title: errorMessages.delete,
|
||||
body: errorMessage,
|
||||
});
|
||||
}
|
||||
},
|
||||
[notifications.toasts]
|
||||
);
|
||||
|
||||
const [createCustomDashboardRequest, createCustomDashboard] = useTrackedPromise(
|
||||
{
|
||||
cancelPreviousOn: 'resolution',
|
||||
createPromise: async ({
|
||||
assetType,
|
||||
dashboardSavedObjectId,
|
||||
dashboardFilterAssetIdEnabled,
|
||||
}: InfraCustomDashboard) => {
|
||||
const rawResponse = await services.http.fetch(`/api/infra/${assetType}/custom-dashboards`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
dashboardSavedObjectId,
|
||||
dashboardFilterAssetIdEnabled,
|
||||
}),
|
||||
});
|
||||
|
||||
return decodeOrThrow(InfraCustomDashboardRT)(rawResponse);
|
||||
},
|
||||
onResolve: (response) => response,
|
||||
onReject: (e: Error | unknown) => onError((e as Error)?.message),
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const isCreateLoading = createCustomDashboardRequest.state === 'pending';
|
||||
|
||||
const hasCreateError = createCustomDashboardRequest.state === 'rejected';
|
||||
|
||||
return {
|
||||
createCustomDashboard,
|
||||
isCreateLoading,
|
||||
hasCreateError,
|
||||
};
|
||||
};
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import type { SearchDashboardsResponse } from '@kbn/dashboard-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { FETCH_STATUS } from '../../../hooks/use_fetcher';
|
||||
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
|
||||
import { useTabSwitcherContext } from './use_tab_switcher';
|
||||
|
||||
export interface SearchDashboardsResult {
|
||||
data: SearchDashboardsResponse['hits'];
|
||||
status: FETCH_STATUS;
|
||||
}
|
||||
|
||||
export function useDashboardFetcher(query = ''): SearchDashboardsResult {
|
||||
const {
|
||||
services: { dashboard },
|
||||
} = useKibanaContextForPlugin();
|
||||
const { notifications } = useKibana();
|
||||
const [result, setResult] = useState<SearchDashboardsResult>({
|
||||
data: [],
|
||||
status: FETCH_STATUS.NOT_INITIATED,
|
||||
});
|
||||
const { isActiveTab } = useTabSwitcherContext();
|
||||
|
||||
useEffect(() => {
|
||||
const getDashboards = async () => {
|
||||
setResult({
|
||||
data: [],
|
||||
status: FETCH_STATUS.LOADING,
|
||||
});
|
||||
try {
|
||||
const findDashboardsService = await dashboard?.findDashboardsService();
|
||||
const data = await findDashboardsService.search({
|
||||
search: query,
|
||||
size: 1000,
|
||||
});
|
||||
|
||||
setResult({
|
||||
data: data.hits,
|
||||
status: FETCH_STATUS.SUCCESS,
|
||||
});
|
||||
} catch (error) {
|
||||
setResult({
|
||||
data: [],
|
||||
status: FETCH_STATUS.FAILURE,
|
||||
});
|
||||
notifications.toasts.danger({
|
||||
title: i18n.translate('xpack.infra.fetchingDashboards.addFailure.toast.title', {
|
||||
defaultMessage: 'Error while fetching dashboards',
|
||||
}),
|
||||
body: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
if (isActiveTab('dashboards')) {
|
||||
getDashboards();
|
||||
}
|
||||
}, [dashboard, notifications.toasts, query, isActiveTab]);
|
||||
return result;
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
|
||||
import { decodeOrThrow } from '@kbn/io-ts-utils';
|
||||
import { isPending, useFetcher } from '../../../hooks/use_fetcher';
|
||||
import { InfraGetCustomDashboardsResponseBodyRT } from '../../../../common/http_api/custom_dashboards_api';
|
||||
import { useRequestObservable } from './use_request_observable';
|
||||
import { useTabSwitcherContext } from './use_tab_switcher';
|
||||
|
||||
interface UseDashboardProps {
|
||||
assetType: InventoryItemType;
|
||||
}
|
||||
|
||||
export function useFetchCustomDashboards({ assetType }: UseDashboardProps) {
|
||||
const { isActiveTab } = useTabSwitcherContext();
|
||||
const { request$ } = useRequestObservable();
|
||||
|
||||
const { data, status, error, refetch } = useFetcher(
|
||||
async (callApi) => {
|
||||
const response = await callApi(`/api/infra/${assetType}/custom-dashboards`, {
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
return decodeOrThrow(InfraGetCustomDashboardsResponseBodyRT)(response);
|
||||
},
|
||||
[assetType],
|
||||
{
|
||||
requestObservable$: request$,
|
||||
autoFetch: isActiveTab('dashboards'),
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
error: (error && error.message) || null,
|
||||
loading: isPending(status),
|
||||
dashboards: data,
|
||||
reload: refetch,
|
||||
};
|
||||
}
|
|
@ -12,8 +12,6 @@ import {
|
|||
type EuiPageHeaderProps,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useUiSetting } from '@kbn/kibana-react-plugin/public';
|
||||
import { enableInfrastructureAssetCustomDashboards } from '@kbn/observability-plugin/common';
|
||||
import type { RouteState } from '@kbn/metrics-data-access-plugin/public';
|
||||
import { capitalize, isEmpty } from 'lodash';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
@ -118,17 +116,13 @@ const useRightSideItems = (links?: LinkOptions[]) => {
|
|||
const useFeatureFlagTabs = () => {
|
||||
const { featureFlags } = usePluginConfig();
|
||||
const isProfilingEnabled = useProfilingIntegrationSetting();
|
||||
const isInfrastructureAssetCustomDashboardsEnabled = useUiSetting<boolean>(
|
||||
enableInfrastructureAssetCustomDashboards
|
||||
);
|
||||
|
||||
const featureFlagControlledTabs: Partial<Record<ContentTabIds, boolean>> = useMemo(
|
||||
() => ({
|
||||
[ContentTabIds.OSQUERY]: featureFlags.osqueryEnabled,
|
||||
[ContentTabIds.PROFILING]: isProfilingEnabled,
|
||||
[ContentTabIds.DASHBOARDS]: isInfrastructureAssetCustomDashboardsEnabled,
|
||||
}),
|
||||
[featureFlags.osqueryEnabled, isInfrastructureAssetCustomDashboardsEnabled, isProfilingEnabled]
|
||||
[featureFlags.osqueryEnabled, isProfilingEnabled]
|
||||
);
|
||||
|
||||
const isTabEnabled = useCallback(
|
||||
|
|
|
@ -1,175 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { EditDashboard, UnlinkDashboard, LinkDashboard } from '.';
|
||||
import { useTabSwitcherContext } from '../../../hooks/use_tab_switcher';
|
||||
import * as fetchCustomDashboards from '../../../hooks/use_fetch_custom_dashboards';
|
||||
import * as hooks from '../../../hooks/use_saved_objects_permissions';
|
||||
|
||||
const TEST_CURRENT_DASHBOARD = {
|
||||
title: 'Test Dashboard',
|
||||
id: 'test-so-id',
|
||||
dashboardSavedObjectId: 'test-dashboard-id',
|
||||
assetType: 'host',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
} as const;
|
||||
|
||||
jest.mock('../../../hooks/use_tab_switcher');
|
||||
|
||||
const tabSwitcherContextHookMock = useTabSwitcherContext as jest.MockedFunction<
|
||||
typeof useTabSwitcherContext
|
||||
>;
|
||||
|
||||
describe('Custom Dashboards Actions', () => {
|
||||
const mockUseSearchSession = () => {
|
||||
tabSwitcherContextHookMock.mockReturnValue({
|
||||
...tabSwitcherContextHookMock(),
|
||||
isActiveTab: jest.fn(() => true),
|
||||
});
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
mockUseSearchSession();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should render the edit dashboard action when the user can edit', () => {
|
||||
jest.spyOn(hooks, 'useSavedObjectUserPermissions').mockImplementation(() => ({
|
||||
canSave: true,
|
||||
canDelete: true,
|
||||
}));
|
||||
render(
|
||||
<EditDashboard
|
||||
onRefresh={() => {}}
|
||||
currentDashboard={TEST_CURRENT_DASHBOARD}
|
||||
assetType="host"
|
||||
/>
|
||||
);
|
||||
expect(screen.getByTestId('infraEditCustomDashboardMenu')).not.toBeDisabled();
|
||||
expect(screen.getByTestId('infraEditCustomDashboardMenu')).toHaveTextContent(
|
||||
'Edit dashboard link'
|
||||
);
|
||||
});
|
||||
it('should render the edit dashboard action when the user cannot edit', () => {
|
||||
jest.spyOn(hooks, 'useSavedObjectUserPermissions').mockImplementation(() => ({
|
||||
canSave: false,
|
||||
canDelete: true,
|
||||
}));
|
||||
render(
|
||||
<EditDashboard
|
||||
onRefresh={() => {}}
|
||||
currentDashboard={TEST_CURRENT_DASHBOARD}
|
||||
assetType="host"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('infraEditCustomDashboardMenu')).toBeDisabled();
|
||||
expect(screen.getByTestId('infraEditCustomDashboardMenu')).toHaveTextContent(
|
||||
'Edit dashboard link'
|
||||
);
|
||||
});
|
||||
it('should render the link dashboard action when the user can link a dashboard', () => {
|
||||
jest.spyOn(hooks, 'useSavedObjectUserPermissions').mockImplementation(() => ({
|
||||
canSave: true,
|
||||
canDelete: true,
|
||||
}));
|
||||
render(<LinkDashboard onRefresh={() => {}} assetType="host" />);
|
||||
|
||||
expect(screen.getByTestId('infraAddDashboard')).not.toBeDisabled();
|
||||
expect(screen.getByTestId('infraAddDashboard')).toHaveTextContent('Link dashboard');
|
||||
});
|
||||
it('should render the link dashboard action when the user cannot link a dashboard', () => {
|
||||
jest.spyOn(hooks, 'useSavedObjectUserPermissions').mockImplementation(() => ({
|
||||
canSave: false,
|
||||
canDelete: true,
|
||||
}));
|
||||
render(<LinkDashboard onRefresh={() => {}} assetType="host" />);
|
||||
|
||||
expect(screen.getByTestId('infraAddDashboard')).toBeDisabled();
|
||||
expect(screen.getByTestId('infraAddDashboard')).toHaveTextContent('Link dashboard');
|
||||
});
|
||||
it('should render the link new dashboard action when the user can link a dashboard', () => {
|
||||
jest.spyOn(hooks, 'useSavedObjectUserPermissions').mockImplementation(() => ({
|
||||
canSave: true,
|
||||
canDelete: true,
|
||||
}));
|
||||
render(<LinkDashboard onRefresh={() => {}} newDashboardButton assetType="host" />);
|
||||
expect(screen.getByTestId('infraLinkDashboardMenu')).not.toBeDisabled();
|
||||
expect(screen.getByTestId('infraLinkDashboardMenu')).toHaveTextContent('Link new dashboard');
|
||||
});
|
||||
it('should render the link new dashboard action when the user cannot link a dashboard', () => {
|
||||
jest.spyOn(hooks, 'useSavedObjectUserPermissions').mockImplementation(() => ({
|
||||
canSave: false,
|
||||
canDelete: true,
|
||||
}));
|
||||
render(<LinkDashboard onRefresh={() => {}} newDashboardButton assetType="host" />);
|
||||
expect(screen.getByTestId('infraLinkDashboardMenu')).toBeDisabled();
|
||||
expect(screen.getByTestId('infraLinkDashboardMenu')).toHaveTextContent('Link new dashboard');
|
||||
});
|
||||
|
||||
describe('UnlinkDashboard', () => {
|
||||
const fetchCustomDashboardsSpy = jest.spyOn(fetchCustomDashboards, 'useFetchCustomDashboards');
|
||||
|
||||
beforeEach(() => {
|
||||
// provide mock for invocation to fetch custom dashboards
|
||||
fetchCustomDashboardsSpy.mockReturnValue({
|
||||
dashboards: [
|
||||
{
|
||||
id: 'test-so-id',
|
||||
dashboardSavedObjectId: 'test-dashboard-id',
|
||||
assetType: 'host',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
},
|
||||
],
|
||||
loading: false,
|
||||
error: null,
|
||||
// @ts-expect-error we provide a mock function as we don't need to test the actual implementation
|
||||
refetch: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fetchCustomDashboardsSpy.mockClear();
|
||||
});
|
||||
|
||||
it('should render the unlink dashboard action when the user can unlink a dashboard', () => {
|
||||
jest.spyOn(hooks, 'useSavedObjectUserPermissions').mockImplementation(() => ({
|
||||
canSave: true,
|
||||
canDelete: true,
|
||||
}));
|
||||
render(
|
||||
<UnlinkDashboard
|
||||
onRefresh={() => {}}
|
||||
assetType="host"
|
||||
currentDashboard={TEST_CURRENT_DASHBOARD}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByTestId('infraUnLinkDashboardMenu')).not.toBeDisabled();
|
||||
expect(screen.getByTestId('infraUnLinkDashboardMenu')).toHaveTextContent('Unlink dashboard');
|
||||
});
|
||||
it('should render the unlink dashboard action when the user cannot unlink a dashboard', () => {
|
||||
jest.spyOn(hooks, 'useSavedObjectUserPermissions').mockImplementation(() => ({
|
||||
canSave: true,
|
||||
canDelete: false,
|
||||
}));
|
||||
render(
|
||||
<UnlinkDashboard
|
||||
onRefresh={() => {}}
|
||||
assetType="host"
|
||||
currentDashboard={TEST_CURRENT_DASHBOARD}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByTestId('infraUnLinkDashboardMenu')).toBeDisabled();
|
||||
expect(screen.getByTestId('infraUnLinkDashboardMenu')).toHaveTextContent('Unlink dashboard');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import type {
|
||||
DashboardItemWithTitle,
|
||||
InfraCustomDashboardAssetType,
|
||||
} from '../../../../../../common/custom_dashboards';
|
||||
import { useSavedObjectUserPermissions } from '../../../hooks/use_saved_objects_permissions';
|
||||
import { SaveDashboardModal } from './save_dashboard_modal';
|
||||
|
||||
export function EditDashboard({
|
||||
onRefresh,
|
||||
currentDashboard,
|
||||
assetType,
|
||||
}: {
|
||||
onRefresh: () => void;
|
||||
currentDashboard: DashboardItemWithTitle;
|
||||
assetType: InfraCustomDashboardAssetType;
|
||||
}) {
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const { canSave } = useSavedObjectUserPermissions();
|
||||
|
||||
const onClick = useCallback(() => setIsModalVisible(!isModalVisible), [isModalVisible]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
content={
|
||||
!canSave
|
||||
? i18n.translate(
|
||||
'xpack.infra.linkDashboard.tooltip.youDoNotHavePermissionToUseThisFeature',
|
||||
{
|
||||
defaultMessage:
|
||||
'You do not have permission to use this feature. Please ask your administrator for access.',
|
||||
}
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
color="text"
|
||||
size="s"
|
||||
iconType="pencil"
|
||||
data-test-subj="infraEditCustomDashboardMenu"
|
||||
onClick={onClick}
|
||||
disabled={!canSave}
|
||||
>
|
||||
{i18n.translate('xpack.infra.customDashboards.editEmptyButtonLabel', {
|
||||
defaultMessage: 'Edit dashboard link',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
</EuiToolTip>
|
||||
|
||||
{isModalVisible && (
|
||||
<SaveDashboardModal
|
||||
onClose={onClick}
|
||||
onRefresh={onRefresh}
|
||||
currentDashboard={currentDashboard}
|
||||
assetType={assetType}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { EuiButtonEmpty } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics';
|
||||
import type { DashboardItemWithTitle } from '../../../../../../common/custom_dashboards';
|
||||
import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana';
|
||||
|
||||
export function GotoDashboardLink({
|
||||
currentDashboard,
|
||||
}: {
|
||||
currentDashboard: DashboardItemWithTitle;
|
||||
}) {
|
||||
const {
|
||||
services: { share },
|
||||
} = useKibanaContextForPlugin();
|
||||
|
||||
const url = share?.url.locators.get(DASHBOARD_APP_LOCATOR)?.getRedirectUrl({
|
||||
dashboardId: currentDashboard?.dashboardSavedObjectId,
|
||||
});
|
||||
return (
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="infraGotoDashboardGoToDashboardButton"
|
||||
color="text"
|
||||
size="s"
|
||||
iconType="visGauge"
|
||||
href={url}
|
||||
>
|
||||
{i18n.translate('xpack.infra.customDashboards.contextMenu.goToDashboard', {
|
||||
defaultMessage: 'Go to dashboard',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { LinkDashboard } from './link_dashboard';
|
||||
export { GotoDashboardLink } from './goto_dashboard_link';
|
||||
export { EditDashboard } from './edit_dashboard';
|
||||
export { UnlinkDashboard } from './unlink_dashboard';
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { EuiButton, EuiToolTip, EuiButtonEmpty } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import type {
|
||||
DashboardItemWithTitle,
|
||||
InfraCustomDashboardAssetType,
|
||||
} from '../../../../../../common/custom_dashboards';
|
||||
import { SaveDashboardModal } from './save_dashboard_modal';
|
||||
import { useSavedObjectUserPermissions } from '../../../hooks/use_saved_objects_permissions';
|
||||
|
||||
export function LinkDashboard({
|
||||
onRefresh,
|
||||
newDashboardButton = false,
|
||||
customDashboards,
|
||||
assetType,
|
||||
}: {
|
||||
onRefresh: () => void;
|
||||
newDashboardButton?: boolean;
|
||||
customDashboards?: DashboardItemWithTitle[];
|
||||
assetType: InfraCustomDashboardAssetType;
|
||||
}) {
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const { canSave } = useSavedObjectUserPermissions();
|
||||
|
||||
const onClick = useCallback(() => setIsModalVisible(true), []);
|
||||
const onClose = useCallback(() => setIsModalVisible(false), []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
content={
|
||||
!canSave ? (
|
||||
<p data-test-subj="infraCannotAddDashboardTooltip">
|
||||
{i18n.translate(
|
||||
'xpack.infra.linkDashboard.tooltip.youDoNotHavePermissionToUseThisFeature',
|
||||
{
|
||||
defaultMessage:
|
||||
'You do not have permission to use this feature. Please ask your administrator for access.',
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
{newDashboardButton ? (
|
||||
<EuiButtonEmpty
|
||||
color="text"
|
||||
size="s"
|
||||
iconType="plusInCircle"
|
||||
data-test-subj="infraLinkDashboardMenu"
|
||||
onClick={onClick}
|
||||
disabled={!canSave}
|
||||
>
|
||||
{i18n.translate('xpack.infra.assetDetails.dashboards.linkNewDashboardButtonLabel', {
|
||||
defaultMessage: 'Link new dashboard',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
) : (
|
||||
<EuiButton data-test-subj="infraAddDashboard" onClick={onClick} disabled={!canSave}>
|
||||
{i18n.translate('xpack.infra.assetDetails.dashboards.linkButtonLabel', {
|
||||
defaultMessage: 'Link dashboard',
|
||||
})}
|
||||
</EuiButton>
|
||||
)}
|
||||
</EuiToolTip>
|
||||
{isModalVisible && (
|
||||
<SaveDashboardModal
|
||||
onClose={onClose}
|
||||
onRefresh={onRefresh}
|
||||
customDashboards={customDashboards}
|
||||
assetType={assetType}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,275 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import type { EuiComboBoxOptionOption } from '@elastic/eui';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiModal,
|
||||
EuiModalFooter,
|
||||
EuiModalHeader,
|
||||
EuiModalHeaderTitle,
|
||||
EuiSwitch,
|
||||
EuiModalBody,
|
||||
EuiComboBox,
|
||||
EuiFlexGroup,
|
||||
EuiToolTip,
|
||||
EuiIcon,
|
||||
EuiButtonEmpty,
|
||||
useEuiTheme,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { FETCH_STATUS } from '../../../../../hooks/use_fetcher';
|
||||
import type {
|
||||
DashboardItemWithTitle,
|
||||
InfraCustomDashboardAssetType,
|
||||
} from '../../../../../../common/custom_dashboards';
|
||||
import { useDashboardFetcher } from '../../../hooks/use_dashboards_fetcher';
|
||||
import {
|
||||
useUpdateCustomDashboard,
|
||||
useCreateCustomDashboard,
|
||||
} from '../../../hooks/use_custom_dashboards';
|
||||
import { useAssetDetailsUrlState } from '../../../hooks/use_asset_details_url_state';
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
onRefresh: () => void;
|
||||
currentDashboard?: DashboardItemWithTitle;
|
||||
customDashboards?: DashboardItemWithTitle[];
|
||||
assetType: InfraCustomDashboardAssetType;
|
||||
}
|
||||
|
||||
export function SaveDashboardModal({
|
||||
onClose,
|
||||
onRefresh,
|
||||
currentDashboard,
|
||||
customDashboards,
|
||||
assetType,
|
||||
}: Props) {
|
||||
const { notifications } = useKibana();
|
||||
const { data: allAvailableDashboards, status } = useDashboardFetcher();
|
||||
const [, setUrlState] = useAssetDetailsUrlState();
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
const [assetNameEnabled, setAssetNameFiltersEnabled] = useState(
|
||||
currentDashboard?.dashboardFilterAssetIdEnabled ?? true
|
||||
);
|
||||
const [selectedDashboard, setSelectedDashboard] = useState<
|
||||
Array<EuiComboBoxOptionOption<string>>
|
||||
>(
|
||||
currentDashboard
|
||||
? [{ label: currentDashboard.title, value: currentDashboard.dashboardSavedObjectId }]
|
||||
: []
|
||||
);
|
||||
|
||||
const { isUpdateLoading, updateCustomDashboard } = useUpdateCustomDashboard();
|
||||
const { isCreateLoading, createCustomDashboard } = useCreateCustomDashboard();
|
||||
|
||||
const isEditMode = !!currentDashboard?.id;
|
||||
const loading = isUpdateLoading || isCreateLoading;
|
||||
|
||||
const options = useMemo(
|
||||
() =>
|
||||
allAvailableDashboards?.map((dashboardItem) => ({
|
||||
label: dashboardItem.attributes.title,
|
||||
value: dashboardItem.id,
|
||||
disabled:
|
||||
customDashboards?.some(
|
||||
({ dashboardSavedObjectId }) => dashboardItem.id === dashboardSavedObjectId
|
||||
) ?? false,
|
||||
})),
|
||||
[allAvailableDashboards, customDashboards]
|
||||
);
|
||||
|
||||
const onChange = useCallback(
|
||||
() => setAssetNameFiltersEnabled(!assetNameEnabled),
|
||||
[assetNameEnabled]
|
||||
);
|
||||
const onSelect = useCallback((newSelection: any) => setSelectedDashboard(newSelection), []);
|
||||
|
||||
const onClickSave = useCallback(
|
||||
async function () {
|
||||
const [newDashboard] = selectedDashboard;
|
||||
try {
|
||||
if (!newDashboard.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dashboardParams = {
|
||||
assetType,
|
||||
dashboardSavedObjectId: newDashboard.value,
|
||||
dashboardFilterAssetIdEnabled: assetNameEnabled,
|
||||
};
|
||||
|
||||
const result =
|
||||
isEditMode && currentDashboard?.id
|
||||
? await updateCustomDashboard({
|
||||
...dashboardParams,
|
||||
id: currentDashboard.id,
|
||||
})
|
||||
: await createCustomDashboard(dashboardParams);
|
||||
|
||||
const getToastLabels = isEditMode ? getEditSuccessToastLabels : getLinkSuccessToastLabels;
|
||||
|
||||
if (result && !(isEditMode ? isUpdateLoading : isCreateLoading)) {
|
||||
notifications.toasts.success(getToastLabels(newDashboard.label));
|
||||
}
|
||||
|
||||
setUrlState({ dashboardId: newDashboard.value });
|
||||
onRefresh();
|
||||
} catch (error) {
|
||||
notifications.toasts.danger({
|
||||
title: i18n.translate('xpack.infra.customDashboards.addFailure.toast.title', {
|
||||
defaultMessage: 'Error while adding "{dashboardName}" dashboard',
|
||||
values: { dashboardName: newDashboard.label },
|
||||
}),
|
||||
body: error.message,
|
||||
});
|
||||
}
|
||||
onClose();
|
||||
},
|
||||
[
|
||||
selectedDashboard,
|
||||
onClose,
|
||||
isEditMode,
|
||||
setUrlState,
|
||||
onRefresh,
|
||||
updateCustomDashboard,
|
||||
assetType,
|
||||
currentDashboard?.id,
|
||||
assetNameEnabled,
|
||||
isUpdateLoading,
|
||||
notifications.toasts,
|
||||
createCustomDashboard,
|
||||
isCreateLoading,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiModal onClose={onClose} data-test-subj="infraSelectCustomDashboard">
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
{isEditMode
|
||||
? i18n.translate('xpack.infra.customDashboards.selectDashboard.modalTitle.edit', {
|
||||
defaultMessage: 'Edit dashboard',
|
||||
})
|
||||
: i18n.translate('xpack.infra.customDashboards.selectDashboard.modalTitle.link', {
|
||||
defaultMessage: 'Select dashboard',
|
||||
})}
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
|
||||
<EuiModalBody>
|
||||
<EuiFlexGroup direction="column" justifyContent="center">
|
||||
<EuiComboBox
|
||||
isLoading={status === FETCH_STATUS.LOADING}
|
||||
isDisabled={status === FETCH_STATUS.LOADING || isEditMode}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.infra.customDashboards.selectDashboard.placeholder',
|
||||
{
|
||||
defaultMessage: 'Select dashboard',
|
||||
}
|
||||
)}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
options={options}
|
||||
selectedOptions={selectedDashboard}
|
||||
onChange={onSelect}
|
||||
isClearable
|
||||
/>
|
||||
|
||||
<EuiSwitch
|
||||
css={{ alignItems: 'center' }}
|
||||
compressed
|
||||
label={
|
||||
<p>
|
||||
<span css={{ marginRight: euiTheme.size.xs }}>
|
||||
{i18n.translate(
|
||||
'xpack.infra.customDashboard.addDashboard.useContextFilterLabel',
|
||||
{
|
||||
defaultMessage: 'Filter by host name',
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
<EuiToolTip
|
||||
position="bottom"
|
||||
content={i18n.translate(
|
||||
'xpack.infra.customDashboard.addDashboard.useContextFilterLabel.tooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
'Enabling this option will apply filters to the dashboard based on your chosen host.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiIcon
|
||||
type="questionInCircle"
|
||||
title={i18n.translate(
|
||||
'xpack.infra.saveDashboardModal.euiIcon.iconWithTooltipLabel',
|
||||
{ defaultMessage: 'Icon with tooltip' }
|
||||
)}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</p>
|
||||
}
|
||||
onChange={onChange}
|
||||
checked={assetNameEnabled}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
</EuiModalBody>
|
||||
|
||||
<EuiModalFooter>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="infraSelectDashboardCancelButton"
|
||||
onClick={onClose}
|
||||
isDisabled={loading}
|
||||
>
|
||||
{i18n.translate('xpack.infra.customDashboards.selectDashboard.cancel', {
|
||||
defaultMessage: 'Cancel',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
<EuiButton
|
||||
data-test-subj="infraSelectDashboardButton"
|
||||
onClick={onClickSave}
|
||||
isLoading={loading}
|
||||
fill
|
||||
>
|
||||
{isEditMode
|
||||
? i18n.translate('xpack.infra.customDashboards.selectDashboard.edit', {
|
||||
defaultMessage: 'Save',
|
||||
})
|
||||
: i18n.translate('xpack.infra.customDashboards.selectDashboard.add', {
|
||||
defaultMessage: 'Link dashboard',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</EuiModal>
|
||||
);
|
||||
}
|
||||
|
||||
function getLinkSuccessToastLabels(dashboardName: string) {
|
||||
return {
|
||||
title: i18n.translate('xpack.infra.customDashboards.linkSuccess.toast.title', {
|
||||
defaultMessage: 'Added "{dashboardName}" dashboard',
|
||||
values: { dashboardName },
|
||||
}),
|
||||
body: i18n.translate('xpack.infra.customDashboards.linkSuccess.toast.text', {
|
||||
defaultMessage: 'Your dashboard is now visible in the asset details page.',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function getEditSuccessToastLabels(dashboardName: string) {
|
||||
return {
|
||||
title: i18n.translate('xpack.infra.customDashboards.editSuccess.toast.title', {
|
||||
defaultMessage: 'Edited "{dashboardName}" dashboard',
|
||||
values: { dashboardName },
|
||||
}),
|
||||
body: i18n.translate('xpack.infra.customDashboards.editSuccess.toast.text', {
|
||||
defaultMessage: 'Your dashboard link has been updated',
|
||||
}),
|
||||
};
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { EuiButtonEmpty, EuiConfirmModal, EuiToolTip } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import type {
|
||||
DashboardItemWithTitle,
|
||||
InfraCustomDashboardAssetType,
|
||||
} from '../../../../../../common/custom_dashboards';
|
||||
import { useDeleteCustomDashboard } from '../../../hooks/use_custom_dashboards';
|
||||
import { useFetchCustomDashboards } from '../../../hooks/use_fetch_custom_dashboards';
|
||||
import { useAssetDetailsUrlState } from '../../../hooks/use_asset_details_url_state';
|
||||
import { useSavedObjectUserPermissions } from '../../../hooks/use_saved_objects_permissions';
|
||||
|
||||
export function UnlinkDashboard({
|
||||
currentDashboard,
|
||||
onRefresh,
|
||||
assetType,
|
||||
}: {
|
||||
currentDashboard: DashboardItemWithTitle;
|
||||
onRefresh: () => void;
|
||||
assetType: InfraCustomDashboardAssetType;
|
||||
}) {
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const { notifications } = useKibana();
|
||||
|
||||
const [, setUrlState] = useAssetDetailsUrlState();
|
||||
const { deleteCustomDashboard, isDeleteLoading } = useDeleteCustomDashboard();
|
||||
const { dashboards, loading } = useFetchCustomDashboards({ assetType });
|
||||
const { canDelete } = useSavedObjectUserPermissions();
|
||||
|
||||
const onClick = useCallback(() => setIsModalVisible(true), []);
|
||||
const onCancel = useCallback(() => setIsModalVisible(false), []);
|
||||
const onError = useCallback(() => setIsModalVisible(!isModalVisible), [isModalVisible]);
|
||||
|
||||
const onConfirm = useCallback(
|
||||
async function () {
|
||||
try {
|
||||
const linkedDashboards = (dashboards ?? []).filter(
|
||||
({ dashboardSavedObjectId }) =>
|
||||
dashboardSavedObjectId !== currentDashboard.dashboardSavedObjectId
|
||||
);
|
||||
const result = await deleteCustomDashboard({
|
||||
assetType,
|
||||
id: currentDashboard.id,
|
||||
});
|
||||
setUrlState({ dashboardId: linkedDashboards[0]?.dashboardSavedObjectId });
|
||||
|
||||
if (result) {
|
||||
notifications.toasts.success({
|
||||
title: i18n.translate('xpack.infra.customDashboards.unlinkSuccess.toast.title', {
|
||||
defaultMessage: 'Unlinked "{dashboardName}" dashboard',
|
||||
values: { dashboardName: currentDashboard?.title },
|
||||
}),
|
||||
});
|
||||
onRefresh();
|
||||
}
|
||||
} catch (error) {
|
||||
notifications.toasts.danger({
|
||||
title: i18n.translate('xpack.infra.customDashboards.unlinkFailure.toast.title', {
|
||||
defaultMessage: 'Error while unlinking "{dashboardName}" dashboard',
|
||||
values: { dashboardName: currentDashboard?.title },
|
||||
}),
|
||||
body: error.body.message,
|
||||
});
|
||||
}
|
||||
onError();
|
||||
},
|
||||
[
|
||||
onError,
|
||||
dashboards,
|
||||
deleteCustomDashboard,
|
||||
assetType,
|
||||
currentDashboard.id,
|
||||
currentDashboard.dashboardSavedObjectId,
|
||||
currentDashboard?.title,
|
||||
setUrlState,
|
||||
notifications.toasts,
|
||||
onRefresh,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
content={
|
||||
!canDelete
|
||||
? i18n.translate(
|
||||
'xpack.infra.linkDashboard.tooltip.youDoNotHavePermissionToUseThisFeature',
|
||||
{
|
||||
defaultMessage:
|
||||
'You do not have permission to use this feature. Please ask your administrator for access.',
|
||||
}
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
color="danger"
|
||||
size="s"
|
||||
iconType="unlink"
|
||||
data-test-subj="infraUnLinkDashboardMenu"
|
||||
onClick={onClick}
|
||||
disabled={!canDelete}
|
||||
>
|
||||
{i18n.translate('xpack.infra.customDashboards.unlinkEmptyButtonLabel', {
|
||||
defaultMessage: 'Unlink dashboard',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
</EuiToolTip>
|
||||
{isModalVisible && (
|
||||
<EuiConfirmModal
|
||||
title={i18n.translate(
|
||||
'xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.title',
|
||||
{
|
||||
defaultMessage: 'Unlink Dashboard',
|
||||
}
|
||||
)}
|
||||
onCancel={onCancel}
|
||||
onConfirm={onConfirm}
|
||||
confirmButtonText={i18n.translate(
|
||||
'xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.button',
|
||||
{
|
||||
defaultMessage: 'Unlink dashboard',
|
||||
}
|
||||
)}
|
||||
buttonColor="danger"
|
||||
defaultFocusedButton="confirm"
|
||||
isLoading={loading || isDeleteLoading}
|
||||
>
|
||||
<p>
|
||||
{i18n.translate('xpack.infra.customDashboards.unlinkEmptyButtonLabel.confirm.body', {
|
||||
defaultMessage: `You are about to unlink the dashboard from the {assetType} context`,
|
||||
values: { assetType },
|
||||
})}
|
||||
</p>
|
||||
</EuiConfirmModal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import React from 'react';
|
||||
import { EuiButtonIcon, EuiContextMenuPanel, EuiContextMenuItem, EuiPopover } from '@elastic/eui';
|
||||
import { useBoolean } from '@kbn/react-hooks';
|
||||
|
||||
interface Props {
|
||||
items: React.ReactNode[];
|
||||
}
|
||||
|
||||
export function ContextMenu({ items }: Props) {
|
||||
const [isPopoverOpen, { off: closePopover, toggle: togglePopover }] = useBoolean(false);
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
data-test-subj="infraDashboardsContextMenuButton"
|
||||
display="base"
|
||||
size="s"
|
||||
iconType="boxesVertical"
|
||||
aria-label={i18n.translate('xpack.infra.assetDetails.dashboards.contextMenu.moreLabel', {
|
||||
defaultMessage: 'More',
|
||||
})}
|
||||
onClick={togglePopover}
|
||||
/>
|
||||
}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={closePopover}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition="downLeft"
|
||||
>
|
||||
<EuiContextMenuPanel
|
||||
size="s"
|
||||
items={items.map((item, index) => (
|
||||
<EuiContextMenuItem key={index} size="s">
|
||||
{item}
|
||||
</EuiContextMenuItem>
|
||||
))}
|
||||
/>
|
||||
</EuiPopover>
|
||||
);
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import useMount from 'react-use/lib/useMount';
|
||||
import { EuiComboBox } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { DashboardItemWithTitle } from '../../../../../common/custom_dashboards';
|
||||
import { useAssetDetailsUrlState } from '../../hooks/use_asset_details_url_state';
|
||||
|
||||
interface Props {
|
||||
customDashboards: DashboardItemWithTitle[];
|
||||
currentDashboardId?: string;
|
||||
setCurrentDashboard: (newDashboard: DashboardItemWithTitle) => void;
|
||||
onRefresh: () => void;
|
||||
}
|
||||
|
||||
export function DashboardSelector({
|
||||
customDashboards,
|
||||
currentDashboardId,
|
||||
setCurrentDashboard,
|
||||
onRefresh,
|
||||
}: Props) {
|
||||
const [, setUrlState] = useAssetDetailsUrlState();
|
||||
|
||||
const [selectedDashboard, setSelectedDashboard] = useState<DashboardItemWithTitle>();
|
||||
|
||||
useMount(() => {
|
||||
if (!currentDashboardId) {
|
||||
setUrlState({ dashboardId: customDashboards[0]?.dashboardSavedObjectId });
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const preselectedDashboard = customDashboards.find(
|
||||
({ dashboardSavedObjectId }) => dashboardSavedObjectId === currentDashboardId
|
||||
);
|
||||
// preselect dashboard
|
||||
if (preselectedDashboard) {
|
||||
setSelectedDashboard(preselectedDashboard);
|
||||
setCurrentDashboard(preselectedDashboard);
|
||||
}
|
||||
}, [customDashboards, currentDashboardId, setCurrentDashboard]);
|
||||
|
||||
const onChange = useCallback(
|
||||
(newDashboardId?: string) => {
|
||||
setUrlState({ dashboardId: newDashboardId });
|
||||
onRefresh();
|
||||
},
|
||||
[onRefresh, setUrlState]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiComboBox
|
||||
compressed
|
||||
style={{ minWidth: '200px' }}
|
||||
placeholder={i18n.translate('xpack.infra.customDashboards.selectDashboard.placeholder', {
|
||||
defaultMessage: 'Select dashboard',
|
||||
})}
|
||||
prepend={i18n.translate('xpack.infra.customDashboards.selectDashboard.prepend', {
|
||||
defaultMessage: 'Dashboard',
|
||||
})}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
options={customDashboards.map(({ dashboardSavedObjectId, title }) => {
|
||||
return {
|
||||
label: title,
|
||||
value: dashboardSavedObjectId,
|
||||
};
|
||||
})}
|
||||
selectedOptions={
|
||||
selectedDashboard
|
||||
? [
|
||||
{
|
||||
value: selectedDashboard.dashboardSavedObjectId,
|
||||
label: selectedDashboard.title,
|
||||
},
|
||||
]
|
||||
: []
|
||||
}
|
||||
onChange={([newItem]) => onChange(newItem.value)}
|
||||
isClearable={false}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,290 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { useCallback, useEffect, useState, useMemo } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiPanel,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiTitle,
|
||||
EuiSpacer,
|
||||
EuiEmptyPrompt,
|
||||
EuiLoadingLogo,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import type { DashboardApi, DashboardCreationOptions } from '@kbn/dashboard-plugin/public';
|
||||
import { DashboardRenderer } from '@kbn/dashboard-plugin/public';
|
||||
import type { ViewMode } from '@kbn/presentation-publishing';
|
||||
|
||||
import type { DashboardSearchOut } from '@kbn/dashboard-plugin/server/content_management';
|
||||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
import {
|
||||
ASSET_DETAILS_FLYOUT_LOCATOR_ID,
|
||||
ASSET_DETAILS_LOCATOR_ID,
|
||||
} from '@kbn/observability-shared-plugin/common';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { decode } from '@kbn/rison';
|
||||
import { isEqual } from 'lodash';
|
||||
import { isPending } from '../../../../hooks/use_fetcher';
|
||||
import type { AssetDashboardLoadedParams } from '../../../../services/telemetry/types';
|
||||
import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana';
|
||||
import { buildAssetIdFilter } from '../../../../utils/filters/build';
|
||||
import type {
|
||||
InfraSavedCustomDashboard,
|
||||
DashboardItemWithTitle,
|
||||
} from '../../../../../common/custom_dashboards';
|
||||
|
||||
import { EmptyDashboards } from './empty_dashboards';
|
||||
import { EditDashboard, GotoDashboardLink, LinkDashboard, UnlinkDashboard } from './actions';
|
||||
import { useFetchCustomDashboards } from '../../hooks/use_fetch_custom_dashboards';
|
||||
import { useDatePickerContext } from '../../hooks/use_date_picker';
|
||||
import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props';
|
||||
import { useDashboardFetcher } from '../../hooks/use_dashboards_fetcher';
|
||||
import { useDataViewsContext } from '../../hooks/use_data_views';
|
||||
import { DashboardSelector } from './dashboard_selector';
|
||||
import { ContextMenu } from './context_menu';
|
||||
import { useAssetDetailsUrlState } from '../../hooks/use_asset_details_url_state';
|
||||
import { FilterExplanationCallout } from './filter_explanation_callout';
|
||||
|
||||
export function Dashboards() {
|
||||
const { dateRange } = useDatePickerContext();
|
||||
const { asset, renderMode } = useAssetDetailsRenderPropsContext();
|
||||
const location = useLocation();
|
||||
const {
|
||||
services: { share, telemetry },
|
||||
} = useKibanaContextForPlugin();
|
||||
const [dashboard, setDashboard] = useState<DashboardApi | undefined>();
|
||||
const [customDashboards, setCustomDashboards] = useState<DashboardItemWithTitle[]>([]);
|
||||
const [currentDashboard, setCurrentDashboard] = useState<DashboardItemWithTitle>();
|
||||
const [trackingEventProperties, setTrackingEventProperties] = useState({});
|
||||
const { data: allAvailableDashboards, status } = useDashboardFetcher();
|
||||
const { metrics } = useDataViewsContext();
|
||||
const [urlState, setUrlState] = useAssetDetailsUrlState();
|
||||
const trackOnlyOnceTheSameDashboardFilters = React.useRef(false);
|
||||
|
||||
const { dashboards, loading, reload } = useFetchCustomDashboards({ assetType: asset.type });
|
||||
|
||||
useEffect(() => {
|
||||
trackOnlyOnceTheSameDashboardFilters.current = false;
|
||||
if (currentDashboard) {
|
||||
const currentEventTrackingProperties: AssetDashboardLoadedParams = {
|
||||
assetType: asset.type,
|
||||
state: currentDashboard.dashboardFilterAssetIdEnabled,
|
||||
filtered_by: currentDashboard.dashboardFilterAssetIdEnabled ? ['assetId'] : [],
|
||||
};
|
||||
if (isEqual(trackingEventProperties, currentEventTrackingProperties)) {
|
||||
trackOnlyOnceTheSameDashboardFilters.current = true;
|
||||
return;
|
||||
}
|
||||
|
||||
setTrackingEventProperties(currentEventTrackingProperties);
|
||||
if (!trackOnlyOnceTheSameDashboardFilters.current) {
|
||||
telemetry.reportAssetDashboardLoaded(currentEventTrackingProperties);
|
||||
}
|
||||
}
|
||||
}, [asset.type, currentDashboard, telemetry, trackingEventProperties]);
|
||||
|
||||
useEffect(() => {
|
||||
const allAvailableDashboardsMap = new Map<string, DashboardSearchOut['hits'][number]>();
|
||||
allAvailableDashboards.forEach((availableDashboard) => {
|
||||
allAvailableDashboardsMap.set(availableDashboard.id, availableDashboard);
|
||||
});
|
||||
const filteredCustomDashboards =
|
||||
dashboards?.reduce<DashboardItemWithTitle[]>(
|
||||
(result: DashboardItemWithTitle[], customDashboard: InfraSavedCustomDashboard) => {
|
||||
const matchedDashboard = allAvailableDashboardsMap.get(
|
||||
customDashboard.dashboardSavedObjectId
|
||||
);
|
||||
if (matchedDashboard) {
|
||||
result.push({
|
||||
title: matchedDashboard.attributes.title,
|
||||
...customDashboard,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[]
|
||||
) ?? [];
|
||||
setCustomDashboards(filteredCustomDashboards);
|
||||
// set a default dashboard if there is no selected dashboard
|
||||
if (!urlState?.dashboardId) {
|
||||
setUrlState({
|
||||
dashboardId:
|
||||
currentDashboard?.dashboardSavedObjectId ??
|
||||
filteredCustomDashboards[0]?.dashboardSavedObjectId,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
allAvailableDashboards,
|
||||
currentDashboard?.dashboardSavedObjectId,
|
||||
dashboards,
|
||||
setUrlState,
|
||||
urlState?.dashboardId,
|
||||
]);
|
||||
|
||||
const getCreationOptions = useCallback((): Promise<DashboardCreationOptions> => {
|
||||
const getInitialInput = () => ({
|
||||
viewMode: 'view' as ViewMode,
|
||||
timeRange: { from: dateRange.from, to: dateRange.to },
|
||||
});
|
||||
return Promise.resolve<DashboardCreationOptions>({
|
||||
getInitialInput,
|
||||
});
|
||||
}, [dateRange.from, dateRange.to]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dashboard) return;
|
||||
dashboard.setFilters(
|
||||
metrics.dataView && currentDashboard?.dashboardFilterAssetIdEnabled
|
||||
? buildAssetIdFilter(asset.name, asset.type, metrics.dataView)
|
||||
: []
|
||||
);
|
||||
dashboard.setTimeRange({ from: dateRange.from, to: dateRange.to });
|
||||
dashboard.forceRefresh();
|
||||
}, [
|
||||
metrics.dataView,
|
||||
asset.name,
|
||||
dashboard,
|
||||
dateRange.from,
|
||||
dateRange.to,
|
||||
currentDashboard,
|
||||
asset.type,
|
||||
]);
|
||||
|
||||
const getLocatorParams = useCallback(
|
||||
(params: any, isFlyoutView: any) => {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const tableProperties = searchParams.get('tableProperties');
|
||||
const flyoutParams =
|
||||
isFlyoutView && tableProperties ? { tableProperties: decode(tableProperties) } : {};
|
||||
|
||||
return {
|
||||
assetDetails: { ...urlState, dashboardId: params.dashboardId },
|
||||
assetType: asset.type,
|
||||
assetId: asset.id,
|
||||
...flyoutParams,
|
||||
};
|
||||
},
|
||||
[asset.id, asset.type, location.search, urlState]
|
||||
);
|
||||
|
||||
const locator = useMemo(() => {
|
||||
const isFlyoutView = renderMode.mode === 'flyout';
|
||||
|
||||
const baseLocator = share.url.locators.get(
|
||||
isFlyoutView ? ASSET_DETAILS_FLYOUT_LOCATOR_ID : ASSET_DETAILS_LOCATOR_ID
|
||||
);
|
||||
|
||||
if (!baseLocator) return;
|
||||
|
||||
return {
|
||||
...baseLocator,
|
||||
getRedirectUrl: (params: SerializableRecord) =>
|
||||
baseLocator.getRedirectUrl(getLocatorParams(params, isFlyoutView)),
|
||||
navigate: (params: SerializableRecord) =>
|
||||
baseLocator.navigate(getLocatorParams(params, isFlyoutView)),
|
||||
};
|
||||
}, [renderMode.mode, share.url.locators, getLocatorParams]);
|
||||
|
||||
if ((loading || isPending(status)) && !dashboards?.length) {
|
||||
return (
|
||||
<EuiPanel hasBorder>
|
||||
<EuiEmptyPrompt
|
||||
icon={<EuiLoadingLogo logo="logoObservability" size="xl" />}
|
||||
title={
|
||||
<h4>
|
||||
{i18n.translate('xpack.infra.customDashboards.loadingCustomDashboards', {
|
||||
defaultMessage: 'Loading dashboard',
|
||||
})}
|
||||
</h4>
|
||||
}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiPanel hasBorder>
|
||||
{!!dashboards?.length ? (
|
||||
<>
|
||||
<EuiFlexGroup justifyContent="spaceBetween" gutterSize="xs" alignItems="center">
|
||||
<EuiFlexItem grow>
|
||||
<EuiTitle size="s">
|
||||
<h3>{currentDashboard?.title}</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<DashboardSelector
|
||||
currentDashboardId={urlState?.dashboardId}
|
||||
customDashboards={customDashboards}
|
||||
setCurrentDashboard={setCurrentDashboard}
|
||||
onRefresh={reload}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
{currentDashboard && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<ContextMenu
|
||||
items={[
|
||||
<LinkDashboard
|
||||
newDashboardButton
|
||||
onRefresh={reload}
|
||||
customDashboards={customDashboards}
|
||||
assetType={asset.type}
|
||||
/>,
|
||||
<GotoDashboardLink currentDashboard={currentDashboard} />,
|
||||
<EditDashboard
|
||||
currentDashboard={currentDashboard}
|
||||
onRefresh={reload}
|
||||
assetType={asset.type}
|
||||
/>,
|
||||
<UnlinkDashboard
|
||||
currentDashboard={currentDashboard}
|
||||
onRefresh={reload}
|
||||
assetType={asset.type}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
{currentDashboard && (
|
||||
<>
|
||||
<EuiSpacer size="s" />
|
||||
<FilterExplanationCallout
|
||||
dashboardFilterAssetIdEnabled={currentDashboard.dashboardFilterAssetIdEnabled}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<EuiFlexItem grow>
|
||||
<EuiSpacer size="l" />
|
||||
{urlState?.dashboardId && (
|
||||
<DashboardRenderer
|
||||
savedObjectId={urlState?.dashboardId}
|
||||
getCreationOptions={getCreationOptions}
|
||||
onApiAvailable={setDashboard}
|
||||
locator={locator}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
) : (
|
||||
<EmptyDashboards
|
||||
actions={
|
||||
<LinkDashboard
|
||||
onRefresh={reload}
|
||||
customDashboards={customDashboards}
|
||||
assetType={asset.type}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { EuiEmptyPrompt, EuiImage } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dashboardsDark, dashboardsLight } from '@kbn/shared-svg';
|
||||
import { useIsDarkMode } from '../../../../hooks/use_is_dark_mode';
|
||||
|
||||
interface Props {
|
||||
actions: React.ReactNode;
|
||||
}
|
||||
|
||||
export function EmptyDashboards({ actions }: Props) {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
hasShadow={false}
|
||||
hasBorder={false}
|
||||
icon={
|
||||
<EuiImage size="fullWidth" src={isDarkMode ? dashboardsDark : dashboardsLight} alt="" />
|
||||
}
|
||||
title={
|
||||
<h2>
|
||||
{i18n.translate('xpack.infra.assetDetails.dashboards.emptyTitle', {
|
||||
defaultMessage: 'Want your own view?',
|
||||
})}
|
||||
</h2>
|
||||
}
|
||||
layout="horizontal"
|
||||
color="plain"
|
||||
body={
|
||||
<>
|
||||
<ul>
|
||||
<li>
|
||||
{i18n.translate('xpack.infra.assetDetails.dashboards.emptyBody.first', {
|
||||
defaultMessage: 'Link your own dashboard to this view',
|
||||
})}
|
||||
</li>
|
||||
<li>
|
||||
{i18n.translate('xpack.infra.assetDetails.dashboards.emptyBody.second', {
|
||||
defaultMessage: 'Provide the best visualizations relevant to your business',
|
||||
})}
|
||||
</li>
|
||||
<li>
|
||||
{i18n.translate('xpack.infra.assetDetails.dashboards.emptyBody', {
|
||||
defaultMessage: 'Add or remove them at any time',
|
||||
})}
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
{i18n.translate('xpack.infra.assetDetails.dashboards.emptyBody.getStarted', {
|
||||
defaultMessage: 'To get started, add your dashboard',
|
||||
})}
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
actions={actions}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiIcon, EuiCode } from '@elastic/eui';
|
||||
|
||||
import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common';
|
||||
import { css } from '@emotion/react';
|
||||
import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props';
|
||||
|
||||
interface Props {
|
||||
dashboardFilterAssetIdEnabled: boolean;
|
||||
}
|
||||
export const FilterExplanationCallout = ({ dashboardFilterAssetIdEnabled }: Props) => {
|
||||
const { asset } = useAssetDetailsRenderPropsContext();
|
||||
|
||||
return (
|
||||
<EuiCallOut
|
||||
size="s"
|
||||
title={
|
||||
<EuiFlexGroup gutterSize="xs" alignItems="center" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip
|
||||
content={
|
||||
dashboardFilterAssetIdEnabled ? (
|
||||
<>
|
||||
{i18n.translate(
|
||||
'xpack.infra.customDashboards.filteredByCurrentAssetExplanation.tooltip',
|
||||
{
|
||||
defaultMessage: 'Filtered by',
|
||||
}
|
||||
)}
|
||||
<EuiCode
|
||||
transparentBackground
|
||||
css={css`
|
||||
color: inherit;
|
||||
`}
|
||||
>{`${findInventoryFields(asset.type).id}: ${asset.id}`}</EuiCode>
|
||||
</>
|
||||
) : (
|
||||
i18n.translate('xpack.infra.customDashboards.notFilteredExplanation.tooltip', {
|
||||
defaultMessage:
|
||||
'You can change this dashboard to filter by the {assetType} by editing the link for it',
|
||||
values: { assetType: asset.type },
|
||||
})
|
||||
)
|
||||
}
|
||||
>
|
||||
<EuiIcon color="primary" size="m" type="iInCircle" />
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
{dashboardFilterAssetIdEnabled
|
||||
? i18n.translate(
|
||||
'xpack.infra.customDashboards.filteredByCurrentAssetExplanation.message',
|
||||
{
|
||||
defaultMessage: 'This dashboard is filtered by the current {assetType}',
|
||||
values: { assetType: asset.type },
|
||||
}
|
||||
)
|
||||
: i18n.translate('xpack.infra.customDashboards.notFilteredExplanation.message', {
|
||||
defaultMessage:
|
||||
'This dashboard is not filtered by the {assetType} you are viewing',
|
||||
values: { assetType: asset.type },
|
||||
})}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -13,4 +13,3 @@ export { Osquery } from './osquery/osquery';
|
|||
export { Logs } from './logs/logs';
|
||||
export { Overview } from './overview/overview';
|
||||
export { Metrics } from './metrics/metrics';
|
||||
export { Dashboards } from './dashboards/dashboards';
|
||||
|
|
|
@ -26,7 +26,6 @@ export enum ContentTabIds {
|
|||
ANOMALIES = 'anomalies',
|
||||
OSQUERY = 'osquery',
|
||||
LOGS = 'logs',
|
||||
DASHBOARDS = 'dashboards',
|
||||
}
|
||||
|
||||
export type TabIds = `${ContentTabIds}`;
|
||||
|
|
|
@ -10,16 +10,12 @@ import { EuiSpacer } from '@elastic/eui';
|
|||
import { EuiForm } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React from 'react';
|
||||
import {
|
||||
enableInfrastructureProfilingIntegration,
|
||||
enableInfrastructureAssetCustomDashboards,
|
||||
} from '@kbn/observability-plugin/common';
|
||||
import { enableInfrastructureProfilingIntegration } from '@kbn/observability-plugin/common';
|
||||
import type { useEditableSettings } from '@kbn/observability-shared-plugin/public';
|
||||
import { withSuspense } from '@kbn/shared-ux-utility';
|
||||
import { FieldRowProvider } from '@kbn/management-settings-components-field-row';
|
||||
import type { ValueValidation } from '@kbn/core-ui-settings-browser/src/types';
|
||||
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
|
||||
import { usePluginConfig } from '../../../containers/plugin_config_context';
|
||||
|
||||
const LazyFieldRow = React.lazy(async () => ({
|
||||
default: (await import('@kbn/management-settings-components-field-row')).FieldRow,
|
||||
|
@ -30,20 +26,12 @@ const FieldRow = withSuspense(LazyFieldRow);
|
|||
type Props = Pick<
|
||||
ReturnType<typeof useEditableSettings>,
|
||||
'handleFieldChange' | 'fields' | 'unsavedChanges'
|
||||
> & {
|
||||
readOnly: boolean;
|
||||
};
|
||||
>;
|
||||
|
||||
export function FeaturesConfigurationPanel({
|
||||
readOnly,
|
||||
handleFieldChange,
|
||||
fields,
|
||||
unsavedChanges,
|
||||
}: Props) {
|
||||
export function FeaturesConfigurationPanel({ handleFieldChange, fields, unsavedChanges }: Props) {
|
||||
const {
|
||||
services: { docLinks, notifications },
|
||||
} = useKibanaContextForPlugin();
|
||||
const { featureFlags } = usePluginConfig();
|
||||
|
||||
// We don't validate the user input on these settings
|
||||
const settingsValidationResponse: ValueValidation = {
|
||||
|
@ -70,19 +58,11 @@ export function FeaturesConfigurationPanel({
|
|||
}}
|
||||
>
|
||||
<FieldRow
|
||||
field={fields[enableInfrastructureAssetCustomDashboards]}
|
||||
field={fields[enableInfrastructureProfilingIntegration]}
|
||||
isSavingEnabled={true}
|
||||
onFieldChange={handleFieldChange}
|
||||
unsavedChange={unsavedChanges[enableInfrastructureAssetCustomDashboards]}
|
||||
unsavedChange={unsavedChanges[enableInfrastructureProfilingIntegration]}
|
||||
/>
|
||||
{featureFlags.profilingEnabled && (
|
||||
<FieldRow
|
||||
field={fields[enableInfrastructureProfilingIntegration]}
|
||||
isSavingEnabled={true}
|
||||
onFieldChange={handleFieldChange}
|
||||
unsavedChange={unsavedChanges[enableInfrastructureProfilingIntegration]}
|
||||
/>
|
||||
)}
|
||||
</FieldRowProvider>
|
||||
</EuiForm>
|
||||
);
|
||||
|
|
|
@ -13,10 +13,7 @@ import {
|
|||
Prompt,
|
||||
useEditableSettings,
|
||||
} from '@kbn/observability-shared-plugin/public';
|
||||
import {
|
||||
enableInfrastructureProfilingIntegration,
|
||||
enableInfrastructureAssetCustomDashboards,
|
||||
} from '@kbn/observability-plugin/common';
|
||||
import { enableInfrastructureProfilingIntegration } from '@kbn/observability-plugin/common';
|
||||
import { loadRuleAggregations } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import type { HttpSetup } from '@kbn/core-http-browser';
|
||||
import {
|
||||
|
@ -35,6 +32,7 @@ import { useSourceConfigurationFormState } from './source_configuration_form_sta
|
|||
import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs';
|
||||
import { settingsTitle } from '../../../translations';
|
||||
import { FeaturesConfigurationPanel } from './features_configuration_panel';
|
||||
import { usePluginConfig } from '../../../containers/plugin_config_context';
|
||||
interface SourceConfigurationSettingsProps {
|
||||
shouldAllowEdit: boolean;
|
||||
http?: HttpSetup;
|
||||
|
@ -51,6 +49,7 @@ export const SourceConfigurationSettings = ({
|
|||
]);
|
||||
|
||||
const [numberOfInfraRules, setNumberOfInfraRules] = useState(0);
|
||||
const { featureFlags } = usePluginConfig();
|
||||
|
||||
useEffect(() => {
|
||||
const getNumberOfInfraRules = async () => {
|
||||
|
@ -90,10 +89,7 @@ export const SourceConfigurationSettings = ({
|
|||
formStateChanges,
|
||||
getUnsavedChanges,
|
||||
} = useSourceConfigurationFormState(source?.configuration);
|
||||
const infraUiSettings = useEditableSettings([
|
||||
enableInfrastructureProfilingIntegration,
|
||||
enableInfrastructureAssetCustomDashboards,
|
||||
]);
|
||||
const infraUiSettings = useEditableSettings([enableInfrastructureProfilingIntegration]);
|
||||
|
||||
const resetAllUnsavedChanges = useCallback(() => {
|
||||
resetForm();
|
||||
|
@ -181,10 +177,14 @@ export const SourceConfigurationSettings = ({
|
|||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
<EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
|
||||
<FeaturesConfigurationPanel readOnly={!isWriteable} {...infraUiSettings} />
|
||||
</EuiPanel>
|
||||
<EuiSpacer />
|
||||
{featureFlags.profilingEnabled && (
|
||||
<>
|
||||
<EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
|
||||
<FeaturesConfigurationPanel {...infraUiSettings} />
|
||||
</EuiPanel>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
{errors.length > 0 ? (
|
||||
<>
|
||||
<EuiCallOut color="danger">
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
} from '@kbn/es-query';
|
||||
import type { DataView } from '@kbn/data-views-plugin/common';
|
||||
import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common';
|
||||
import type { InfraCustomDashboardAssetType } from '../../../common/custom_dashboards';
|
||||
import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
|
||||
|
||||
export const buildCombinedAssetFilter = ({
|
||||
field,
|
||||
|
@ -57,7 +57,7 @@ export const retrieveFieldsFromFilter = (filters: Filter[], fields: string[] = [
|
|||
|
||||
export const buildAssetIdFilter = (
|
||||
assetId: string,
|
||||
assetType: InfraCustomDashboardAssetType,
|
||||
assetType: InventoryItemType,
|
||||
dataView: DataView
|
||||
): Filter[] => {
|
||||
const assetIdField = dataView.getFieldByName(findInventoryFields(assetType).id);
|
||||
|
|
|
@ -32,7 +32,6 @@ import { initInfraAssetRoutes } from './routes/infra';
|
|||
import { initMetricsExplorerViewRoutes } from './routes/metrics_explorer_views';
|
||||
import { initProfilingRoutes } from './routes/profiling';
|
||||
import { initServicesRoute } from './routes/services';
|
||||
import { initCustomDashboardsRoutes } from './routes/custom_dashboards/custom_dashboards';
|
||||
import type { InfraBackendLibs } from './lib/infra_types';
|
||||
import { initEntitiesConfigurationRoutes } from './routes/entities';
|
||||
|
||||
|
@ -63,6 +62,5 @@ export const registerRoutes = (libs: InfraBackendLibs) => {
|
|||
initInfraAssetRoutes(libs);
|
||||
initProfilingRoutes(libs);
|
||||
initServicesRoute(libs);
|
||||
initCustomDashboardsRoutes(libs.framework);
|
||||
initEntitiesConfigurationRoutes(libs);
|
||||
};
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
|
||||
import { initGetCustomDashboardRoute } from './get_custom_dashboard';
|
||||
import { initSaveCustomDashboardRoute } from './save_custom_dashboard';
|
||||
import { initDeleteCustomDashboardRoute } from './delete_custom_dashboard';
|
||||
import { initUpdateCustomDashboardRoute } from './update_custom_dashboard';
|
||||
|
||||
export function initCustomDashboardsRoutes(framework: KibanaFramework) {
|
||||
initGetCustomDashboardRoute(framework);
|
||||
initSaveCustomDashboardRoute(framework);
|
||||
initDeleteCustomDashboardRoute(framework);
|
||||
initUpdateCustomDashboardRoute(framework);
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createRouteValidationFunction } from '@kbn/io-ts-utils';
|
||||
import { InfraDeleteCustomDashboardsRequestParamsRT } from '../../../common/http_api/custom_dashboards_api';
|
||||
import type { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
|
||||
import { handleRouteErrors } from '../../utils/handle_route_errors';
|
||||
import { checkCustomDashboardsEnabled } from './lib/check_custom_dashboards_enabled';
|
||||
import { deleteCustomDashboard } from './lib/delete_custom_dashboard';
|
||||
|
||||
export function initDeleteCustomDashboardRoute(framework: KibanaFramework) {
|
||||
const validateParams = createRouteValidationFunction(InfraDeleteCustomDashboardsRequestParamsRT);
|
||||
|
||||
framework.registerRoute(
|
||||
{
|
||||
method: 'delete',
|
||||
path: '/api/infra/{assetType}/custom-dashboards/{id}',
|
||||
validate: {
|
||||
params: validateParams,
|
||||
},
|
||||
options: {
|
||||
access: 'internal',
|
||||
},
|
||||
},
|
||||
handleRouteErrors(async (context, request, response) => {
|
||||
const { savedObjectsClient, uiSettingsClient } = await context.infra;
|
||||
|
||||
await checkCustomDashboardsEnabled(uiSettingsClient);
|
||||
|
||||
const { id } = request.params;
|
||||
|
||||
await deleteCustomDashboard({
|
||||
savedObjectsClient,
|
||||
savedObjectId: id,
|
||||
});
|
||||
|
||||
return response.ok({
|
||||
body: id,
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createRouteValidationFunction } from '@kbn/io-ts-utils';
|
||||
import {
|
||||
InfraGetCustomDashboardsRequestPathParamsRT,
|
||||
InfraGetCustomDashboardsResponseBodyRT,
|
||||
} from '../../../common/http_api/custom_dashboards_api';
|
||||
import type { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
|
||||
import { handleRouteErrors } from '../../utils/handle_route_errors';
|
||||
import { checkCustomDashboardsEnabled } from './lib/check_custom_dashboards_enabled';
|
||||
import { findCustomDashboard } from './lib/find_custom_dashboard';
|
||||
|
||||
export function initGetCustomDashboardRoute(framework: KibanaFramework) {
|
||||
const validateParams = createRouteValidationFunction(InfraGetCustomDashboardsRequestPathParamsRT);
|
||||
|
||||
framework.registerRoute(
|
||||
{
|
||||
method: 'get',
|
||||
path: '/api/infra/{assetType}/custom-dashboards',
|
||||
validate: {
|
||||
params: validateParams,
|
||||
},
|
||||
options: {
|
||||
access: 'internal',
|
||||
},
|
||||
},
|
||||
handleRouteErrors(async (context, request, response) => {
|
||||
const { savedObjectsClient, uiSettingsClient } = await context.infra;
|
||||
|
||||
await checkCustomDashboardsEnabled(uiSettingsClient);
|
||||
|
||||
const params = request.params;
|
||||
const customDashboards = await findCustomDashboard(params.assetType, savedObjectsClient);
|
||||
|
||||
return response.ok({
|
||||
body: InfraGetCustomDashboardsResponseBodyRT.encode(customDashboards),
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import Boom from '@hapi/boom';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { IUiSettingsClient } from '@kbn/core/server';
|
||||
import { enableInfrastructureAssetCustomDashboards } from '@kbn/observability-plugin/common';
|
||||
|
||||
export async function checkCustomDashboardsEnabled(uiSettingsClient: IUiSettingsClient) {
|
||||
const isEnabled = await uiSettingsClient.get(enableInfrastructureAssetCustomDashboards);
|
||||
|
||||
if (!isEnabled) {
|
||||
throw Boom.forbidden(
|
||||
i18n.translate('xpack.infra.routes.customDashboards', {
|
||||
defaultMessage: 'Custom dashboards are not enabled',
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { type SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
|
||||
import { INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../../saved_objects';
|
||||
|
||||
interface Options {
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
savedObjectId: string;
|
||||
}
|
||||
export function deleteCustomDashboard({ savedObjectsClient, savedObjectId }: Options) {
|
||||
return savedObjectsClient.delete(INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, savedObjectId);
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { type SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
|
||||
import { INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../../saved_objects';
|
||||
import type {
|
||||
InfraCustomDashboard,
|
||||
InfraCustomDashboardAssetType,
|
||||
} from '../../../../common/custom_dashboards';
|
||||
|
||||
export async function findCustomDashboard(
|
||||
assetType: InfraCustomDashboardAssetType,
|
||||
savedObjectsClient: SavedObjectsClientContract
|
||||
) {
|
||||
const result = await savedObjectsClient.find<InfraCustomDashboard>({
|
||||
type: INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
search: assetType,
|
||||
searchFields: ['assetType'] as [keyof InfraCustomDashboard],
|
||||
page: 1,
|
||||
perPage: 1000,
|
||||
sortField: 'updated_at',
|
||||
sortOrder: 'desc',
|
||||
});
|
||||
|
||||
return result.saved_objects.map(({ id, attributes }) => ({
|
||||
id,
|
||||
...attributes,
|
||||
}));
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createRouteValidationFunction } from '@kbn/io-ts-utils';
|
||||
import type { InfraCustomDashboard } from '../../../common/custom_dashboards';
|
||||
import {
|
||||
InfraSaveCustomDashboardsRequestPayloadRT,
|
||||
InfraSaveCustomDashboardsResponseBodyRT,
|
||||
InfraGetCustomDashboardsRequestPathParamsRT,
|
||||
} from '../../../common/http_api/custom_dashboards_api';
|
||||
import type { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
|
||||
import { INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../saved_objects';
|
||||
import { checkCustomDashboardsEnabled } from './lib/check_custom_dashboards_enabled';
|
||||
import { handleRouteErrors } from '../../utils/handle_route_errors';
|
||||
import { findCustomDashboard } from './lib/find_custom_dashboard';
|
||||
|
||||
export function initSaveCustomDashboardRoute(framework: KibanaFramework) {
|
||||
const validatePayload = createRouteValidationFunction(InfraSaveCustomDashboardsRequestPayloadRT);
|
||||
const validateParams = createRouteValidationFunction(InfraGetCustomDashboardsRequestPathParamsRT);
|
||||
|
||||
framework.registerRoute(
|
||||
{
|
||||
method: 'post',
|
||||
path: '/api/infra/{assetType}/custom-dashboards',
|
||||
validate: {
|
||||
body: validatePayload,
|
||||
params: validateParams,
|
||||
},
|
||||
options: {
|
||||
access: 'internal',
|
||||
},
|
||||
},
|
||||
handleRouteErrors(async (context, request, response) => {
|
||||
const { savedObjectsClient, uiSettingsClient } = await context.infra;
|
||||
|
||||
await checkCustomDashboardsEnabled(uiSettingsClient);
|
||||
|
||||
const { dashboardSavedObjectId } = request.body;
|
||||
|
||||
const { assetType } = request.params;
|
||||
|
||||
const customDashboards = await findCustomDashboard(assetType, savedObjectsClient);
|
||||
|
||||
const dashboardExist = customDashboards.find(
|
||||
(customDashboard) => customDashboard.dashboardSavedObjectId === dashboardSavedObjectId
|
||||
);
|
||||
|
||||
if (dashboardExist) {
|
||||
return response.badRequest({
|
||||
body: `Dashboard with id ${dashboardSavedObjectId} has already been linked to ${assetType}`,
|
||||
});
|
||||
}
|
||||
|
||||
const savedCustomDashboard = await savedObjectsClient.create<InfraCustomDashboard>(
|
||||
INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
{
|
||||
assetType,
|
||||
...request.body,
|
||||
}
|
||||
);
|
||||
|
||||
return response.ok({
|
||||
body: InfraSaveCustomDashboardsResponseBodyRT.encode({
|
||||
id: savedCustomDashboard.id,
|
||||
...savedCustomDashboard.attributes,
|
||||
}),
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createRouteValidationFunction } from '@kbn/io-ts-utils';
|
||||
import type { InfraCustomDashboard } from '../../../common/custom_dashboards';
|
||||
import {
|
||||
InfraSaveCustomDashboardsRequestPayloadRT,
|
||||
InfraSaveCustomDashboardsResponseBodyRT,
|
||||
InfraUpdateCustomDashboardsRequestPathParamsRT,
|
||||
} from '../../../common/http_api/custom_dashboards_api';
|
||||
import type { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
|
||||
import { INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../saved_objects';
|
||||
import { checkCustomDashboardsEnabled } from './lib/check_custom_dashboards_enabled';
|
||||
import { handleRouteErrors } from '../../utils/handle_route_errors';
|
||||
|
||||
export function initUpdateCustomDashboardRoute(framework: KibanaFramework) {
|
||||
const validatePayload = createRouteValidationFunction(InfraSaveCustomDashboardsRequestPayloadRT);
|
||||
const validateParams = createRouteValidationFunction(
|
||||
InfraUpdateCustomDashboardsRequestPathParamsRT
|
||||
);
|
||||
|
||||
framework.registerRoute(
|
||||
{
|
||||
method: 'put',
|
||||
path: '/api/infra/{assetType}/custom-dashboards/{id}',
|
||||
validate: {
|
||||
body: validatePayload,
|
||||
params: validateParams,
|
||||
},
|
||||
options: {
|
||||
access: 'internal',
|
||||
},
|
||||
},
|
||||
handleRouteErrors(async (context, request, response) => {
|
||||
const { savedObjectsClient, uiSettingsClient } = await context.infra;
|
||||
|
||||
await checkCustomDashboardsEnabled(uiSettingsClient);
|
||||
|
||||
const { id, assetType } = request.params;
|
||||
|
||||
const savedCustomDashboard = await savedObjectsClient.update<InfraCustomDashboard>(
|
||||
INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
id,
|
||||
{
|
||||
assetType,
|
||||
...request.body,
|
||||
}
|
||||
);
|
||||
|
||||
return response.ok({
|
||||
body: InfraSaveCustomDashboardsResponseBodyRT.encode({
|
||||
id: savedCustomDashboard.id,
|
||||
assetType,
|
||||
...request.body,
|
||||
...savedCustomDashboard.attributes,
|
||||
}),
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
|
@ -8,10 +8,17 @@
|
|||
import type { SavedObjectsFieldMapping, SavedObjectsType } from '@kbn/core/server';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import type { InfraCustomDashboard } from '../../../common/custom_dashboards';
|
||||
import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
|
||||
|
||||
// This feature is deprecated and will be removed in https://github.com/elastic/kibana/issues/220340
|
||||
export const INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE = 'infra-custom-dashboards';
|
||||
|
||||
interface InfraCustomDashboard {
|
||||
dashboardSavedObjectId: string;
|
||||
assetType: InventoryItemType;
|
||||
dashboardFilterAssetIdEnabled: boolean;
|
||||
}
|
||||
|
||||
const properties: Record<keyof InfraCustomDashboard, SavedObjectsFieldMapping> = {
|
||||
assetType: {
|
||||
type: 'keyword',
|
||||
|
|
|
@ -86,12 +86,10 @@
|
|||
"@kbn/shared-ux-utility",
|
||||
"@kbn/management-settings-components-field-row",
|
||||
"@kbn/core-ui-settings-browser",
|
||||
"@kbn/core-saved-objects-api-server",
|
||||
"@kbn/securitysolution-io-ts-utils",
|
||||
"@kbn/serverless",
|
||||
"@kbn/elastic-agent-utils",
|
||||
"@kbn/dashboard-plugin",
|
||||
"@kbn/shared-svg",
|
||||
"@kbn/aiops-log-rate-analysis",
|
||||
"@kbn/search-types",
|
||||
"@kbn/alerting-comparators",
|
||||
|
@ -120,7 +118,6 @@
|
|||
"@kbn/response-ops-rule-form",
|
||||
"@kbn/traced-es-client",
|
||||
"@kbn/fields-metadata-plugin",
|
||||
"@kbn/deeplinks-analytics",
|
||||
"@kbn/core-chrome-browser",
|
||||
"@kbn/presentation-containers",
|
||||
"@kbn/object-utils"
|
||||
|
|
|
@ -29,7 +29,6 @@ export {
|
|||
apmServiceGroupMaxNumberOfServices,
|
||||
apmLabsButton,
|
||||
enableInfrastructureProfilingIntegration,
|
||||
enableInfrastructureAssetCustomDashboards,
|
||||
apmEnableTableSearchBar,
|
||||
entityCentricExperience,
|
||||
apmAWSLambdaPriceFactor,
|
||||
|
|
|
@ -15,8 +15,6 @@ export const apmServiceGroupMaxNumberOfServices =
|
|||
export const apmLabsButton = 'observability:apmLabsButton';
|
||||
export const enableInfrastructureProfilingIntegration =
|
||||
'observability:enableInfrastructureProfilingIntegration';
|
||||
export const enableInfrastructureAssetCustomDashboards =
|
||||
'observability:enableInfrastructureAssetCustomDashboards';
|
||||
export const apmEnableTableSearchBar = 'observability:apmEnableTableSearchBar';
|
||||
export const entityCentricExperience = 'observability:entityCentricExperience';
|
||||
export const apmEnableServiceInventoryTableSearchBar =
|
||||
|
|
|
@ -34,7 +34,6 @@ import {
|
|||
profilingAzureCostDiscountRate,
|
||||
enableInfrastructureProfilingIntegration,
|
||||
apmEnableTransactionProfiling,
|
||||
enableInfrastructureAssetCustomDashboards,
|
||||
apmEnableServiceInventoryTableSearchBar,
|
||||
searchExcludedDataTiers,
|
||||
apmEnableServiceMapApiV2,
|
||||
|
@ -203,25 +202,6 @@ export const uiSettings: Record<string, UiSettings> = {
|
|||
schema: schema.boolean(),
|
||||
solution: 'oblt',
|
||||
},
|
||||
[enableInfrastructureAssetCustomDashboards]: {
|
||||
category: [observabilityFeatureId],
|
||||
name: i18n.translate('xpack.observability.enableInfrastructureAssetCustomDashboards', {
|
||||
defaultMessage: 'Custom dashboards for asset details in Infrastructure',
|
||||
}),
|
||||
value: false,
|
||||
description: i18n.translate(
|
||||
'xpack.observability.enableInfrastructureAssetCustomDashboardsDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'{technicalPreviewLabel} Enable option to link custom dashboards in the asset details view.',
|
||||
values: {
|
||||
technicalPreviewLabel: `<em>[${technicalPreviewLabel}]</em>`,
|
||||
},
|
||||
}
|
||||
),
|
||||
schema: schema.boolean(),
|
||||
solution: 'oblt',
|
||||
},
|
||||
[apmEnableTableSearchBar]: {
|
||||
category: [observabilityFeatureId],
|
||||
name: i18n.translate('xpack.observability.apmEnableTableSearchBar', {
|
||||
|
|
|
@ -20,7 +20,6 @@ export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext)
|
|||
loadTestFile(require.resolve('./infra'));
|
||||
loadTestFile(require.resolve('./inventory_threshold_alert'));
|
||||
loadTestFile(require.resolve('./services'));
|
||||
loadTestFile(require.resolve('./infra_custom_dashboards'));
|
||||
loadTestFile(require.resolve('./infra_asset_count'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,326 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { InfraCustomDashboard } from '@kbn/infra-plugin/common/custom_dashboards';
|
||||
import { InfraSaveCustomDashboardsRequestPayload } from '@kbn/infra-plugin/common/http_api/custom_dashboards_api';
|
||||
import { INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '@kbn/infra-plugin/server/saved_objects';
|
||||
import { enableInfrastructureAssetCustomDashboards } from '@kbn/observability-plugin/common';
|
||||
import type { SupertestWithRoleScopeType } from '../../../services';
|
||||
import type { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
const getCustomDashboardsUrl = (assetType: string, dashboardSavedObjectId?: string) =>
|
||||
dashboardSavedObjectId
|
||||
? `/api/infra/${assetType}/custom-dashboards/${dashboardSavedObjectId}`
|
||||
: `/api/infra/${assetType}/custom-dashboards`;
|
||||
|
||||
export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
|
||||
const roleScopedSupertest = getService('roleScopedSupertest');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
describe('Infra Custom Dashboards API', () => {
|
||||
let supertestWithAdminScope: SupertestWithRoleScopeType;
|
||||
|
||||
before(async () => {
|
||||
supertestWithAdminScope = await roleScopedSupertest.getSupertestWithRoleScope('admin', {
|
||||
withInternalHeaders: true,
|
||||
useCookieHeader: true,
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await supertestWithAdminScope.destroy();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await kibanaServer.savedObjects.clean({
|
||||
types: [INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE],
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET endpoint for fetching custom dashboard', () => {
|
||||
it('responds with an error if Custom Dashboards UI setting is not enabled', async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: false,
|
||||
});
|
||||
|
||||
await supertestWithAdminScope.get(getCustomDashboardsUrl('host')).expect(403);
|
||||
});
|
||||
|
||||
it('responds with an error when trying to request a custom dashboard for unsupported asset type', async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: false,
|
||||
});
|
||||
|
||||
await supertestWithAdminScope
|
||||
.get(getCustomDashboardsUrl('unsupported-asset-type'))
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('responds with an empty configuration if custom dashboard saved object does not exist', async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
await kibanaServer.savedObjects.clean({
|
||||
types: [INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE],
|
||||
});
|
||||
|
||||
const response = await supertestWithAdminScope
|
||||
.get(getCustomDashboardsUrl('host'))
|
||||
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.be.eql([]);
|
||||
});
|
||||
|
||||
it('responds with the custom dashboard configuration for a given asset type when it exists', async () => {
|
||||
const customDashboard: InfraCustomDashboard = {
|
||||
assetType: 'host',
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
await kibanaServer.savedObjects.create({
|
||||
type: INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
attributes: customDashboard,
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
const response = await supertestWithAdminScope
|
||||
.get(getCustomDashboardsUrl('host'))
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.have.length(1);
|
||||
expect(response.body[0]).to.have.property('dashboardFilterAssetIdEnabled', true);
|
||||
expect(response.body[0]).to.have.property('assetType', 'host');
|
||||
expect(response.body[0]).to.have.property('dashboardSavedObjectId', '123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST endpoint for saving custom dashboard', () => {
|
||||
it('responds with an error if Custom Dashboards UI setting is not enabled', async () => {
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: false,
|
||||
});
|
||||
|
||||
await supertestWithAdminScope
|
||||
.post(getCustomDashboardsUrl('host'))
|
||||
.send(payload)
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it('responds with an error when trying to update a custom dashboard for unsupported asset type', async () => {
|
||||
const payload = {
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
|
||||
await supertestWithAdminScope
|
||||
.post(getCustomDashboardsUrl('unsupported-asset-type'))
|
||||
.send(payload)
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('creates a new dashboard configuration when saving for the first time', async () => {
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
await kibanaServer.savedObjects.clean({
|
||||
types: [INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE],
|
||||
});
|
||||
|
||||
const response = await supertestWithAdminScope
|
||||
.post(getCustomDashboardsUrl('host'))
|
||||
.send(payload)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.have.property('id');
|
||||
expect(response.body).to.have.property('dashboardFilterAssetIdEnabled', true);
|
||||
expect(response.body).to.have.property('assetType', 'host');
|
||||
expect(response.body).to.have.property('dashboardSavedObjectId', '123');
|
||||
});
|
||||
|
||||
it('returns 400 when the dashboard already exist and tries to create it again', async () => {
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
await kibanaServer.savedObjects.clean({
|
||||
types: [INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE],
|
||||
});
|
||||
await kibanaServer.savedObjects.create({
|
||||
type: INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
attributes: {
|
||||
assetType: 'host',
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
},
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
const response = await supertestWithAdminScope
|
||||
.post(getCustomDashboardsUrl('host'))
|
||||
.send(payload)
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.error).to.be.eql('Bad Request');
|
||||
expect(response.body.message).to.be.eql(
|
||||
'Dashboard with id 123 has already been linked to host'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT endpoint for updating custom dashboard', () => {
|
||||
it('responds with an error if Custom Dashboards UI setting is not enabled', async () => {
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: false,
|
||||
});
|
||||
|
||||
await supertestWithAdminScope
|
||||
.put(getCustomDashboardsUrl('host', '123'))
|
||||
.send(payload)
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it('responds with an error when trying to update not existing dashboard', async () => {
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
|
||||
await supertestWithAdminScope
|
||||
.put(getCustomDashboardsUrl('host', '000'))
|
||||
.send(payload)
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
it('updates existing dashboard configuration for a given asset type', async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
await kibanaServer.savedObjects.clean({
|
||||
types: [INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE],
|
||||
});
|
||||
await kibanaServer.savedObjects.create({
|
||||
type: INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
attributes: {
|
||||
assetType: 'host',
|
||||
dashboardSavedObjectId: '456',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
},
|
||||
overwrite: true,
|
||||
});
|
||||
const existingDashboardSavedObject = await kibanaServer.savedObjects.create({
|
||||
type: INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
attributes: {
|
||||
assetType: 'host',
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
},
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: false,
|
||||
};
|
||||
const updateResponse = await supertestWithAdminScope
|
||||
.put(getCustomDashboardsUrl('host', existingDashboardSavedObject.id))
|
||||
.send(payload)
|
||||
.expect(200);
|
||||
const getResponse = await supertestWithAdminScope
|
||||
.get(getCustomDashboardsUrl('host'))
|
||||
.expect(200);
|
||||
|
||||
expect(updateResponse.body).to.be.eql({
|
||||
...payload,
|
||||
assetType: 'host',
|
||||
id: updateResponse.body.id,
|
||||
});
|
||||
|
||||
expect(getResponse.body).to.have.length(2);
|
||||
expect(getResponse.body[0]).to.have.property('dashboardSavedObjectId', '123');
|
||||
expect(getResponse.body[0]).to.have.property('dashboardFilterAssetIdEnabled', false);
|
||||
expect(getResponse.body[0]).to.have.property('assetType', 'host');
|
||||
expect(getResponse.body[1]).to.have.property('dashboardSavedObjectId', '456');
|
||||
expect(getResponse.body[1]).to.have.property('dashboardFilterAssetIdEnabled', true);
|
||||
expect(getResponse.body[1]).to.have.property('assetType', 'host');
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE endpoint for removing a custom dashboard', () => {
|
||||
it('responds with an error if Custom Dashboards UI setting is not enabled', async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: false,
|
||||
});
|
||||
|
||||
await supertestWithAdminScope.delete(getCustomDashboardsUrl('host', '123')).expect(403);
|
||||
});
|
||||
|
||||
it('responds with an error when trying to delete not existing dashboard', async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
|
||||
await supertestWithAdminScope.delete(getCustomDashboardsUrl('host', '000')).expect(404);
|
||||
});
|
||||
|
||||
it('deletes an existing dashboard', async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
await kibanaServer.savedObjects.clean({
|
||||
types: [INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE],
|
||||
});
|
||||
const existingDashboardSavedObject = await kibanaServer.savedObjects.create({
|
||||
type: INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
attributes: {
|
||||
assetType: 'host',
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
},
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
await supertestWithAdminScope
|
||||
.delete(getCustomDashboardsUrl('host', existingDashboardSavedObject.id))
|
||||
.expect(200);
|
||||
|
||||
const afterDeleteResponse = await supertestWithAdminScope
|
||||
.get(getCustomDashboardsUrl('host'))
|
||||
.expect(200);
|
||||
|
||||
expect(afterDeleteResponse.body).to.be.eql([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -12,7 +12,6 @@ import {
|
|||
InfraSynthtraceEsClient,
|
||||
LogsSynthtraceEsClient,
|
||||
} from '@kbn/apm-synthtrace';
|
||||
import { enableInfrastructureAssetCustomDashboards } from '@kbn/observability-plugin/common';
|
||||
import { ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED } from '@kbn/rule-data-utils';
|
||||
import { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
@ -201,11 +200,9 @@ const SYNTH_HOSTS = [
|
|||
|
||||
export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||
const browser = getService('browser');
|
||||
const security = getService('security');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const esClient = getService('es');
|
||||
const find = getService('find');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const observability = getService('observability');
|
||||
const retry = getService('retry');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
@ -223,56 +220,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
]);
|
||||
|
||||
// Helpers
|
||||
|
||||
const loginWithReadOnlyUser = async () => {
|
||||
await security.role.create('global_hosts_read_privileges_role', {
|
||||
elasticsearch: {
|
||||
indices: [
|
||||
{ names: ['metrics-*'], privileges: ['read', 'view_index_metadata'] },
|
||||
{ names: ['metricbeat-*'], privileges: ['read', 'view_index_metadata'] },
|
||||
],
|
||||
},
|
||||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
infrastructure: ['read'],
|
||||
apm: ['read'],
|
||||
advancedSettings: ['read'],
|
||||
streams: ['read'],
|
||||
},
|
||||
spaces: ['*'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await security.user.create('global_hosts_read_privileges_user', {
|
||||
password: 'global_hosts_read_privileges_user-password',
|
||||
roles: ['global_hosts_read_privileges_role'],
|
||||
full_name: 'test user',
|
||||
});
|
||||
|
||||
await pageObjects.security.forceLogout();
|
||||
|
||||
await pageObjects.security.login(
|
||||
'global_hosts_read_privileges_user',
|
||||
'global_hosts_read_privileges_user-password',
|
||||
{
|
||||
expectSpaceSelector: false,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const logoutAndDeleteReadOnlyUser = async () => {
|
||||
await pageObjects.security.forceLogout();
|
||||
await Promise.all([
|
||||
security.role.delete('global_hosts_read_privileges_role'),
|
||||
security.user.delete('global_hosts_read_privileges_user'),
|
||||
]);
|
||||
};
|
||||
|
||||
const setCustomDashboardsEnabled = (value: boolean = true) =>
|
||||
kibanaServer.uiSettings.update({ [enableInfrastructureAssetCustomDashboards]: value });
|
||||
|
||||
const returnTo = async (path: string, timeout = 2000) =>
|
||||
retry.waitForWithTimeout('returned to hosts view', timeout, async () => {
|
||||
await browser.goBack();
|
||||
|
@ -374,7 +321,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
|
||||
describe('#Single Host Flyout', () => {
|
||||
before(async () => {
|
||||
await setCustomDashboardsEnabled(true);
|
||||
await pageObjects.common.navigateToApp(HOSTS_VIEW_PATH);
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
});
|
||||
|
@ -521,16 +467,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Dashboards Tab', () => {
|
||||
before(async () => {
|
||||
await pageObjects.assetDetails.clickDashboardsTab();
|
||||
});
|
||||
|
||||
it('should render dashboards tab splash screen with option to add dashboard', async () => {
|
||||
await pageObjects.assetDetails.addDashboardExists();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Flyout links', () => {
|
||||
it('should navigate to Host Details page after click', async () => {
|
||||
await pageObjects.assetDetails.clickOpenAsPageLink();
|
||||
|
@ -986,47 +922,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
});
|
||||
describe('#Permissions: Read Only User - Single Host Flyout', () => {
|
||||
describe('Dashboards Tab', () => {
|
||||
before(async () => {
|
||||
await setCustomDashboardsEnabled(true);
|
||||
await loginWithReadOnlyUser();
|
||||
// Setting the params directly will help us reduce flakiness (and clicking the wrong tab)
|
||||
// as we already test the flow (in #Single Host Flyout) here we can directly navigate to test the permissions
|
||||
const dashboardsTabSearchParams = `?_a=(dateRange:(from:%27${DATE_WITH_HOSTS_DATA_FROM}%27,to:%27${DATE_WITH_HOSTS_DATA_TO}%27),filters:!(),limit:100,panelFilters:!(),query:(language:kuery,query:%27%27))&tableProperties=(detailsItemId:host-1-Linux,pagination:(pageIndex:0,pageSize:10),sorting:(direction:desc,field:alertsCount))&assetDetails=(dateRange:(from:%27${DATE_WITH_HOSTS_DATA_FROM}%27,to:%27${DATE_WITH_HOSTS_DATA_TO}%27),name:host-1,tabId:dashboards)`;
|
||||
|
||||
await pageObjects.common.navigateToApp(HOSTS_VIEW_PATH, {
|
||||
search: dashboardsTabSearchParams,
|
||||
});
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await pageObjects.infraHome.clickCloseFlyoutButton();
|
||||
});
|
||||
await logoutAndDeleteReadOnlyUser();
|
||||
});
|
||||
|
||||
it('should render dashboards tab splash screen with disabled option to add dashboard', async () => {
|
||||
await pageObjects.assetDetails.dashboardsTabExistsOrFail();
|
||||
await pageObjects.assetDetails.clickDashboardsTab();
|
||||
await pageObjects.assetDetails.addDashboardExists();
|
||||
const elementToHover = await pageObjects.assetDetails.getAddDashboardButton();
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await elementToHover.moveMouseTo();
|
||||
await testSubjects.existOrFail('infraCannotAddDashboardTooltip');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not render dashboards tab if the feature is disabled', async () => {
|
||||
await setCustomDashboardsEnabled(false);
|
||||
await pageObjects.assetDetails.clickOverviewTab();
|
||||
await browser.refresh();
|
||||
await !pageObjects.assetDetails.dashboardsTabExists();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue