-
Notifications
You must be signed in to change notification settings - Fork 8.6k
Linked dashboards in Rule and Alert pages #219019
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ba16762
a25a162
5ac42b5
6e60602
53de12b
e96fdc1
d326c5f
0b8c050
d2bba7f
02d173e
5e18c4f
01058f3
ff35d41
e505ba5
62088ee
9d5aaa3
7f8e493
651d889
3da6049
0931209
beacd8b
cf6cbd7
15b411a
b142609
b7e5dd4
5c9b93a
06bfa36
8648567
64ac2e9
af5c673
f4e9e04
3504780
21e9b85
fb12054
c636a86
cd338aa
9f76798
400ae43
4fec3c8
e490cc3
011b0f9
6f2babc
64cb433
35a9b9c
aa90535
89ad5b8
7a16036
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,3 +8,4 @@ | |
| */ | ||
|
|
||
| export * from './types'; | ||
| export * from './services/dashboard_service'; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| /* | ||
| * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
| * or more contributor license agreements. Licensed under the "Elastic License | ||
| * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
| * Public License v 1"; you may not use this file except in compliance with, at | ||
| * your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
| * License v3.0 only", or the "Server Side Public License, v 1". | ||
| */ | ||
|
|
||
| import { dashboardServiceProvider } from './dashboard_service'; | ||
| import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; | ||
|
|
||
| describe('DashboardService', () => { | ||
| const contentManagement = contentManagementMock.createStartContract(); | ||
| const dashboardService = dashboardServiceProvider(contentManagement); | ||
|
|
||
| test('should fetch dashboards', async () => { | ||
| // arrange | ||
| const searchMock = jest.spyOn(contentManagement.client, 'search').mockResolvedValue({ | ||
| total: 0, | ||
| hits: [], | ||
| }); | ||
|
|
||
| // act | ||
| const resp = await dashboardService.fetchDashboards({ text: 'test*' }); | ||
|
|
||
| // assert | ||
| expect(searchMock).toHaveBeenCalledWith({ | ||
| contentTypeId: 'dashboard', | ||
| query: { | ||
| text: 'test*', | ||
| }, | ||
| options: { | ||
| fields: ['title', 'description'], | ||
| spaces: ['*'], | ||
| }, | ||
| }); | ||
| expect(resp).toEqual([]); | ||
|
|
||
| searchMock.mockRestore(); | ||
| }); | ||
|
|
||
| test('should fetch dashboard by id', async () => { | ||
| // mock get to resolve with a dashboard | ||
| const getMock = jest.spyOn(contentManagement.client, 'get').mockResolvedValue({ | ||
| item: { | ||
| error: null, | ||
| attributes: { | ||
| title: 'Dashboard 1', | ||
| }, | ||
| references: [], | ||
| }, | ||
| }); | ||
|
|
||
| // act | ||
| const resp = await dashboardService.fetchDashboard('1'); | ||
|
|
||
| // assert | ||
| expect(getMock).toHaveBeenCalledWith({ contentTypeId: 'dashboard', id: '1' }); | ||
| expect(resp).toEqual({ | ||
| status: 'success', | ||
| id: '1', | ||
| attributes: { | ||
| title: 'Dashboard 1', | ||
| }, | ||
| references: [], | ||
| }); | ||
|
|
||
| getMock.mockRestore(); | ||
| }); | ||
|
|
||
| test('should return an error if dashboard id is not found', async () => { | ||
| const getMock = jest.spyOn(contentManagement.client, 'get').mockRejectedValue({ | ||
| message: 'Dashboard not found', | ||
| }); | ||
|
|
||
| const resp = await dashboardService.fetchDashboard('2'); | ||
| expect(getMock).toHaveBeenCalledWith({ contentTypeId: 'dashboard', id: '2' }); | ||
| expect(resp).toEqual({ | ||
| status: 'error', | ||
| id: '2', | ||
| error: 'Dashboard not found', | ||
| }); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| /* | ||
| * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
| * or more contributor license agreements. Licensed under the "Elastic License | ||
| * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
| * Public License v 1"; you may not use this file except in compliance with, at | ||
| * your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
| * License v3.0 only", or the "Server Side Public License, v 1". | ||
| */ | ||
|
|
||
| import type { SearchQuery } from '@kbn/content-management-plugin/common'; | ||
| import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; | ||
| import type { Reference, ContentManagementCrudTypes } from '@kbn/content-management-utils'; | ||
| import type { SavedObjectError } from '@kbn/core/public'; | ||
| import type { GetIn } from '@kbn/content-management-plugin/common'; | ||
|
|
||
| const DASHBOARD_CONTENT_TYPE_ID = 'dashboard'; | ||
| export type DashboardGetIn = GetIn<typeof DASHBOARD_CONTENT_TYPE_ID>; | ||
|
|
||
| export type FindDashboardsByIdResponse = { id: string } & ( | ||
| | { status: 'success'; attributes: any; references: Reference[] } | ||
| | { status: 'error'; error: SavedObjectError } | ||
| ); | ||
|
|
||
| export interface DashboardItem { | ||
| id: string; | ||
| attributes: any; // DashboardAttributes is exported in the Dashboard plugin and this causes a cycle dependency. Get feedback on the best approach here | ||
| } | ||
|
|
||
| export type DashboardService = ReturnType<typeof dashboardServiceProvider>; | ||
| export type DashboardItems = Awaited<ReturnType<DashboardService['fetchDashboards']>>; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL |
||
|
|
||
| export function dashboardServiceProvider(contentManagementService: ContentManagementPublicStart) { | ||
| return { | ||
| /** | ||
| * Fetch dashboards | ||
| * @param query - The query to search for dashboards | ||
| * @returns - The dashboards that match the query | ||
| */ | ||
| async fetchDashboards(query: SearchQuery = {}) { | ||
| const response = await contentManagementService.client.search({ | ||
| contentTypeId: 'dashboard', | ||
| query, | ||
| options: { spaces: ['*'], fields: ['title', 'description'] }, | ||
| }); | ||
|
|
||
| // Assert the type of response to access hits property | ||
| return (response as { hits: DashboardItem[] }).hits; | ||
| }, | ||
| /** | ||
| * Fetch dashboard by id | ||
| * @param id - The id of the dashboard to fetch | ||
| * @returns - The dashboard with the given id | ||
| * @throws - An error if the dashboard does not exist | ||
| */ | ||
| async fetchDashboard(id: string): Promise<FindDashboardsByIdResponse> { | ||
| try { | ||
| const response = await contentManagementService.client.get< | ||
| DashboardGetIn, | ||
| ContentManagementCrudTypes< | ||
| typeof DASHBOARD_CONTENT_TYPE_ID, | ||
| any, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| object, | ||
| object, | ||
| object | ||
| >['GetOut'] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, here's the |
||
| >({ | ||
| contentTypeId: 'dashboard', | ||
| id, | ||
| }); | ||
| if (response.item.error) { | ||
| throw response.item.error; | ||
| } | ||
|
|
||
| return { | ||
| id, | ||
| status: 'success', | ||
| attributes: response.item.attributes, | ||
| references: response.item.references, | ||
| }; | ||
| } catch (error) { | ||
| return { | ||
| status: 'error', | ||
| error: error.body || error.message, | ||
| id, | ||
| }; | ||
| } | ||
| }, | ||
|
|
||
| async fetchDashboardsByIds(ids: string[]) { | ||
| const findPromises = ids.map((id) => this.fetchDashboard(id)); | ||
| const results = await Promise.all(findPromises); | ||
| return results as FindDashboardsByIdResponse[]; | ||
| }, | ||
| /** | ||
| * Fetch only the dashboards that still exist | ||
| * @param ids - The ids of the dashboards to fetch | ||
| * @returns - The dashboards that exist | ||
| */ | ||
| async fetchValidDashboards(ids: string[]) { | ||
| const responses = await this.fetchDashboardsByIds(ids); | ||
| const existingDashboards = responses.filter(({ status }) => status === 'success'); | ||
| return existingDashboards; | ||
| }, | ||
| }; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nickpeihl DashboardAttributes is exported in the Dashboard plugin. I tried to search for any existing package that exports DashboardAttributes and I found
@kbn/content-packs-schemaSo here's what I tried:
In this file
x-pack/platform/packages/shared/kbn-content-packs-schema/src/helpers/index.tsI exportedexport type { DashboardAttributes } from '@kbn/dashboard-plugin/common/content_management/v2';and then I imported like thisimport type { DashboardAttributes } from '@kbn/content-packs-schema';Then I ran
yarn test:type_checkand I was getting quite a few ts errors. I didn't push that code on Github, maybe CI would have autocommited some fix for me?What do you recommend me to do here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefer
unknownoveranyThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nickpeihl Shall I copy the types directly here?