Skip to content

Commit

Permalink
[ObsInfra][APM] Extract agent utils and custom icons (#170968)
Browse files Browse the repository at this point in the history
## 📓 Summary

Closes #170728 

This work comes from the need to use agent and cloud provider icons in
the new Log Detail flyout.

Since those icons were already used across the `infra` and `apm`
plugins, this was a good opportunity to extract the shared logic into
packages.
The results of this refactoring are two new packages:
- **@kbn/elastic-agent-utils**: exports small utilities and type
definition used to parse the icon to render and exploits also across the
APM plugin.
- **@kbn/custom-icons**: exports custom icons built on top of EuiIcon,
encapsulating logic related to mapping from data to the relative icon.

Apart from creating the new plugins, this also applies their usage to
the `infra` and `apm` plugins, while the Log Explorer flyout will
benefit from these working on
#170721.

## 🧪 How to test

### Infra

- Navigate to `Infrastructure -> Hosts`
- Verify the hosts table correctly renders the cloud provider icon for
each table entry.

### APM

- Navigate to `APM -> Services`.
  - Verify each table entry correctly displays the related agent icon.

- Navigate to `APM -> Services`.
- Click on a service where t a cloud provider icon is expected to appear
next to the service name.
  - Verify the icon is correctly displayed.

- Navigate to `APM -> Services -> Service Map`.
  - Create a new group.
- Verify the agent icon is correctly displayed for each entry in the
preview list.

- Navigate to `APM -> Traces`.
  - Verify each table entry correctly displays the related agent icon.

- Navigate to `APM -> Settings -> Agent Explorer`.
  - Verify each table entry correctly displays the related agent icon.

---------

Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
3 people committed Nov 20, 2023
1 parent a968a2e commit b83b495
Show file tree
Hide file tree
Showing 82 changed files with 886 additions and 589 deletions.
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ x-pack/plugins/cross_cluster_replication @elastic/platform-deployment-management
packages/kbn-crypto @elastic/kibana-security
packages/kbn-crypto-browser @elastic/kibana-core
x-pack/plugins/custom_branding @elastic/appex-sharedux
packages/kbn-custom-icons @elastic/obs-ux-logs-team
packages/kbn-custom-integrations @elastic/obs-ux-logs-team
src/plugins/custom_integrations @elastic/fleet
packages/kbn-cypress-config @elastic/kibana-operations
Expand Down Expand Up @@ -345,6 +346,7 @@ packages/kbn-ebt-tools @elastic/kibana-core
packages/kbn-ecs @elastic/kibana-core @elastic/security-threat-hunting-investigations
x-pack/packages/security-solution/ecs_data_quality_dashboard @elastic/security-threat-hunting-investigations
x-pack/plugins/ecs_data_quality_dashboard @elastic/security-threat-hunting-investigations
packages/kbn-elastic-agent-utils @elastic/obs-ux-logs-team
x-pack/packages/kbn-elastic-assistant @elastic/security-solution
x-pack/plugins/elastic_assistant @elastic/security-solution
test/plugin_functional/plugins/elasticsearch_client_plugin @elastic/kibana-core
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@
"@kbn/crypto": "link:packages/kbn-crypto",
"@kbn/crypto-browser": "link:packages/kbn-crypto-browser",
"@kbn/custom-branding-plugin": "link:x-pack/plugins/custom_branding",
"@kbn/custom-icons": "link:packages/kbn-custom-icons",
"@kbn/custom-integrations": "link:packages/kbn-custom-integrations",
"@kbn/custom-integrations-plugin": "link:src/plugins/custom_integrations",
"@kbn/dashboard-enhanced-plugin": "link:x-pack/plugins/dashboard_enhanced",
Expand Down Expand Up @@ -390,6 +391,7 @@
"@kbn/ecs": "link:packages/kbn-ecs",
"@kbn/ecs-data-quality-dashboard": "link:x-pack/packages/security-solution/ecs_data_quality_dashboard",
"@kbn/ecs-data-quality-dashboard-plugin": "link:x-pack/plugins/ecs_data_quality_dashboard",
"@kbn/elastic-agent-utils": "link:packages/kbn-elastic-agent-utils",
"@kbn/elastic-assistant": "link:x-pack/packages/kbn-elastic-assistant",
"@kbn/elastic-assistant-plugin": "link:x-pack/plugins/elastic_assistant",
"@kbn/elasticsearch-client-plugin": "link:test/plugin_functional/plugins/elasticsearch_client_plugin",
Expand Down
9 changes: 9 additions & 0 deletions packages/kbn-custom-icons/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/

module.exports = require('@kbn/storybook').defaultConfig;
29 changes: 29 additions & 0 deletions packages/kbn-custom-icons/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# @kbn/custom-icons

A utility package, `@kbn/custom-icons`, that provides components for rendering icons related to Elastic Agents, Cloud Providers and more.

## Components

### `<AgentIcon />`

```jsx
<AgentIcon agentName={agentName} />
```

This component renders an icon corresponding to the specified Elastic Agent name (`agentName`).

#### Props

- **`agentName`**: The name of the Elastic Agent for which the icon should be rendered.

### `<CloudProviderIcon />`

```jsx
<CloudProviderIcon cloudProvider={cloudProvider} />
```

This component renders an icon associated with the specified Cloud Provider (`cloudProvider`).

#### Props

- **`cloudProvider`**: The name of the Cloud Provider for which the icon should be rendered.
File renamed without changes
3 changes: 3 additions & 0 deletions packages/kbn-custom-icons/assets/default.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
16 changes: 16 additions & 0 deletions packages/kbn-custom-icons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export { getAgentIcon } from './src/components/agent_icon/get_agent_icon';
export { getServerlessIcon } from './src/components/agent_icon/get_serverless_icon';
export { AgentIcon } from './src/components/agent_icon';
export type { AgentIconProps } from './src/components/agent_icon';

export { getCloudProviderIcon } from './src/components/cloud_provider_icon/get_cloud_provider_icon';
export type { CloudProvider } from './src/components/cloud_provider_icon/get_cloud_provider_icon';
export { CloudProviderIcon } from './src/components/cloud_provider_icon';
export type { CloudProviderIconProps } from './src/components/cloud_provider_icon';
13 changes: 13 additions & 0 deletions packages/kbn-custom-icons/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/

module.exports = {
preset: '@kbn/test',
rootDir: '../..',
roots: ['<rootDir>/packages/kbn-custom-icons'],
};
5 changes: 5 additions & 0 deletions packages/kbn-custom-icons/kibana.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/custom-icons",
"owner": "@elastic/obs-ux-logs-team"
}
7 changes: 7 additions & 0 deletions packages/kbn-custom-icons/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@kbn/custom-icons",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0",
"sideEffects": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/

