Skip to content

Commit

Permalink
[Inventory] Fixing entity links (#195625)
Browse files Browse the repository at this point in the history
Regression from #195204
  • Loading branch information
cauemarcondes authored Oct 9, 2024
1 parent ac2e0c8 commit d2644ff
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,26 +78,27 @@ interface BaseEntity {
[ENTITY_TYPE]: EntityType;
[ENTITY_DISPLAY_NAME]: string;
[ENTITY_DEFINITION_ID]: string;
[ENTITY_IDENTITY_FIELDS]: string[];
[ENTITY_IDENTITY_FIELDS]: string | string[];
[key: string]: any;
}

/**
* These types are based on service, host and container from the built in definition.
*/
interface ServiceEntity extends BaseEntity {
export interface ServiceEntity extends BaseEntity {
[ENTITY_TYPE]: 'service';
[SERVICE_NAME]: string;
[SERVICE_ENVIRONMENT]?: string | string[] | null;
[AGENT_NAME]: string | string[] | null;
}

interface HostEntity extends BaseEntity {
export interface HostEntity extends BaseEntity {
[ENTITY_TYPE]: 'host';
[HOST_NAME]: string;
[CLOUD_PROVIDER]: string | string[] | null;
}

interface ContainerEntity extends BaseEntity {
export interface ContainerEntity extends BaseEntity {
[ENTITY_TYPE]: 'container';
[CONTAINER_ID]: string;
[CLOUD_PROVIDER]: string | string[] | null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* 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 KibanaReactContextValue } from '@kbn/kibana-react-plugin/public';
import * as useKibana from '../../../hooks/use_kibana';
import { EntityName } from '.';
import { ContainerEntity, HostEntity, ServiceEntity } from '../../../../common/entities';
import { render, screen } from '@testing-library/react';
import React from 'react';
import { ASSET_DETAILS_LOCATOR_ID } from '@kbn/observability-shared-plugin/common/locators/infra/asset_details_locator';

describe('EntityName', () => {
jest.spyOn(useKibana, 'useKibana').mockReturnValue({
services: {
share: {
url: {
locators: {
get: (locatorId: string) => {
return {
getRedirectUrl: (params: { [key: string]: any }) => {
if (locatorId === ASSET_DETAILS_LOCATOR_ID) {
return `assets_url/${params.assetType}/${params.assetId}`;
}
return `services_url/${params.serviceName}?environment=${params.environment}`;
},
};
},
},
},
},
},
} as unknown as KibanaReactContextValue<useKibana.InventoryKibanaContext>);

afterAll(() => {
jest.clearAllMocks();
});

it('returns host link', () => {
const entity: HostEntity = {
'entity.lastSeenTimestamp': 'foo',
'entity.id': '1',
'entity.type': 'host',
'entity.displayName': 'foo',
'entity.identityFields': 'host.name',
'host.name': 'foo',
'entity.definitionId': 'host',
'cloud.provider': null,
};
render(<EntityName entity={entity} />);
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
'assets_url/host/foo'
);
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
});

it('returns container link', () => {
const entity: ContainerEntity = {
'entity.lastSeenTimestamp': 'foo',
'entity.id': '1',
'entity.type': 'container',
'entity.displayName': 'foo',
'entity.identityFields': 'container.id',
'container.id': 'foo',
'entity.definitionId': 'container',
'cloud.provider': null,
};
render(<EntityName entity={entity} />);
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
'assets_url/container/foo'
);
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
});

it('returns service link without environment', () => {
const entity: ServiceEntity = {
'entity.lastSeenTimestamp': 'foo',
'entity.id': '1',
'entity.type': 'service',
'entity.displayName': 'foo',
'entity.identityFields': 'service.name',
'service.name': 'foo',
'entity.definitionId': 'service',
'agent.name': 'bar',
};
render(<EntityName entity={entity} />);
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
'services_url/foo?environment=undefined'
);
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
});

it('returns service link with environment', () => {
const entity: ServiceEntity = {
'entity.lastSeenTimestamp': 'foo',
'entity.id': '1',
'entity.type': 'service',
'entity.displayName': 'foo',
'entity.identityFields': 'service.name',
'service.name': 'foo',
'entity.definitionId': 'service',
'agent.name': 'bar',
'service.environment': 'baz',
};
render(<EntityName entity={entity} />);
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
'services_url/foo?environment=baz'
);
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
});

it('returns service link with first environment when it is an array', () => {
const entity: ServiceEntity = {
'entity.lastSeenTimestamp': 'foo',
'entity.id': '1',
'entity.type': 'service',
'entity.displayName': 'foo',
'entity.identityFields': 'service.name',
'service.name': 'foo',
'entity.definitionId': 'service',
'agent.name': 'bar',
'service.environment': ['baz', 'bar', 'foo'],
};
render(<EntityName entity={entity} />);
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
'services_url/foo?environment=baz'
);
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
});

it('returns service link identity fields is an array', () => {
const entity: ServiceEntity = {
'entity.lastSeenTimestamp': 'foo',
'entity.id': '1',
'entity.type': 'service',
'entity.displayName': 'foo',
'entity.identityFields': ['service.name', 'service.environment'],
'service.name': 'foo',
'entity.definitionId': 'service',
'agent.name': 'bar',
'service.environment': 'baz',
};
render(<EntityName entity={entity} />);
expect(screen.queryByTestId('entityNameLink')?.getAttribute('href')).toEqual(
'services_url/foo?environment=baz'
);
expect(screen.queryByTestId('entityNameDisplayName')?.textContent).toEqual('foo');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,37 @@ export function EntityName({ entity }: EntityNameProps) {
const getEntityRedirectUrl = useCallback(() => {
const type = entity[ENTITY_TYPE];
// For service, host and container type there is only one identity field
const identityField = entity[ENTITY_IDENTITY_FIELDS][0];
const identityField = Array.isArray(entity[ENTITY_IDENTITY_FIELDS])
? entity[ENTITY_IDENTITY_FIELDS][0]
: entity[ENTITY_IDENTITY_FIELDS];
const identityValue = entity[identityField];

// Any unrecognised types will always return undefined
switch (type) {
case 'host':
case 'container':
return assetDetailsLocator?.getRedirectUrl({
assetId: identityField,
assetId: identityValue,
assetType: type,
});

case 'service':
return serviceOverviewLocator?.getRedirectUrl({
serviceName: identityField,
serviceName: identityValue,
environment: [entity[SERVICE_ENVIRONMENT] || undefined].flat()[0],
});
}
}, [entity, assetDetailsLocator, serviceOverviewLocator]);

return (
<EuiLink data-test-subj="inventoryCellValueLink" href={getEntityRedirectUrl()}>
<EuiLink data-test-subj="entityNameLink" href={getEntityRedirectUrl()}>
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={0}>
<EntityIcon entity={entity} />
</EuiFlexItem>
<EuiFlexItem className="eui-textTruncate">
<span className="eui-textTruncate">{entity[ENTITY_DISPLAY_NAME]}</span>
<span className="eui-textTruncate" data-test-subj="entityNameDisplayName">
{entity[ENTITY_DISPLAY_NAME]}
</span>
</EuiFlexItem>
</EuiFlexGroup>
</EuiLink>
Expand Down

0 comments on commit d2644ff

Please sign in to comment.