Skip to content

Commit

Permalink
[8.x] [Inventory][ECO] Entities table (#193272) (#193327)
Browse files Browse the repository at this point in the history
# Backport

This will backport the following commits from `main` to `8.x`:
- [[Inventory][ECO] Entities table
(#193272)](#193272)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Cauê
Marcondes","email":"55978943+cauemarcondes@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-09-18T15:06:13Z","message":"[Inventory][ECO]
Entities table (#193272)\n\nReal data:\r\n<img width=\"1237\"
alt=\"Screenshot 2024-09-18 at 14 23
17\"\r\nsrc=\"https://github.com/user-attachments/assets/ecc496aa-1c43-4c3c-9ac8-d6e4e6cb8aad\">\r\n\r\nStorybook:\r\n<img
width=\"1256\" alt=\"Screenshot 2024-09-18 at 14 23
22\"\r\nsrc=\"https://github.com/user-attachments/assets/03d9f940-7b3f-4aea-9221-42b1c07119d1\">\r\n\r\nTooltips:\r\n<img
width=\"1250\" alt=\"Screenshot 2024-09-18 at 13 49
19\"\r\nsrc=\"https://github.com/user-attachments/assets/dc99b4cc-4eba-4815-8892-8e3fe7a041bb\">\r\n\r\n\r\n-
Use ESQL to fetch the top 500 entities sorted by last seen
property.\r\n- Display 20 entities per page.\r\n- Sorting is handles by
the server and saved on the URL\r\n- Current page is saved on the
URL\r\n- Filter entities types `service`, `host` or `container`\r\n-
Filter only entities from the built in definition\r\n- LIMITATION: The
EuiGrid doesn't have an embedded loading state, for\r\nnow, I'm
switching the entire view to display a loading spinner while\r\ndata is
being fetched.\r\n- PLUS: Storybook created with mock
data.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"e3f3c68e8d2e106740db9b4612df867c583eb512","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","ci:project-deploy-observability","v8.16.0"],"title":"[Inventory][ECO]
Entities
table","number":193272,"url":"https://github.com/elastic/kibana/pull/193272","mergeCommit":{"message":"[Inventory][ECO]
Entities table (#193272)\n\nReal data:\r\n<img width=\"1237\"
alt=\"Screenshot 2024-09-18 at 14 23
17\"\r\nsrc=\"https://github.com/user-attachments/assets/ecc496aa-1c43-4c3c-9ac8-d6e4e6cb8aad\">\r\n\r\nStorybook:\r\n<img
width=\"1256\" alt=\"Screenshot 2024-09-18 at 14 23
22\"\r\nsrc=\"https://github.com/user-attachments/assets/03d9f940-7b3f-4aea-9221-42b1c07119d1\">\r\n\r\nTooltips:\r\n<img
width=\"1250\" alt=\"Screenshot 2024-09-18 at 13 49
19\"\r\nsrc=\"https://github.com/user-attachments/assets/dc99b4cc-4eba-4815-8892-8e3fe7a041bb\">\r\n\r\n\r\n-
Use ESQL to fetch the top 500 entities sorted by last seen
property.\r\n- Display 20 entities per page.\r\n- Sorting is handles by
the server and saved on the URL\r\n- Current page is saved on the
URL\r\n- Filter entities types `service`, `host` or `container`\r\n-
Filter only entities from the built in definition\r\n- LIMITATION: The
EuiGrid doesn't have an embedded loading state, for\r\nnow, I'm
switching the entire view to display a loading spinner while\r\ndata is
being fetched.\r\n- PLUS: Storybook created with mock
data.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"e3f3c68e8d2e106740db9b4612df867c583eb512"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/193272","number":193272,"mergeCommit":{"message":"[Inventory][ECO]
Entities table (#193272)\n\nReal data:\r\n<img width=\"1237\"
alt=\"Screenshot 2024-09-18 at 14 23
17\"\r\nsrc=\"https://github.com/user-attachments/assets/ecc496aa-1c43-4c3c-9ac8-d6e4e6cb8aad\">\r\n\r\nStorybook:\r\n<img
width=\"1256\" alt=\"Screenshot 2024-09-18 at 14 23
22\"\r\nsrc=\"https://github.com/user-attachments/assets/03d9f940-7b3f-4aea-9221-42b1c07119d1\">\r\n\r\nTooltips:\r\n<img
width=\"1250\" alt=\"Screenshot 2024-09-18 at 13 49
19\"\r\nsrc=\"https://github.com/user-attachments/assets/dc99b4cc-4eba-4815-8892-8e3fe7a041bb\">\r\n\r\n\r\n-
Use ESQL to fetch the top 500 entities sorted by last seen
property.\r\n- Display 20 entities per page.\r\n- Sorting is handles by
the server and saved on the URL\r\n- Current page is saved on the
URL\r\n- Filter entities types `service`, `host` or `container`\r\n-
Filter only entities from the built in definition\r\n- LIMITATION: The
EuiGrid doesn't have an embedded loading state, for\r\nnow, I'm
switching the entire view to display a loading spinner while\r\ndata is
being fetched.\r\n- PLUS: Storybook created with mock
data.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"e3f3c68e8d2e106740db9b4612df867c583eb512"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com>
  • Loading branch information
kibanamachine and cauemarcondes authored Sep 18, 2024
1 parent aea6345 commit b5e09d1
Show file tree
Hide file tree
Showing 13 changed files with 3,501 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
*/

import type { ElasticsearchClient, Logger } from '@kbn/core/server';
import type { ESSearchRequest, InferSearchResponseOf } from '@kbn/es-types';
import type { ESQLSearchResponse, ESSearchRequest, InferSearchResponseOf } from '@kbn/es-types';
import { withSpan } from '@kbn/apm-utils';
import type { EsqlQueryRequest } from '@elastic/elasticsearch/lib/api/types';

type SearchRequest = ESSearchRequest & {
index: string | string[];
Expand All @@ -24,6 +25,7 @@ export interface ObservabilityElasticsearchClient {
operationName: string,
parameters: TSearchRequest
): Promise<InferSearchResponseOf<TDocument, TSearchRequest>>;
esql(operationName: string, parameters: EsqlQueryRequest): Promise<ESQLSearchResponse>;
client: ElasticsearchClient;
}

Expand All @@ -38,6 +40,26 @@ export function createObservabilityEsClient({
}): ObservabilityElasticsearchClient {
return {
client,
esql(operationName: string, parameters: EsqlQueryRequest) {
logger.trace(() => `Request (${operationName}):\n${JSON.stringify(parameters, null, 2)}`);
return withSpan({ name: operationName, labels: { plugin } }, () => {
return client.esql.query(
{ ...parameters },
{
querystring: {
drop_null_columns: true,
},
}
);
})
.then((response) => {
logger.trace(() => `Response (${operationName}):\n${JSON.stringify(response, null, 2)}`);
return response as unknown as ESQLSearchResponse;
})
.catch((error) => {
throw error;
});
},
search<TDocument = unknown, TSearchRequest extends SearchRequest = SearchRequest>(
operationName: string,
parameters: SearchRequest
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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 { ESQLSearchResponse } from '@kbn/es-types';

export function esqlResultToPlainObjects<T extends Record<string, any>>(
result: ESQLSearchResponse
): T[] {
return result.values.map((row) => {
return row.reduce<Record<string, unknown>>((acc, value, index) => {
const column = result.columns[index];
acc[column.name] = value;
return acc;
}, {});
}) as T[];
}
29 changes: 10 additions & 19 deletions x-pack/plugins/observability_solution/inventory/common/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,14 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as t from 'io-ts';

export interface LatestEntity {
agent: {
name: string[];
};
data_stream: {
type: string[];
};
cloud: {
availability_zone: string[];
};
entity: {
firstSeenTimestamp: string;
lastSeenTimestamp: string;
type: string;
displayName: string;
id: string;
identityFields: string[];
};
}
export const entityTypeRt = t.union([
t.literal('service'),
t.literal('host'),
t.literal('container'),
]);

export type EntityType = t.TypeOf<typeof entityTypeRt>;

export const MAX_NUMBER_OF_ENTITIES = 500;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export const ENTITY_LAST_SEEN = 'entity.lastSeenTimestamp';
export const ENTITY_ID = 'entity.id';
export const ENTITY_TYPE = 'entity.type';
export const ENTITY_DISPLAY_NAME = 'entity.displayName';
export const ENTITY_DEFINITION_ID = 'entity.definitionId';
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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 { EuiDataGridSorting } from '@elastic/eui';
import { Meta, Story } from '@storybook/react';
import React, { useMemo, useState } from 'react';
import { orderBy } from 'lodash';
import { EntitiesGrid } from '.';
import { ENTITY_LAST_SEEN } from '../../../common/es_fields/entities';
import { entitiesMock } from './mock/entities_mock';

const stories: Meta<{}> = {
title: 'app/inventory/entities_grid',
component: EntitiesGrid,
};
export default stories;

export const Example: Story<{}> = () => {
const [pageIndex, setPageIndex] = useState(0);
const [sort, setSort] = useState<EuiDataGridSorting['columns'][0]>({
id: ENTITY_LAST_SEEN,
direction: 'desc',
});

const sortedItems = useMemo(
() => orderBy(entitiesMock, sort.id, sort.direction),
[sort.direction, sort.id]
);

return (
<EntitiesGrid
entities={sortedItems}
loading={false}
sortDirection={sort.direction}
sortField={sort.id}
onChangePage={setPageIndex}
onChangeSort={setSort}
pageIndex={pageIndex}
/>
);
};

export const EmptyGridExample: Story<{}> = () => {
const [pageIndex, setPageIndex] = useState(0);
const [sort, setSort] = useState<EuiDataGridSorting['columns'][0]>({
id: ENTITY_LAST_SEEN,
direction: 'desc',
});

return (
<EntitiesGrid
entities={[]}
loading={false}
sortDirection={sort.direction}
sortField={sort.id}
onChangePage={setPageIndex}
onChangeSort={setSort}
pageIndex={pageIndex}
/>
);
};
Loading

0 comments on commit b5e09d1

Please sign in to comment.