import { EuiCard, EuiFlexGroup, EuiFlexItem, EuiImage, EuiToolTip } from '@elastic/eui';
import type { Story } from '@storybook/react';
import React from 'react';
import { AGENT_NAMES } from '@kbn/elastic-agent-utils';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import { getAgentIcon } from './get_agent_icon';
import { AgentIcon } from '.';

export default {
title: 'Custom Icons/AgentIcon',
component: AgentIcon,
};

export const List: Story = () => {
return (
<EuiThemeProvider darkMode={false}>
<EuiFlexGroup gutterSize="l" wrap={true}>
{AGENT_NAMES.map((agentName) => {
return (
<EuiFlexItem key={agentName} grow={false}>
<EuiCard
icon={
<EuiToolTip position="top" content="Icon rendered with `EuiImage`">
<EuiImage
size="s"
hasShadow
alt={agentName}
src={getAgentIcon(agentName, false)}
/>
</EuiToolTip>
}
title={agentName}
description={
<EuiToolTip position="bottom" content="Icon rendered with `AgentIcon`">
<AgentIcon agentName={agentName} />
</EuiToolTip>
}
/>
</EuiFlexItem>
);
})}
</EuiFlexGroup>
</EuiThemeProvider>
);
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/*
* 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.
* 2.0 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 or the Server
* Side Public License, v 1.
*/

import { getAgentIconKey } from './get_agent_icon';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
/*
* 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.
* 2.0 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 or the Server
* Side Public License, v 1.
*/

import {
isAndroidAgentName,
isIosAgentName,
isRumAgentName,
isJavaAgentName,
isAndroidAgentName,
isRumAgentName,
OpenTelemetryAgentName,
OPEN_TELEMETRY_AGENT_NAMES,
} from '../../../../common/agent_name';
import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent';
import defaultIcon from '../span_icon/icons/default.svg';
import cppIcon from './icons/cpp.svg';
import darkCppIcon from './icons/cpp_dark.svg';
import dotNetIcon from './icons/dot_net.svg';
import erlangIcon from './icons/erlang.svg';
import darkErlangIcon from './icons/erlang_dark.svg';
import goIcon from './icons/go.svg';
import iosIcon from './icons/ios.svg';
import darkIosIcon from './icons/ios_dark.svg';
import javaIcon from './icons/java.svg';
import nodeJsIcon from './icons/nodejs.svg';
import ocamlIcon from './icons/ocaml.svg';
import openTelemetryIcon from './icons/otel_default.svg';
import phpIcon from './icons/php.svg';
import pythonIcon from './icons/python.svg';
import rubyIcon from './icons/ruby.svg';
import rumJsIcon from './icons/rumjs.svg';
import darkPhpIcon from './icons/php_dark.svg';
import darkRumJsIcon from './icons/rumjs_dark.svg';
import rustIcon from './icons/rust.svg';
import darkRustIcon from './icons/rust_dark.svg';
import androidIcon from './icons/android.svg';
} from '@kbn/elastic-agent-utils';
import defaultIcon from '../../../assets/default.svg';
import cppIcon from '../../../assets/cpp.svg';
import darkCppIcon from '../../../assets/cpp_dark.svg';
import dotNetIcon from '../../../assets/dot_net.svg';
import erlangIcon from '../../../assets/erlang.svg';
import darkErlangIcon from '../../../assets/erlang_dark.svg';
import goIcon from '../../../assets/go.svg';
import iosIcon from '../../../assets/ios.svg';
import darkIosIcon from '../../../assets/ios_dark.svg';
import javaIcon from '../../../assets/java.svg';
import nodeJsIcon from '../../../assets/nodejs.svg';
import ocamlIcon from '../../../assets/ocaml.svg';
import openTelemetryIcon from '../../../assets/otel_default.svg';
import phpIcon from '../../../assets/php.svg';
import pythonIcon from '../../../assets/python.svg';
import rubyIcon from '../../../assets/ruby.svg';
import rumJsIcon from '../../../assets/rumjs.svg';
import darkPhpIcon from '../../../assets/php_dark.svg';
import darkRumJsIcon from '../../../assets/rumjs_dark.svg';
import rustIcon from '../../../assets/rust.svg';
import darkRustIcon from '../../../assets/rust_dark.svg';
import androidIcon from '../../../assets/android.svg';

