Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Watcher history page: port to react #33047

Merged
merged 26 commits into from
Apr 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const DeleteWatchesModal = ({
'xpack.watcher.deleteSelectedWatchesConfirmModal.descriptionText',
{
defaultMessage:
'This will permanently delete {numWatchesToDelete, plural, one {a watch} other {# watches}}. Are you sure?',
'This will permanently delete {numWatchesToDelete, plural, one {a watch} other {# watches}}. You can’t recover a deleted watch.',
values: { numWatchesToDelete },
}
);
Expand Down
47 changes: 47 additions & 0 deletions x-pack/plugins/watcher/public/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Watch } from 'plugins/watcher/models/watch';
import { WatchHistoryItem } from 'plugins/watcher/models/watch_history_item';
import { WatchStatus } from 'plugins/watcher/models/watch_status';

import { __await } from 'tslib';
import chrome from 'ui/chrome';
import { ROUTES } from '../../common/constants';
Expand All @@ -25,6 +28,35 @@ export const fetchWatches = async () => {
return Watch.fromUpstreamJson(watch);
});
};

export const fetchWatchDetail = async (id: string) => {
const {
data: { watch },
} = await getHttpClient().get(`${basePath}/watch/${id}`);
return Watch.fromUpstreamJson(watch);
};

export const fetchWatchHistoryDetail = async (id: string) => {
const {
data: { watchHistoryItem },
} = await getHttpClient().get(`${basePath}/history/${id}`);
const item = WatchHistoryItem.fromUpstreamJson(watchHistoryItem);
return item;
};

export const fetchWatchHistory = async (id: string, startTime: string) => {
let url = `${basePath}/watch/${id}/history`;
if (startTime) {
url += `?startTime=${startTime}`;
}
const result: any = await getHttpClient().get(url);
const items: any = result.data.watchHistoryItems;
return items.map((historyItem: any) => {
const item = WatchHistoryItem.fromUpstreamJson(historyItem);
return item;
});
};

export const deleteWatches = async (watchIds: string[]) => {
const body = {
watchIds,
Expand All @@ -34,6 +66,21 @@ export const deleteWatches = async (watchIds: string[]) => {
} = await getHttpClient().post(`${basePath}/watches/delete`, body);
return results;
};

export const deactivateWatch = async (id: string) => {
const {
data: { watchStatus },
} = await getHttpClient().put(`${basePath}/watch/${id}/deactivate`, null);
return WatchStatus.fromUpstreamJson(watchStatus);
};

export const activateWatch = async (id: string) => {
const {
data: { watchStatus },
} = await getHttpClient().put(`${basePath}/watch/${id}/activate`, null);
return WatchStatus.fromUpstreamJson(watchStatus);
};

export const fetchWatch = async (watchId: string) => {
const body = {
watchId,
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/watcher/public/models/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ declare module 'plugins/watcher/models/watch_history_item' {
export const WatchHistoryItem: any;
}

declare module 'plugins/watcher/models/watch_status' {
export const WatchStatus: any;
}
// TODO: Remove once typescript definitions are in EUI
declare module '@elastic/eui' {
export const EuiCodeEditor: React.SFC<any>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pcsanwald I think this is something that I'd like to reuse for the advanced watcher pages too. Maybe we should move it under /watcher/public/components?

Generally speaking, I'm not sure how we want to approach the file structure. However, right now, it seems we have some common components in there already (e.g., form_errors.tsx, delete_watches_modal.tsx) FYI @bmcconaghy

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah seems like a good candidate for moving up to the common components dir.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we do this after merge, or should I go ahead and do in this PR? I'm fine either way

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another PR is fine I think

* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
import React from 'react';
import { EuiIcon } from '@elastic/eui';
import { WATCH_STATES } from '../../../../../common/constants/watch_states';

const WatchActionStatusUi = ({ intl, watchState }: { intl: InjectedIntl; watchState: string }) => {
const stateToIcon: { [key: string]: JSX.Element } = {
[WATCH_STATES.OK]: <EuiIcon type="check" color="green" />,
[WATCH_STATES.DISABLED]: <EuiIcon type="minusInCircle" color="grey" />,
[WATCH_STATES.FIRING]: <EuiIcon type="play" color="primary" />,
[WATCH_STATES.ERROR]: <EuiIcon type="crossInACircleFilled" color="red" />,
[WATCH_STATES.CONFIG_ERROR]: <EuiIcon type="crossInACircleFilled" color="red" />,
};

return <div>{stateToIcon[watchState]}</div>;
};

export const WatchActionStatus = injectI18n(WatchActionStatusUi);
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import {
EuiFlexGroup,
EuiFlexItem,
EuiInMemoryTable,
EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
import React, { Fragment, useEffect, useState } from 'react';
import { fetchWatchDetail } from '../../../../lib/api';
import { WatchActionStatus } from './watch_action_status';

const WatchDetailUi = ({ intl, watchId }: { intl: InjectedIntl; watchId: string }) => {
const [isWatchesLoading, setIsWatchesLoading] = useState<boolean>(true);
const [actions, setWatchActions] = useState([]);

const pagination = {
initialPageSize: 10,
pageSizeOptions: [10, 50, 100],
};

const columns = [
{
field: 'id',
name: i18n.translate('xpack.watcher.sections.watchDetail.watchTable.actionHeader', {
defaultMessage: 'Action',
}),
sortable: true,
truncateText: true,
render: (action: string) => {
return <EuiText>{action}</EuiText>;
},
},
{
field: 'state',
name: i18n.translate('xpack.watcher.sections.watchDetail.watchTable.stateHeader', {
defaultMessage: 'State',
}),
sortable: true,
truncateText: true,
render: (state: string) => {
return (
<EuiFlexGroup gutterSize="xs" alignItems="center">
<EuiFlexItem grow={false}>
<WatchActionStatus watchState={state} />
</EuiFlexItem>
<EuiFlexItem grow={false} className="watchState__message">
<EuiText>{state}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
},
},
];
const loadWatchActions = async () => {
const loadedWatchActions = await fetchWatchDetail(watchId);
setWatchActions(loadedWatchActions.watchStatus.actionStatuses);
setIsWatchesLoading(false);
};
useEffect(() => {
loadWatchActions();
// only run the first time the component loads
}, []);

return (
<Fragment>
<EuiTitle size="m">
<h1>
<FormattedMessage
id="xpack.watcher.sections.watchDetail.header"
defaultMessage="Current status"
/>
</h1>
</EuiTitle>
<EuiSpacer size="s" />
<EuiInMemoryTable
items={actions}
itemId="id"
columns={columns}
pagination={pagination}
sorting={true}
loading={isWatchesLoading}
message={
<FormattedMessage
id="xpack.watcher.sections.watchDetail.watchTable.noWatchesMessage"
defaultMessage="No current status"
/>
}
/>
</Fragment>
);
};

export const WatchDetail = injectI18n(WatchDetailUi);
Loading