Skip to content

Commit a3da125

Browse files
[8.x] [Inventory] Fixing entity links (#195625) (#195667)
# Backport This will backport the following commits from `main` to `8.x`: - [[Inventory] Fixing entity links (#195625)](#195625) <!--- 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-10-09T18:19:49Z","message":"[Inventory] Fixing entity links (#195625)\n\nRegression from https://github.com/elastic/kibana/pull/195204","sha":"d2644ffe49ea732e5f048957a51350efbc321687","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","backport:prev-minor","ci:project-deploy-observability","v8.16.0"],"title":"[Inventory] Fixing entity links","number":195625,"url":"https://github.com/elastic/kibana/pull/195625","mergeCommit":{"message":"[Inventory] Fixing entity links (#195625)\n\nRegression from https://github.com/elastic/kibana/pull/195204","sha":"d2644ffe49ea732e5f048957a51350efbc321687"}},"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/195625","number":195625,"mergeCommit":{"message":"[Inventory] Fixing entity links (#195625)\n\nRegression from https://github.com/elastic/kibana/pull/195204","sha":"d2644ffe49ea732e5f048957a51350efbc321687"}},{"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>
1 parent 8fdf77d commit a3da125

3 files changed

Lines changed: 167 additions & 10 deletions

File tree

x-pack/plugins/observability_solution/inventory/common/entities.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,26 +78,27 @@ interface BaseEntity {
7878
[ENTITY_TYPE]: EntityType;
7979
[ENTITY_DISPLAY_NAME]: string;
8080
[ENTITY_DEFINITION_ID]: string;
81-
[ENTITY_IDENTITY_FIELDS]: string[];
81+
[ENTITY_IDENTITY_FIELDS]: string | string[];
82+
[key: string]: any;
8283
}
8384

8485
/**
8586
* These types are based on service, host and container from the built in definition.
8687
*/
87-
interface ServiceEntity extends BaseEntity {
88+
export interface ServiceEntity extends BaseEntity {
8889
[ENTITY_TYPE]: 'service';
8990
[SERVICE_NAME]: string;
9091
[SERVICE_ENVIRONMENT]?: string | string[] | null;
9192
[AGENT_NAME]: string | string[] | null;
9293
}
9394

94-
interface HostEntity extends BaseEntity {
95+
export interface HostEntity extends BaseEntity {
9596
[ENTITY_TYPE]: 'host';
9697
[HOST_NAME]: string;
9798
[CLOUD_PROVIDER]: string | string[] | null;
9899
}
99100

100-
interface ContainerEntity extends BaseEntity {
101+
export interface ContainerEntity extends BaseEntity {
101102
[ENTITY_TYPE]: 'container';
102103
[CONTAINER_ID]: string;
103104
[CLOUD_PROVIDER]: string | string[] | null;
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { type KibanaReactContextValue } from '@kbn/kibana-react-plugin/public';
9+
import * as useKibana from '../../../hooks/use_kibana';
10+
import { EntityName } from '.';
11+
import { ContainerEntity, HostEntity, ServiceEntity } from '../../../../common/entities';
12+
import { render, screen } from '@testing-library/react';
13+
import React from 'react';
14+
import { ASSET_DETAILS_LOCATOR_ID } from '@kbn/observability-shared-plugin/common/locators/infra/asset_details_locator';
15+
16+
describe('EntityName', () => {
17+
jest.spyOn(useKibana, 'useKibana').mockReturnValue({
18+
services: {
19+
share: {
20+
url: {
21+
locators: {
22+
get: (locatorId: string) => {
23+
return {
24+
getRedirectUrl: (params: { [key: string]: any }) => {
25+
if (locatorId === ASSET_DETAILS_LOCATOR_ID) {
26+
return `assets_url/${params.assetType}/${params.assetId}`;
27+
}
28+
return `services_url/${params.serviceName}?environment=${params.environment}`;
29+
},
30+
};
31+
},
32+
},
33+
},
34+
},
35+
},
36+
} as unknown as KibanaReactContextValue<useKibana.InventoryKibanaContext>);
37+
38+
afterAll(() => {
39+
jest.clearAllMocks();
40+
});
41+
42+
it('returns host link', () => {
43+
const entity: HostEntity = {
44+
'entity.lastSeenTimestamp': 'foo',
45+
'entity.id': '1',
46+
'entity.type': 'host',
47+
'entity.displayName': 'foo',
48+
'entity.identityFields': 'host.name',
49+
'host.name': 'foo',
50+
'entity.definitionId': 'host',
51+
'cloud.provider': null,
52+
};
53+
render(<EntityName entity={entity} />);
54+
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
55+
'assets_url/host/foo'
56+
);
57+
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
58+
});
59+
60+
it('returns container link', () => {
61+
const entity: ContainerEntity = {
62+
'entity.lastSeenTimestamp': 'foo',
63+
'entity.id': '1',
64+
'entity.type': 'container',
65+
'entity.displayName': 'foo',
66+
'entity.identityFields': 'container.id',
67+
'container.id': 'foo',
68+
'entity.definitionId': 'container',
69+
'cloud.provider': null,
70+
};
71+
render(<EntityName entity={entity} />);
72+
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
73+
'assets_url/container/foo'
74+
);
75+
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
76+
});
77+
78+
it('returns service link without environment', () => {
79+
const entity: ServiceEntity = {
80+
'entity.lastSeenTimestamp': 'foo',
81+
'entity.id': '1',
82+
'entity.type': 'service',
83+
'entity.displayName': 'foo',
84+
'entity.identityFields': 'service.name',
85+
'service.name': 'foo',
86+
'entity.definitionId': 'service',
87+
'agent.name': 'bar',
88+
};
89+
render(<EntityName entity={entity} />);
90+
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
91+
'services_url/foo?environment=undefined'
92+
);
93+
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
94+
});
95+
96+
it('returns service link with environment', () => {
97+
const entity: ServiceEntity = {
98+
'entity.lastSeenTimestamp': 'foo',
99+
'entity.id': '1',
100+
'entity.type': 'service',
101+
'entity.displayName': 'foo',
102+
'entity.identityFields': 'service.name',
103+
'service.name': 'foo',
104+
'entity.definitionId': 'service',
105+
'agent.name': 'bar',
106+
'service.environment': 'baz',
107+
};
108+
render(<EntityName entity={entity} />);
109+
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
110+
'services_url/foo?environment=baz'
111+
);
112+
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
113+
});
114+
115+
it('returns service link with first environment when it is an array', () => {
116+
const entity: ServiceEntity = {
117+
'entity.lastSeenTimestamp': 'foo',
118+
'entity.id': '1',
119+
'entity.type': 'service',
120+
'entity.displayName': 'foo',
121+
'entity.identityFields': 'service.name',
122+
'service.name': 'foo',
123+
'entity.definitionId': 'service',
124+
'agent.name': 'bar',
125+
'service.environment': ['baz', 'bar', 'foo'],
126+
};
127+
render(<EntityName entity={entity} />);
128+
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
129+
'services_url/foo?environment=baz'
130+
);
131+
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
132+
});
133+
134+
it('returns service link identity fields is an array', () => {
135+
const entity: ServiceEntity = {
136+
'entity.lastSeenTimestamp': 'foo',
137+
'entity.id': '1',
138+
'entity.type': 'service',
139+
'entity.displayName': 'foo',
140+
'entity.identityFields': ['service.name', 'service.environment'],
141+
'service.name': 'foo',
142+
'entity.definitionId': 'service',
143+
'agent.name': 'bar',
144+
'service.environment': 'baz',
145+
};
146+
render(<EntityName entity={entity} />);
147+
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
148+
'services_url/foo?environment=baz'
149+
);
150+
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
151+
});
152+
});

