From 0ac9f238dae90e7f2627be6e82ed14091faaffec Mon Sep 17 00:00:00 2001 From: gbamparop Date: Fri, 3 Dec 2021 16:22:22 +0000 Subject: [PATCH 01/15] Add support for creating agent keys --- x-pack/plugins/apm/common/agent_key_types.ts | 13 + .../Settings/agent_keys/create_agent_key.tsx | 248 ++++++++++++++++++ .../create_agent_key/agent_key_callout.tsx | 86 ++++++ .../app/Settings/agent_keys/index.tsx | 207 +++++++++------ x-pack/plugins/apm/public/plugin.ts | 3 + .../routes/agent_keys/create_agent_key.ts | 138 ++++++++++ .../apm/server/routes/agent_keys/route.ts | 34 ++- 7 files changed, 646 insertions(+), 83 deletions(-) create mode 100644 x-pack/plugins/apm/common/agent_key_types.ts create mode 100644 x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx create mode 100644 x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx create mode 100644 x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts diff --git a/x-pack/plugins/apm/common/agent_key_types.ts b/x-pack/plugins/apm/common/agent_key_types.ts new file mode 100644 index 0000000000000..986e67d35698e --- /dev/null +++ b/x-pack/plugins/apm/common/agent_key_types.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface CreateApiKeyResponse { + api_key: string; + expiration?: number; + id: string; + name: string; +} diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx new file mode 100644 index 0000000000000..e7fcefc62ac80 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx @@ -0,0 +1,248 @@ +/* + * 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 React, { useState, useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButton, + EuiFlyout, + EuiFlyoutHeader, + EuiTitle, + EuiFlyoutBody, + EuiFlexGroup, + EuiFlexItem, + EuiFlyoutFooter, + EuiButtonEmpty, + EuiForm, + EuiFormRow, + EuiSpacer, + EuiFieldText, + EuiText, + EuiFormFieldset, + EuiCheckbox, + htmlIdGenerator, +} from '@elastic/eui'; +import { isEmpty } from 'lodash'; +import { callApmApi } from '../../../../services/rest/createCallApmApi'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { ApmPluginStartDeps } from '../../../../plugin'; +import { CreateApiKeyResponse } from '../../../../../common/agent_key_types'; + +interface Props { + onCancel(): void; + onSuccess: (agentKey: CreateApiKeyResponse) => void; + onError: (keyName: string) => void; +} + +export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { + const { + services: { security }, + } = useKibana(); + + const [username, setUsername] = useState(''); + + const [formTouched, setFormTouched] = useState(false); + const [keyName, setKeyName] = useState(''); + const [agentConfigChecked, setAgentConfigChecked] = useState(true); + const [eventWriteChecked, setEventWriteChecked] = useState(true); + const [sourcemapChecked, setSourcemapChecked] = useState(true); + + const isFormInvalid = isEmpty(keyName); + const formError = i18n.translate( + 'xpack.apm.settings.agentKeys.createKeyFlyout.name.placeholder', + { + defaultMessage: 'Enter a name', + } + ); + + useEffect(() => { + const getCurrentUser = async () => { + try { + const authenticatedUser = await security?.authc.getCurrentUser(); + setUsername(authenticatedUser?.username || ''); + } catch { + setUsername(''); + } + }; + getCurrentUser(); + }, [security?.authc]); + + const createAgentKeyTitle = i18n.translate( + 'xpack.apm.settings.agentKeys.createKeyFlyout.createAgentKey', + { + defaultMessage: 'Create agent key', + } + ); + + const createAgentKey = async () => { + setFormTouched(true); + if (isFormInvalid) { + return; + } + + try { + const { agentKey } = await callApmApi({ + endpoint: 'POST /apm/agent_keys', + signal: null, + params: { + body: { + name: keyName, + sourcemap: sourcemapChecked, + event: eventWriteChecked, + agentConfig: agentConfigChecked, + }, + }, + }); + + onSuccess(agentKey); + } catch (error) { + onError(keyName); + } + }; + + return ( + + + +

{createAgentKeyTitle}

+
+
+ + + + {username && ( + + {username} + + )} + + setKeyName(e.target.value)} + isInvalid={formTouched && isFormInvalid} + onBlur={() => setFormTouched(true)} + /> + + + + + setAgentConfigChecked(!agentConfigChecked)} + /> + + + + setEventWriteChecked(!eventWriteChecked)} + /> + + + + setSourcemapChecked(!sourcemapChecked)} + /> + + + + + + + + + + + {i18n.translate( + 'xpack.apm.settings.agentKeys.createKeyFlyout.cancelButton', + { + defaultMessage: 'Cancel', + } + )} + + + + + {createAgentKeyTitle} + + + + +
+ ); +} diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx new file mode 100644 index 0000000000000..a67b3821120fd --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx @@ -0,0 +1,86 @@ +/* + * 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 React, { Fragment } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiSpacer, + EuiCallOut, + EuiButtonIcon, + EuiCopy, + EuiFormControlLayout, +} from '@elastic/eui'; + +interface Props { + name: string; + token: string; +} + +export function AgentKeyCallOut({ name, token }: Props) { + return ( + + +

+ {i18n.translate( + 'xpack.apm.settings.agentKeys.copyAgentKeyField.message', + { + defaultMessage: + 'Copy this key now. You will not be able to view it again.', + } + )} +

+ + {(copy) => ( + + )} + + } + > + + +
+ +
+ ); +} diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx index 23acc2e98dd73..89c348c5646b2 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { Fragment } from 'react'; +import React, { Fragment, useState } from 'react'; import { isEmpty } from 'lodash'; import { i18n } from '@kbn/i18n'; import { @@ -21,6 +21,10 @@ import { useFetcher, FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { PermissionDenied } from './prompts/permission_denied'; import { ApiKeysNotEnabled } from './prompts/api_keys_not_enabled'; import { AgentKeysTable } from './agent_keys_table'; +import { CreateAgentKeyFlyout } from './create_agent_key'; +import { AgentKeyCallOut } from './create_agent_key/agent_key_callout'; +import { CreateApiKeyResponse } from '../../../../../common/agent_key_types'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; const INITIAL_DATA = { areApiKeysEnabled: false, @@ -28,33 +32,12 @@ const INITIAL_DATA = { }; export function AgentKeys() { - return ( - - - {i18n.translate('xpack.apm.settings.agentKeys.descriptionText', { - defaultMessage: - 'View and delete agent keys. An agent key sends requests on behalf of a user.', - })} - - - - - -

- {i18n.translate('xpack.apm.settings.agentKeys.title', { - defaultMessage: 'Agent keys', - })} -

-
-
-
- - -
- ); -} + const { toasts } = useApmPluginContext().core.notifications; + + const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); + const [createdAgentKey, setCreatedAgentKey] = + useState(); -function AgentKeysContent() { const { data: { areApiKeysEnabled, canManage } = INITIAL_DATA, status: privilegesStatus, @@ -93,9 +76,11 @@ function AgentKeysContent() { privilegesStatus === FETCH_STATUS.FAILURE || status === FETCH_STATUS.FAILURE; + let content = null; + if (!agentKeys) { if (isLoading) { - return ( + content = ( } titleSize="xs" @@ -111,10 +96,8 @@ function AgentKeysContent() { } /> ); - } - - if (requestFailed) { - return ( + } else if (requestFailed) { + content = ( ); + } else if (!canManage) { + content = ; + } else if (!areApiKeysEnabled) { + content = ; } - - if (!canManage) { - return ; - } - - if (!areApiKeysEnabled) { - return ; + } else { + if (isEmpty(agentKeys)) { + content = ( + + {i18n.translate('xpack.apm.settings.agentKeys.emptyPromptTitle', { + defaultMessage: 'Create your first agent key', + })} + + } + body={ +

+ {i18n.translate('xpack.apm.settings.agentKeys.emptyPromptBody', { + defaultMessage: + 'Create agent keys to authorize requests to the APM Server.', + })} +

+ } + actions={ + + {i18n.translate( + 'xpack.apm.settings.agentKeys.createAgentKeyButton', + { + defaultMessage: 'Create agent key', + } + )} + + } + /> + ); + } else { + content = ( + + ); } } - if (agentKeys && isEmpty(agentKeys)) { - return ( - - {i18n.translate('xpack.apm.settings.agentKeys.emptyPromptTitle', { - defaultMessage: 'Create your first agent key', - })} - - } - body={ -

- {i18n.translate('xpack.apm.settings.agentKeys.emptyPromptBody', { - defaultMessage: - 'Create agent keys to authorize requests to the APM Server.', - })} -

- } - actions={ - - {i18n.translate( - 'xpack.apm.settings.agentKeys.createAgentKeyButton', - { - defaultMessage: 'Create agent key', - } - )} - - } - /> - ); - } - - if (agentKeys && !isEmpty(agentKeys)) { - return ( - - ); - } - - return null; + return ( + + + {i18n.translate('xpack.apm.settings.agentKeys.descriptionText', { + defaultMessage: + 'View and delete agent keys. An agent key sends requests on behalf of a user.', + })} + + + + + +

+ {i18n.translate('xpack.apm.settings.agentKeys.title', { + defaultMessage: 'Agent keys', + })} +

+
+
+ {areApiKeysEnabled && canManage && ( + + setIsFlyoutVisible(true)} + fill={true} + iconType="plusInCircleFilled" + > + {i18n.translate( + 'xpack.apm.settings.agentKeys.createAgentKeyButton', + { + defaultMessage: 'Create agent key', + } + )} + + + )} +
+ + {createdAgentKey && ( + + )} + {isFlyoutVisible && ( + { + setIsFlyoutVisible(false); + }} + onSuccess={(agentKey: CreateApiKeyResponse) => { + setCreatedAgentKey(agentKey); + setIsFlyoutVisible(false); + refetchAgentKeys(); + }} + onError={(keyName: string) => { + toasts.addDanger( + i18n.translate('xpack.apm.settings.agentKeys.crate.failed', { + defaultMessage: 'Error creating agent key "{keyName}"', + values: { keyName }, + }) + ); + setIsFlyoutVisible(false); + }} + /> + )} + {content} +
+ ); } diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index 3a439df245609..d62cca4e07d45 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -54,6 +54,8 @@ import { getLazyApmAgentsTabExtension } from './components/fleet_integration/laz import { getLazyAPMPolicyCreateExtension } from './components/fleet_integration/lazy_apm_policy_create_extension'; import { getLazyAPMPolicyEditExtension } from './components/fleet_integration/lazy_apm_policy_edit_extension'; import { featureCatalogueEntry } from './featureCatalogueEntry'; +import type { SecurityPluginStart } from '../../security/public'; + export type ApmPluginSetup = ReturnType; export type ApmPluginStart = void; @@ -81,6 +83,7 @@ export interface ApmPluginStartDeps { triggersActionsUi: TriggersAndActionsUIPublicPluginStart; observability: ObservabilityPublicStart; fleet?: FleetStart; + security?: SecurityPluginStart; } const servicesTitle = i18n.translate('xpack.apm.navigation.servicesTitle', { diff --git a/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts b/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts new file mode 100644 index 0000000000000..671de6658237e --- /dev/null +++ b/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts @@ -0,0 +1,138 @@ +/* + * 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 Boom from '@hapi/boom'; +import { ApmPluginRequestHandlerContext } from '../typings'; +import { CreateApiKeyResponse } from '../../../common/agent_key_types'; + +const enum PrivilegeType { + SOURCEMAP = 'sourcemap:write', + EVENT = 'event:write', + AGENT_CONFIG = 'config_agent:read', +} + +interface SecurityHasPrivilegesResponse { + application: { + apm: { + '-': { + [PrivilegeType.SOURCEMAP]: boolean; + [PrivilegeType.EVENT]: boolean; + [PrivilegeType.AGENT_CONFIG]: boolean; + }; + }; + }; + has_all_requested: boolean; + username: string; +} + +export async function createAgentKey({ + context, + requestBody, +}: { + context: ApmPluginRequestHandlerContext; + requestBody: { + name: string; + sourcemap?: boolean; + event?: boolean; + agentConfig?: boolean; + }; +}) { + // Elasticsearch will allow a user without the right apm privileges to create API keys, but the keys won't validate + // check first whether the user has the right privileges, and bail out early if not + const { + body: { application, username, has_all_requested: hasRequiredPrivileges }, + } = await context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges( + { + body: { + application: [ + { + application: 'apm', + privileges: [ + PrivilegeType.SOURCEMAP, + PrivilegeType.EVENT, + PrivilegeType.AGENT_CONFIG, + ], + resources: ['-'], + }, + ], + }, + } + ); + + if (!hasRequiredPrivileges) { + const missingPrivileges = Object.entries(application.apm['-']) + .filter((x) => !x[1]) + .map((x) => x[0]) + .join(', '); + const error = `${username} is missing the following requested privilege(s): ${missingPrivileges}.\ + You might try with the superuser, or add the APM application privileges to the role of the authenticated user, eg.: + PUT /_security/role/my_role { + ... + "applications": [{ + "application": "apm", + "privileges": ["sourcemap:write", "event:write", "config_agent:read"], + "resources": ["*"] + }], + ... + }`; + throw Boom.internal(error); + } + + const { name = 'apm-key', sourcemap, event, agentConfig } = requestBody; + + let privileges: PrivilegeType[] = []; + if (!sourcemap && !event && !agentConfig) { + privileges = [ + PrivilegeType.SOURCEMAP, + PrivilegeType.EVENT, + PrivilegeType.AGENT_CONFIG, + ]; + } + + if (sourcemap) { + privileges.push(PrivilegeType.SOURCEMAP); + } + + if (event) { + privileges.push(PrivilegeType.EVENT); + } + + if (agentConfig) { + privileges.push(PrivilegeType.AGENT_CONFIG); + } + + const body = { + name, + metadata: { + application: 'apm', + }, + role_descriptors: { + apm: { + cluster: [], + index: [], + applications: [ + { + application: 'apm', + privileges, + resources: ['*'], + }, + ], + }, + }, + }; + + const { body: agentKey } = + await context.core.elasticsearch.client.asCurrentUser.security.createApiKey( + { + body, + } + ); + + return { + agentKey, + }; +} diff --git a/x-pack/plugins/apm/server/routes/agent_keys/route.ts b/x-pack/plugins/apm/server/routes/agent_keys/route.ts index e5f40205b2912..44bbb22e703b5 100644 --- a/x-pack/plugins/apm/server/routes/agent_keys/route.ts +++ b/x-pack/plugins/apm/server/routes/agent_keys/route.ts @@ -8,11 +8,13 @@ import Boom from '@hapi/boom'; import { i18n } from '@kbn/i18n'; import * as t from 'io-ts'; +import { toBooleanRt } from '@kbn/io-ts-utils/to_boolean_rt'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { createApmServerRouteRepository } from '../apm_routes/create_apm_server_route_repository'; import { getAgentKeys } from './get_agent_keys'; import { getAgentKeysPrivileges } from './get_agent_keys_privileges'; import { invalidateAgentKey } from './invalidate_agent_key'; +import { createAgentKey } from './create_agent_key'; const agentKeysRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/agent_keys', @@ -74,10 +76,40 @@ const invalidateAgentKeyRoute = createApmServerRoute({ }, }); +const createAgentKeyRoute = createApmServerRoute({ + endpoint: 'POST /apm/agent_keys', + options: { tags: ['access:apm', 'access:apm_write'] }, + params: t.type({ + body: t.intersection([ + t.partial({ + sourcemap: toBooleanRt, + event: toBooleanRt, + agentConfig: toBooleanRt, + }), + t.type({ + name: t.string, + }), + ]), + }), + handler: async (resources) => { + const { context, params } = resources; + + const { body: requestBody } = params; + + const agentKey = await createAgentKey({ + context, + requestBody, + }); + + return agentKey; + }, +}); + export const agentKeysRouteRepository = createApmServerRouteRepository() .add(agentKeysRoute) .add(agentKeysPrivilegesRoute) - .add(invalidateAgentKeyRoute); + .add(invalidateAgentKeyRoute) + .add(createAgentKeyRoute); const SECURITY_REQUIRED_MESSAGE = i18n.translate( 'xpack.apm.api.apiKeys.securityRequired', From ae64f897ae9406818ed2a25f3cc8fb49a1067680 Mon Sep 17 00:00:00 2001 From: gbamparop Date: Fri, 3 Dec 2021 17:22:49 +0000 Subject: [PATCH 02/15] Add click handler to the empty prompt button --- .../apm/public/components/app/Settings/agent_keys/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx index 89c348c5646b2..3e2298fcffe6e 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx @@ -138,7 +138,11 @@ export function AgentKeys() {

} actions={ - + setIsFlyoutVisible(true)} + fill={true} + iconType="plusInCircleFilled" + > {i18n.translate( 'xpack.apm.settings.agentKeys.createAgentKeyButton', { From 8f55489ff6c255189a14808e4a0abab2e0f10f8b Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Mon, 6 Dec 2021 13:53:20 +0200 Subject: [PATCH 03/15] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Casper Hübertz --- .../app/Settings/agent_keys/create_agent_key.tsx | 6 +++--- .../agent_keys/create_agent_key/agent_key_callout.tsx | 2 +- .../public/components/app/Settings/agent_keys/index.tsx | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx index e7fcefc62ac80..87b65462436ed 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx @@ -161,7 +161,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { children: i18n.translate( 'xpack.apm.settings.agentKeys.createKeyFlyout.privilegesFieldset', { - defaultMessage: 'Assign privileges to the agent key', + defaultMessage: 'Assign privileges', } ), }} @@ -171,7 +171,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { 'xpack.apm.settings.agentKeys.createKeyFlyout.agentConfigHelpText', { defaultMessage: - 'Required for agents to read Agent configuration remotely.', + 'Required for agents to read agent configuration remotely.', } )} > @@ -187,7 +187,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { helpText={i18n.translate( 'xpack.apm.settings.agentKeys.createKeyFlyout.ingestAgentEvents', { - defaultMessage: 'Required for ingesting Agent events.', + defaultMessage: 'Required for ingesting events.', } )} > diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx index a67b3821120fd..ace4ab02c38f5 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx @@ -27,7 +27,7 @@ export function AgentKeyCallOut({ name, token }: Props) { title={i18n.translate( 'xpack.apm.settings.agentKeys.copyAgentKeyField.title', { - defaultMessage: 'Created agent key "{name}"', + defaultMessage: 'Created "{name}" key', values: { name }, } )} diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx index 3e2298fcffe6e..a63d2e0af9908 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx @@ -125,7 +125,7 @@ export function AgentKeys() { title={

{i18n.translate('xpack.apm.settings.agentKeys.emptyPromptTitle', { - defaultMessage: 'Create your first agent key', + defaultMessage: 'Create your first key', })}

} @@ -133,7 +133,7 @@ export function AgentKeys() {

{i18n.translate('xpack.apm.settings.agentKeys.emptyPromptBody', { defaultMessage: - 'Create agent keys to authorize requests to the APM Server.', + 'Create keys to authorize agent requests to the APM Server.', })}

} @@ -141,7 +141,7 @@ export function AgentKeys() { setIsFlyoutVisible(true)} fill={true} - iconType="plusInCircleFilled" + iconType="plusInCircle" > {i18n.translate( 'xpack.apm.settings.agentKeys.createAgentKeyButton', @@ -187,7 +187,7 @@ export function AgentKeys() { setIsFlyoutVisible(true)} fill={true} - iconType="plusInCircleFilled" + iconType="plusInCircle" > {i18n.translate( 'xpack.apm.settings.agentKeys.createAgentKeyButton', From de2585eb4de0a9c909dce79e6eee29033e366965 Mon Sep 17 00:00:00 2001 From: gbamparop Date: Mon, 6 Dec 2021 15:52:59 +0200 Subject: [PATCH 04/15] Move agent keys tab after agent configuration --- .../routing/templates/settings_template.tsx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/apm/public/components/routing/templates/settings_template.tsx b/x-pack/plugins/apm/public/components/routing/templates/settings_template.tsx index ec8366dfb36b4..229f34f7857ad 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/settings_template.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/settings_template.tsx @@ -18,11 +18,11 @@ import { getLegacyApmHref } from '../../shared/Links/apm/APMLink'; type Tab = NonNullable[0] & { key: | 'agent-configurations' + | 'agent-keys' | 'anomaly-detection' | 'apm-indices' | 'customize-ui' - | 'schema' - | 'agent-keys'; + | 'schema'; hidden?: boolean; }; @@ -76,6 +76,17 @@ function getTabs({ search, }), }, + { + key: 'agent-keys', + label: i18n.translate('xpack.apm.settings.agentKeys', { + defaultMessage: 'Agent Keys', + }), + href: getLegacyApmHref({ + basePath, + path: `/settings/agent-keys`, + search, + }), + }, { key: 'anomaly-detection', label: i18n.translate('xpack.apm.settings.anomalyDetection', { @@ -117,17 +128,6 @@ function getTabs({ }), href: getLegacyApmHref({ basePath, path: `/settings/schema`, search }), }, - { - key: 'agent-keys', - label: i18n.translate('xpack.apm.settings.agentKeys', { - defaultMessage: 'Agent Keys', - }), - href: getLegacyApmHref({ - basePath, - path: `/settings/agent-keys`, - search, - }), - }, ]; return tabs From 2d30f02f16ca23d5abfeee22f166241423338a81 Mon Sep 17 00:00:00 2001 From: gbamparop Date: Mon, 6 Dec 2021 15:56:56 +0200 Subject: [PATCH 05/15] Hide the 'create agent' button when there are no keys / Remove the callout when invalidating a key --- .../app/Settings/agent_keys/agent_keys_table.tsx | 6 +++--- .../public/components/app/Settings/agent_keys/index.tsx | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/agent_keys_table.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/agent_keys_table.tsx index 4a05f38d8e505..f49264242e63f 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/agent_keys_table.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/agent_keys_table.tsx @@ -18,10 +18,10 @@ import { ConfirmDeleteModal } from './confirm_delete_modal'; interface Props { agentKeys: ApiKey[]; - refetchAgentKeys: () => void; + onKeyDelete: () => void; } -export function AgentKeysTable({ agentKeys, refetchAgentKeys }: Props) { +export function AgentKeysTable({ agentKeys, onKeyDelete }: Props) { const [agentKeyToBeDeleted, setAgentKeyToBeDeleted] = useState(); const columns: Array> = [ @@ -159,7 +159,7 @@ export function AgentKeysTable({ agentKeys, refetchAgentKeys }: Props) { agentKey={agentKeyToBeDeleted} onConfirm={() => { setAgentKeyToBeDeleted(undefined); - refetchAgentKeys(); + onKeyDelete(); }} /> )} diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx index a63d2e0af9908..abde580e43b19 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx @@ -157,7 +157,10 @@ export function AgentKeys() { content = ( { + setCreatedAgentKey(undefined); + refetchAgentKeys(); + }} /> ); } @@ -182,7 +185,7 @@ export function AgentKeys() { - {areApiKeysEnabled && canManage && ( + {areApiKeysEnabled && canManage && !isEmpty(agentKeys) && ( setIsFlyoutVisible(true)} From 09ed6bf7fac09c5436cba20ed3898aeb2b823647 Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Tue, 7 Dec 2021 12:15:46 +0200 Subject: [PATCH 06/15] Change privileges array to const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com> --- .../apm/server/routes/agent_keys/create_agent_key.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts b/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts index 671de6658237e..c30f0ed53efdb 100644 --- a/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts +++ b/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts @@ -84,13 +84,13 @@ export async function createAgentKey({ const { name = 'apm-key', sourcemap, event, agentConfig } = requestBody; - let privileges: PrivilegeType[] = []; + const privileges: PrivilegeType[] = []; if (!sourcemap && !event && !agentConfig) { - privileges = [ + privileges.push( PrivilegeType.SOURCEMAP, PrivilegeType.EVENT, PrivilegeType.AGENT_CONFIG, - ]; + ); } if (sourcemap) { From 7fc9deb7745ba748265debb33e3ded1cd7ef8707 Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Tue, 7 Dec 2021 12:19:08 +0200 Subject: [PATCH 07/15] Toggle sourcemapChecked state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com> --- .../components/app/Settings/agent_keys/create_agent_key.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx index 87b65462436ed..3c3fe6a5f960e 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx @@ -211,7 +211,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { id={htmlIdGenerator()()} label="sourcemap:write" checked={sourcemapChecked} - onChange={() => setSourcemapChecked(!sourcemapChecked)} + onChange={() => setSourcemapChecked(state => !state)} /> From 98330785af8eb2738f67d09528878717e7797fd9 Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Tue, 7 Dec 2021 12:32:07 +0200 Subject: [PATCH 08/15] Update x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com> --- .../components/app/Settings/agent_keys/create_agent_key.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx index 3c3fe6a5f960e..11522f820e942 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx @@ -73,9 +73,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { const createAgentKeyTitle = i18n.translate( 'xpack.apm.settings.agentKeys.createKeyFlyout.createAgentKey', - { - defaultMessage: 'Create agent key', - } + { defaultMessage: 'Create agent key', } ); const createAgentKey = async () => { From abcb497816e576d2411b2656c0aa671c52ffcc4d Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Tue, 7 Dec 2021 12:32:16 +0200 Subject: [PATCH 09/15] Update x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com> --- .../components/app/Settings/agent_keys/create_agent_key.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx index 11522f820e942..4b6231aea3dad 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx @@ -116,9 +116,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { {username} From 37b89126e7ce1aedeadf7bdf90abc2e737a8ccbd Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Tue, 7 Dec 2021 12:57:33 +0200 Subject: [PATCH 10/15] Toggle agentConfigChecked state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com> --- .../components/app/Settings/agent_keys/create_agent_key.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx index 4b6231aea3dad..03ba450a177eb 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx @@ -175,7 +175,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { id={htmlIdGenerator()()} label="config_agent:read" checked={agentConfigChecked} - onChange={() => setAgentConfigChecked(!agentConfigChecked)} + onChange={() => setAgentConfigChecked(state => !state) /> From 7f0d2af24b18b4880968f8cd1fb6d995d16bd16e Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Tue, 7 Dec 2021 12:58:16 +0200 Subject: [PATCH 11/15] Toggle eventWriteChecked state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com> --- .../components/app/Settings/agent_keys/create_agent_key.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx index 03ba450a177eb..fa7976e09b2ca 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx @@ -191,7 +191,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { id={htmlIdGenerator()()} label="event:write" checked={eventWriteChecked} - onChange={() => setEventWriteChecked(!eventWriteChecked)} + onChange={() => setEventWriteChecked(state => !state)} /> From c3b1afba0a7f69e2c35ad0702a30422cbfd46eab Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Tue, 7 Dec 2021 13:00:01 +0200 Subject: [PATCH 12/15] Change onCancel prop type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com> --- .../components/app/Settings/agent_keys/create_agent_key.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx index fa7976e09b2ca..f2823285bff91 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx @@ -33,7 +33,7 @@ import { ApmPluginStartDeps } from '../../../../plugin'; import { CreateApiKeyResponse } from '../../../../../common/agent_key_types'; interface Props { - onCancel(): void; + onCancel: () => void; onSuccess: (agentKey: CreateApiKeyResponse) => void; onError: (keyName: string) => void; } From 5d906de3630d57efc7c63ea085d739b69402d3c6 Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Tue, 7 Dec 2021 13:01:02 +0200 Subject: [PATCH 13/15] Update x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com> --- .../components/app/Settings/agent_keys/create_agent_key.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx index f2823285bff91..40b25a58a5ac5 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx @@ -54,9 +54,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { const isFormInvalid = isEmpty(keyName); const formError = i18n.translate( 'xpack.apm.settings.agentKeys.createKeyFlyout.name.placeholder', - { - defaultMessage: 'Enter a name', - } + { defaultMessage: 'Enter a name', } ); useEffect(() => { From cf7052b70eb253a4ec678f84edccb41496c34652 Mon Sep 17 00:00:00 2001 From: gbamparop Date: Tue, 7 Dec 2021 14:12:52 +0200 Subject: [PATCH 14/15] Address PR feedback --- .../Settings/agent_keys/create_agent_key.tsx | 26 ++++++++++--------- .../create_agent_key/agent_key_callout.tsx | 6 ++--- .../routes/agent_keys/create_agent_key.ts | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx index 40b25a58a5ac5..5803e5a2a75a8 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key.tsx @@ -51,10 +51,12 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { const [eventWriteChecked, setEventWriteChecked] = useState(true); const [sourcemapChecked, setSourcemapChecked] = useState(true); - const isFormInvalid = isEmpty(keyName); + const isInputInvalid = isEmpty(keyName); + const isFormInvalid = formTouched && isInputInvalid; + const formError = i18n.translate( 'xpack.apm.settings.agentKeys.createKeyFlyout.name.placeholder', - { defaultMessage: 'Enter a name', } + { defaultMessage: 'Enter a name' } ); useEffect(() => { @@ -71,12 +73,12 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { const createAgentKeyTitle = i18n.translate( 'xpack.apm.settings.agentKeys.createKeyFlyout.createAgentKey', - { defaultMessage: 'Create agent key', } + { defaultMessage: 'Create agent key' } ); const createAgentKey = async () => { setFormTouched(true); - if (isFormInvalid) { + if (isInputInvalid) { return; } @@ -109,12 +111,12 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { - + {username && ( {username} @@ -133,7 +135,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { defaultMessage: 'What is this key used for?', } )} - isInvalid={formTouched && isFormInvalid} + isInvalid={isFormInvalid} error={formError} > setKeyName(e.target.value)} - isInvalid={formTouched && isFormInvalid} + isInvalid={isFormInvalid} onBlur={() => setFormTouched(true)} /> @@ -173,7 +175,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { id={htmlIdGenerator()()} label="config_agent:read" checked={agentConfigChecked} - onChange={() => setAgentConfigChecked(state => !state) + onChange={() => setAgentConfigChecked((state) => !state)} /> @@ -189,7 +191,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { id={htmlIdGenerator()()} label="event:write" checked={eventWriteChecked} - onChange={() => setEventWriteChecked(state => !state)} + onChange={() => setEventWriteChecked((state) => !state)} /> @@ -205,7 +207,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { id={htmlIdGenerator()()} label="sourcemap:write" checked={sourcemapChecked} - onChange={() => setSourcemapChecked(state => !state)} + onChange={() => setSourcemapChecked((state) => !state)} /> @@ -230,7 +232,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { fill={true} onClick={createAgentKey} type="submit" - disabled={formTouched && isFormInvalid} + disabled={isFormInvalid} > {createAgentKeyTitle} diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx index ace4ab02c38f5..db313e35a0229 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/create_agent_key/agent_key_callout.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { Fragment } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSpacer, @@ -22,7 +22,7 @@ interface Props { export function AgentKeyCallOut({ name, token }: Props) { return ( - + <> - + ); } diff --git a/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts b/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts index c30f0ed53efdb..02207dad32efb 100644 --- a/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts +++ b/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts @@ -89,7 +89,7 @@ export async function createAgentKey({ privileges.push( PrivilegeType.SOURCEMAP, PrivilegeType.EVENT, - PrivilegeType.AGENT_CONFIG, + PrivilegeType.AGENT_CONFIG ); } From 3571120f6bad99d1b38d87df60c0710e1b693c52 Mon Sep 17 00:00:00 2001 From: gbamparop Date: Tue, 7 Dec 2021 15:31:00 +0200 Subject: [PATCH 15/15] Move Agent keys page content to another component --- .../app/Settings/agent_keys/index.tsx | 227 ++++++++++-------- 1 file changed, 129 insertions(+), 98 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx index abde580e43b19..8fb4ede96a819 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_keys/index.tsx @@ -25,6 +25,7 @@ import { CreateAgentKeyFlyout } from './create_agent_key'; import { AgentKeyCallOut } from './create_agent_key/agent_key_callout'; import { CreateApiKeyResponse } from '../../../../../common/agent_key_types'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; +import { ApiKey } from '../../../../../../security/common/model'; const INITIAL_DATA = { areApiKeysEnabled: false, @@ -68,103 +69,6 @@ export function AgentKeys() { ); const agentKeys = data?.agentKeys; - const isLoading = - privilegesStatus === FETCH_STATUS.LOADING || - status === FETCH_STATUS.LOADING; - - const requestFailed = - privilegesStatus === FETCH_STATUS.FAILURE || - status === FETCH_STATUS.FAILURE; - - let content = null; - - if (!agentKeys) { - if (isLoading) { - content = ( - } - titleSize="xs" - title={ -

- {i18n.translate( - 'xpack.apm.settings.agentKeys.agentKeysLoadingPromptTitle', - { - defaultMessage: 'Loading Agent keys...', - } - )} -

- } - /> - ); - } else if (requestFailed) { - content = ( - - {i18n.translate( - 'xpack.apm.settings.agentKeys.agentKeysErrorPromptTitle', - { - defaultMessage: 'Could not load agent keys.', - } - )} - - } - /> - ); - } else if (!canManage) { - content = ; - } else if (!areApiKeysEnabled) { - content = ; - } - } else { - if (isEmpty(agentKeys)) { - content = ( - - {i18n.translate('xpack.apm.settings.agentKeys.emptyPromptTitle', { - defaultMessage: 'Create your first key', - })} - - } - body={ -

- {i18n.translate('xpack.apm.settings.agentKeys.emptyPromptBody', { - defaultMessage: - 'Create keys to authorize agent requests to the APM Server.', - })} -

- } - actions={ - setIsFlyoutVisible(true)} - fill={true} - iconType="plusInCircle" - > - {i18n.translate( - 'xpack.apm.settings.agentKeys.createAgentKeyButton', - { - defaultMessage: 'Create agent key', - } - )} - - } - /> - ); - } else { - content = ( - { - setCreatedAgentKey(undefined); - refetchAgentKeys(); - }} - /> - ); - } - } return ( @@ -230,7 +134,134 @@ export function AgentKeys() { }} /> )} - {content} + { + setCreatedAgentKey(undefined); + refetchAgentKeys(); + }} + onCreateAgentClick={() => setIsFlyoutVisible(true)} + /> ); } + +function AgentKeysContent({ + loading, + requestFailed, + canManage, + areApiKeysEnabled, + agentKeys, + onKeyDelete, + onCreateAgentClick, +}: { + loading: boolean; + requestFailed: boolean; + canManage: boolean; + areApiKeysEnabled: boolean; + agentKeys?: ApiKey[]; + onKeyDelete: () => void; + onCreateAgentClick: () => void; +}) { + if (!agentKeys) { + if (loading) { + return ( + } + titleSize="xs" + title={ +

+ {i18n.translate( + 'xpack.apm.settings.agentKeys.agentKeysLoadingPromptTitle', + { + defaultMessage: 'Loading Agent keys...', + } + )} +

+ } + /> + ); + } + + if (requestFailed) { + return ( + + {i18n.translate( + 'xpack.apm.settings.agentKeys.agentKeysErrorPromptTitle', + { + defaultMessage: 'Could not load agent keys.', + } + )} + + } + /> + ); + } + + if (!canManage) { + return ; + } + + if (!areApiKeysEnabled) { + return ; + } + } + + if (agentKeys && isEmpty(agentKeys)) { + return ( + + {i18n.translate('xpack.apm.settings.agentKeys.emptyPromptTitle', { + defaultMessage: 'Create your first key', + })} + + } + body={ +

+ {i18n.translate('xpack.apm.settings.agentKeys.emptyPromptBody', { + defaultMessage: + 'Create keys to authorize agent requests to the APM Server.', + })} +

+ } + actions={ + + {i18n.translate( + 'xpack.apm.settings.agentKeys.createAgentKeyButton', + { + defaultMessage: 'Create agent key', + } + )} + + } + /> + ); + } + + if (agentKeys && !isEmpty(agentKeys)) { + return ( + + ); + } + + return null; +}