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] Handle 403 and 404 in Status and Edit routes and fix breadcrumbs #35444

Merged
merged 11 commits into from
Apr 23, 2019
7 changes: 7 additions & 0 deletions x-pack/plugins/watcher/public/components/page_error/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* 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.
*/

export { getPageErrorCode, PageError } from './page_error';
38 changes: 38 additions & 0 deletions x-pack/plugins/watcher/public/components/page_error/page_error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 React from 'react';

import { PageErrorNotExist } from './page_error_not_exist';
import { PageErrorForbidden } from './page_error_forbidden';

export function getPageErrorCode(errorOrErrors: any) {
const errors = Array.isArray(errorOrErrors) ? errorOrErrors : [errorOrErrors];
const firstError = errors.find((error: any) => {
if (error) {
return [403, 404].includes(error.status);
}

return false;
});

if (firstError) {
return firstError.status;
}
}

export function PageError({ errorCode, id }: { errorCode?: any; id?: any }) {
switch (errorCode) {
case 404:
return <PageErrorNotExist id={id} />;

case 403:
default:
return <PageErrorForbidden />;
}

return null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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 React from 'react';

import { EuiEmptyPrompt } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';

export function PageErrorForbidden() {
return (
<EuiEmptyPrompt
iconType="securityApp"
iconColor={undefined}
title={
<h1>
<FormattedMessage
id="xpack.watcher.pageErrorForbidden.title"
defaultMessage="You don't have privileges to use Watcher"
/>
</h1>
}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 React from 'react';

import { EuiEmptyPrompt } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';

export function PageErrorNotExist({ id }: { id: any }) {
return (
<EuiEmptyPrompt
iconType="search"
iconColor="primary"
title={
<h1>
<FormattedMessage
id="xpack.watcher.pageErrorNotExist.title"
defaultMessage="Couldn't find watch"
/>
</h1>
}
body={
<p>
<FormattedMessage
id="xpack.watcher.pageErrorNotExist.description"
defaultMessage="A watch with ID '{id}' could not be found."
values={{ id }}
/>
</p>
}
/>
);
}
71 changes: 45 additions & 26 deletions x-pack/plugins/watcher/public/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,56 +11,69 @@ import { __await } from 'tslib';
import chrome from 'ui/chrome';
import { ROUTES } from '../../common/constants';
import { BaseWatch, ExecutedWatchDetails } from '../../common/types/watch_types';
import { useRequest } from './use_request';

let httpClient: ng.IHttpService;

export const setHttpClient = (anHttpClient: ng.IHttpService) => {
httpClient = anHttpClient;
};

export const getHttpClient = () => {
return httpClient;
};

let savedObjectsClient: any;

export const setSavedObjectsClient = (aSavedObjectsClient: any) => {
savedObjectsClient = aSavedObjectsClient;
};

export const getSavedObjectsClient = () => {
return savedObjectsClient;
};

const basePath = chrome.addBasePath(ROUTES.API_ROOT);
export const fetchWatches = async () => {
const {
data: { watches },
} = await getHttpClient().get(`${basePath}/watches`);
return watches.map((watch: any) => {
return Watch.fromUpstreamJson(watch);

export const loadWatches = (interval: number) => {
return useRequest({
path: `${basePath}/watches`,
method: 'get',
interval,
processData: ({ watches }: { watches: any }) =>
watches.map((watch: any) => Watch.fromUpstreamJson(watch)),
});
};

export const fetchWatchDetail = async (id: string) => {
const {
data: { watch },
} = await getHttpClient().get(`${basePath}/watch/${id}`);
return Watch.fromUpstreamJson(watch);
export const loadWatchDetail = (id: string) => {
return useRequest({
path: `${basePath}/watch/${id}`,
method: 'get',
processData: ({ watch }: { watch: any }) => 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 loadWatchHistory = (id: string, startTime: string) => {
let path = `${basePath}/watch/${id}/history`;

export const fetchWatchHistory = async (id: string, startTime: string) => {
let url = `${basePath}/watch/${id}/history`;
if (startTime) {
url += `?startTime=${startTime}`;
path += `?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;

return useRequest({
path,
method: 'get',
processData: ({ watchHistoryItems: items }: { watchHistoryItems: any }) =>
items.map((historyItem: any) => WatchHistoryItem.fromUpstreamJson(historyItem)),
});
};

export const loadWatchHistoryDetail = (id: string | undefined) => {
return useRequest({
path: !id ? undefined : `${basePath}/history/${id}`,
method: 'get',
processData: ({ watchHistoryItem }: { watchHistoryItem: any }) =>
WatchHistoryItem.fromUpstreamJson(watchHistoryItem),
});
};

Expand Down Expand Up @@ -97,10 +110,12 @@ export const fetchWatch = async (watchId: string) => {
} = await getHttpClient().post(`${basePath}/watches/`, body);
return results;
};

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

export const getMatchingIndices = async (pattern: string) => {
if (!pattern.startsWith('*')) {
pattern = `*${pattern}`;
Expand All @@ -113,23 +128,27 @@ export const getMatchingIndices = async (pattern: string) => {
} = await getHttpClient().post(`${basePath}/indices`, { pattern });
return indices;
};

export const fetchFields = async (indexes: string[]) => {
const {
data: { fields },
} = await getHttpClient().post(`${basePath}/fields`, { indexes });
return fields;
};

export const createWatch = async (watch: BaseWatch) => {
const { data } = await getHttpClient().put(`${basePath}/watch/${watch.id}`, watch.upstreamJson);
return data;
};

export const executeWatch = async (executeWatchDetails: ExecutedWatchDetails, watch: BaseWatch) => {
const { data } = await getHttpClient().put(`${basePath}/watch/execute`, {
executeDetails: executeWatchDetails.upstreamJson,
watch: watch.upstreamJson,
});
return data;
};

export const loadIndexPatterns = async () => {
const { savedObjects } = await getSavedObjectsClient().find({
type: 'index-pattern',
Expand Down
65 changes: 24 additions & 41 deletions x-pack/plugins/watcher/public/lib/breadcrumbs.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,29 @@
* you may not use this file except in compliance with the Elastic License.
*/

import chrome from 'ui/chrome';
import { i18n } from '@kbn/i18n';

import { MANAGEMENT_BREADCRUMB } from 'ui/management';

const uiSettings = chrome.getUiSettingsClient();

export function getWatchListBreadcrumbs() {
return [
MANAGEMENT_BREADCRUMB,
{
text: i18n.translate('xpack.watcher.list.breadcrumb', {
defaultMessage: 'Watcher'
}),
href: '#/management/elasticsearch/watcher/watches/'
}
];
}

export function getWatchDetailBreadcrumbs($route) {
const watch = $route.current.locals.watch || $route.current.locals.xpackWatch;

return [
...getWatchListBreadcrumbs(),
{
text: !watch.isNew
? watch.name
: i18n.translate('xpack.watcher.create.breadcrumb', { defaultMessage: 'Create' }),
href: '#/management/elasticsearch/watcher/watches/watch/23eebf28-94fd-47e9-ac44-6fee6e427c33'
}
];
}

export function getWatchHistoryBreadcrumbs($route) {
const { watchHistoryItem } = $route.current.locals;

return [
...getWatchDetailBreadcrumbs($route),
{
text: watchHistoryItem.startTime.format(uiSettings.get('dateFormat'))
}
];
}
export const listBreadcrumb = {
text: i18n.translate('xpack.watcher.breadcrumb.listLabel', {
defaultMessage: 'Watcher'
}),
href: '#/management/elasticsearch/watcher/watches/',
};

export const createBreadcrumb = {
text: i18n.translate('xpack.watcher.breadcrumb.createLabel', {
defaultMessage: 'Create',
}),
};

export const editBreadcrumb = {
text: i18n.translate('xpack.watcher.breadcrumb.editLabel', {
defaultMessage: 'Edit',
}),
};

export const statusBreadcrumb = {
text: i18n.translate('xpack.watcher.breadcrumb.statusLabel', {
defaultMessage: 'Status',
}),
};
Loading