x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/index.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,33 +36,37 @@ export function EntityName({ entity }: EntityNameProps) {
3636
const getEntityRedirectUrl = useCallback(() => {
3737
const type = entity[ENTITY_TYPE];
3838
// For service, host and container type there is only one identity field
39-
const identityField = entity[ENTITY_IDENTITY_FIELDS][0];
39+
const identityField = Array.isArray(entity[ENTITY_IDENTITY_FIELDS])
40+
? entity[ENTITY_IDENTITY_FIELDS][0]
41+
: entity[ENTITY_IDENTITY_FIELDS];
42+
const identityValue = entity[identityField];
4043

41-
// Any unrecognised types will always return undefined
4244
switch (type) {
4345
case 'host':
4446
case 'container':
4547
return assetDetailsLocator?.getRedirectUrl({
46-
assetId: identityField,
48+
assetId: identityValue,
4749
assetType: type,
4850
});
4951

5052
case 'service':
5153
return serviceOverviewLocator?.getRedirectUrl({
52-
serviceName: identityField,
54+
serviceName: identityValue,
5355
environment: [entity[SERVICE_ENVIRONMENT] || undefined].flat()[0],
5456
});
5557
}
5658
}, [entity, assetDetailsLocator, serviceOverviewLocator]);
5759

5860
return (
59-
<EuiLink data-test-subj="inventoryCellValueLink" href={getEntityRedirectUrl()}>
61+
<EuiLink data-test-subj="entityNameLink" href={getEntityRedirectUrl()}>
6062
<EuiFlexGroup gutterSize="s" alignItems="center">
6163
<EuiFlexItem grow={0}>
6264
<EntityIcon entity={entity} />
6365
</EuiFlexItem>
6466
<EuiFlexItem className="eui-textTruncate">
65-
<span className="eui-textTruncate">{entity[ENTITY_DISPLAY_NAME]}</span>
67+
<span className="eui-textTruncate" data-test-subj="entityNameDisplayName">
68+
{entity[ENTITY_DISPLAY_NAME]}
69+
</span>
6670
</EuiFlexItem>
6771
</EuiFlexGroup>
6872
</EuiLink>

0 commit comments

Comments
 (0)