const agentIcons: { [key: string]: string } = {
cpp: cppIcon,
Expand Down Expand Up @@ -89,25 +89,19 @@ export function getAgentIconKey(agentName: string) {
}

// Remove "opentelemetry/" prefix
const agentNameWithoutPrefix = lowercasedAgentName.replace(
/^opentelemetry\//,
''
);
const agentNameWithoutPrefix = lowercasedAgentName.replace(/^opentelemetry\//, '');

if (Object.keys(agentIcons).includes(agentNameWithoutPrefix)) {
return agentNameWithoutPrefix;
}

// OpenTelemetry-only agents
if (OPEN_TELEMETRY_AGENT_NAMES.includes(lowercasedAgentName as AgentName)) {
if (OPEN_TELEMETRY_AGENT_NAMES.includes(lowercasedAgentName as OpenTelemetryAgentName)) {
return 'opentelemetry';
}
}

export function getAgentIcon(
agentName: string | undefined,
isDarkMode: boolean
) {
export function getAgentIcon(agentName: string | undefined, isDarkMode: boolean = false) {
const key = agentName && getAgentIconKey(agentName);
if (!key) {
return defaultIcon;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { ServerlessType } from '@kbn/elastic-agent-utils';
import defaultIcon from '../../../assets/default.svg';
import lambdaIcon from '../../../assets/lambda.svg';
import azureFunctionsIcon from '../../../assets/functions.svg';

const serverlessIcons: Record<ServerlessType, string> = {
'aws.lambda': lambdaIcon,
'azure.functions': azureFunctionsIcon,
} as const;

export function getServerlessIcon(serverlessType?: ServerlessType) {
if (!serverlessType) {
return defaultIcon;
}
return serverlessIcons[serverlessType] ?? defaultIcon;
}
23 changes: 23 additions & 0 deletions packages/kbn-custom-icons/src/components/agent_icon/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/

import React from 'react';
import { EuiIcon, EuiIconProps, useEuiTheme } from '@elastic/eui';
import { AgentName } from '@kbn/elastic-agent-utils';
import { getAgentIcon } from './get_agent_icon';

export interface AgentIconProps extends Omit<EuiIconProps, 'type'> {
agentName?: AgentName;
}

export function AgentIcon({ agentName, size = 'l', ...props }: AgentIconProps) {
const theme = useEuiTheme();
const icon = getAgentIcon(agentName, theme.colorMode === 'DARK');

return <EuiIcon type={icon} size={size} title={agentName} {...props} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/

import { EuiCard, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import type { Story } from '@storybook/react';
import React from 'react';
import { CloudProviderIcon } from '.';
import { CloudProvider } from './get_cloud_provider_icon';

export default {
title: 'Custom Icons/CloudProviderIcon',
component: CloudProviderIcon,
};

const providers: CloudProvider[] = ['gcp', 'aws', 'azure', 'unknownProvider'];

export const List: Story = () => {
return (
<EuiFlexGroup gutterSize="l" wrap={true}>
{providers.map((cloudProvider) => {
return (
<EuiFlexItem key={cloudProvider} grow={false}>
<EuiCard
title={cloudProvider as string}
description={
<EuiToolTip position="bottom" content="Icon rendered with `CloudProviderIcon`">
<CloudProviderIcon cloudProvider={cloudProvider} size="xxl" />
</EuiToolTip>
}
/>
</EuiFlexItem>
);
})}
</EuiFlexGroup>
);
};

0 comments on commit b83b495

Please sign in to comment.