Skip to content

Commit

Permalink
[APM] Add Agent Keys in APM settings - Agent key table (#119543)
Browse files Browse the repository at this point in the history
* Add agent keys table

* Add support for invalidating keys

Co-authored-by: Søren Louv-Jansen <[email protected]>
Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
3 people authored Nov 30, 2021
1 parent 3bde2ec commit d1bc264
Show file tree
Hide file tree
Showing 12 changed files with 772 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* 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 } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiInMemoryTable,
EuiBasicTableColumn,
EuiInMemoryTableProps,
} from '@elastic/eui';
import { TimestampTooltip } from '../../../shared/TimestampTooltip';
import { ApiKey } from '../../../../../../security/common/model';
import { ConfirmDeleteModal } from './confirm_delete_modal';

interface Props {
agentKeys: ApiKey[];
refetchAgentKeys: () => void;
}

export function AgentKeysTable({ agentKeys, refetchAgentKeys }: Props) {
const [agentKeyToBeDeleted, setAgentKeyToBeDeleted] = useState<ApiKey>();

const columns: Array<EuiBasicTableColumn<ApiKey>> = [
{
field: 'name',
name: i18n.translate(
'xpack.apm.settings.agentKeys.table.nameColumnName',
{
defaultMessage: 'Name',
}
),
sortable: true,
},
{
field: 'username',
name: i18n.translate(
'xpack.apm.settings.agentKeys.table.userNameColumnName',
{
defaultMessage: 'User',
}
),
sortable: true,
},
{
field: 'realm',
name: i18n.translate(
'xpack.apm.settings.agentKeys.table.realmColumnName',
{
defaultMessage: 'Realm',
}
),
sortable: true,
},
{
field: 'creation',
name: i18n.translate(
'xpack.apm.settings.agentKeys.table.creationColumnName',
{
defaultMessage: 'Created',
}
),
dataType: 'date',
sortable: true,
mobileOptions: {
show: false,
},
render: (date: number) => <TimestampTooltip time={date} />,
},
{
actions: [
{
name: i18n.translate(
'xpack.apm.settings.agentKeys.table.deleteActionTitle',
{
defaultMessage: 'Delete',
}
),
description: i18n.translate(
'xpack.apm.settings.agentKeys.table.deleteActionDescription',
{
defaultMessage: 'Delete this agent key',
}
),
icon: 'trash',
color: 'danger',
type: 'icon',
onClick: (agentKey: ApiKey) => setAgentKeyToBeDeleted(agentKey),
},
],
},
];

const search: EuiInMemoryTableProps<ApiKey>['search'] = {
box: {
incremental: true,
},
filters: [
{
type: 'field_value_selection',
field: 'username',
name: i18n.translate(
'xpack.apm.settings.agentKeys.table.userFilterLabel',
{
defaultMessage: 'User',
}
),
multiSelect: 'or',
operator: 'exact',
options: Object.keys(
agentKeys.reduce((acc: Record<string, boolean>, { username }) => {
acc[username] = true;
return acc;
}, {})
).map((value) => ({ value })),
},
{
type: 'field_value_selection',
field: 'realm',
name: i18n.translate(
'xpack.apm.settings.agentKeys.table.realmFilterLabel',
{
defaultMessage: 'Realm',
}
),
multiSelect: 'or',
operator: 'exact',
options: Object.keys(
agentKeys.reduce((acc: Record<string, boolean>, { realm }) => {
acc[realm] = true;
return acc;
}, {})
).map((value) => ({ value })),
},
],
};

return (
<React.Fragment>
<EuiInMemoryTable
tableCaption={i18n.translate(
'xpack.apm.settings.agentKeys.tableCaption',
{
defaultMessage: 'Agent keys',
}
)}
items={agentKeys ?? []}
columns={columns}
pagination={true}
search={search}
sorting={true}
/>
{agentKeyToBeDeleted && (
<ConfirmDeleteModal
onCancel={() => setAgentKeyToBeDeleted(undefined)}
agentKey={agentKeyToBeDeleted}
onConfirm={() => {
setAgentKeyToBeDeleted(undefined);
refetchAgentKeys();
}}
/>
)}
</React.Fragment>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* 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 } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiConfirmModal } from '@elastic/eui';
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';
import { callApmApi } from '../../../../services/rest/createCallApmApi';
import { ApiKey } from '../../../../../../security/common/model';

interface Props {
agentKey: ApiKey;
onCancel: () => void;
onConfirm: () => void;
}

export function ConfirmDeleteModal({ agentKey, onCancel, onConfirm }: Props) {
const [isDeleting, setIsDeleting] = useState(false);
const { toasts } = useApmPluginContext().core.notifications;
const { id, name } = agentKey;

const deleteAgentKey = async () => {
try {
await callApmApi({
endpoint: 'POST /internal/apm/api_key/invalidate',
signal: null,
params: {
body: { id },
},
});
toasts.addSuccess(
i18n.translate('xpack.apm.settings.agentKeys.invalidate.succeeded', {
defaultMessage: 'Deleted agent key "{name}"',
values: { name },
})
);
} catch (error) {
toasts.addDanger(
i18n.translate('xpack.apm.settings.agentKeys.invalidate.failed', {
defaultMessage: 'Error deleting agent key "{name}"',
values: { name },
})
);
}
};

return (
<EuiConfirmModal
title={i18n.translate(
'xpack.apm.settings.agentKeys.deleteConfirmModal.title',
{
defaultMessage: 'Delete agent key "{name}"?',
values: { name },
}
)}
onCancel={onCancel}
onConfirm={async () => {
setIsDeleting(true);
await deleteAgentKey();
setIsDeleting(false);
onConfirm();
}}
cancelButtonText={i18n.translate(
'xpack.apm.settings.agentKeys.deleteConfirmModal.cancel',
{
defaultMessage: 'Cancel',
}
)}
confirmButtonText={i18n.translate(
'xpack.apm.settings.agentKeys.deleteConfirmModal.delete',
{
defaultMessage: 'Delete',
}
)}
confirmButtonDisabled={isDeleting}
buttonColor="danger"
defaultFocusedButton="confirm"
/>
);
}
Loading

0 comments on commit d1bc264

Please sign in to comment.