Skip to content

Commit

Permalink
Merge branch 'master' into fix-spaces-job-id-check
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Nov 30, 2020
2 parents ee08f32 + bdf7b88 commit 30ae309
Show file tree
Hide file tree
Showing 56 changed files with 753 additions and 346 deletions.
6 changes: 3 additions & 3 deletions src/plugins/discover/public/application/angular/discover.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise
if (!_.isEqual(newStatePartial, oldStatePartial)) {
$scope.$evalAsync(async () => {
if (oldStatePartial.index !== newStatePartial.index) {
//in case of index switch the route has currently to be reloaded, legacy
//in case of index pattern switch the route has currently to be reloaded, legacy
$route.reload();
return;
}

Expand Down Expand Up @@ -289,8 +290,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise
$scope.state.sort,
config.get(MODIFY_COLUMNS_ON_SWITCH)
);
await replaceUrlAppState(nextAppState);
$route.reload();
await setAppState(nextAppState);
}
};

Expand Down
45 changes: 24 additions & 21 deletions src/plugins/share/server/routes/lib/short_url_assert_valid.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,39 @@

import { shortUrlAssertValid } from './short_url_assert_valid';

const PROTOCOL_ERROR = /^Short url targets cannot have a protocol/;
const HOSTNAME_ERROR = /^Short url targets cannot have a hostname/;
const PATH_ERROR = /^Short url target path must be in the format/;

describe('shortUrlAssertValid()', () => {
const invalid = [
['protocol', 'http://localhost:5601/app/kibana'],
['protocol', 'https://localhost:5601/app/kibana'],
['protocol', 'mailto:[email protected]'],
['protocol', 'javascript:alert("hi")'], // eslint-disable-line no-script-url
['hostname', 'localhost/app/kibana'],
['hostname and port', 'local.host:5601/app/kibana'],
['hostname and auth', 'user:[email protected]/app/kibana'],
['path traversal', '/app/../../not-kibana'],
['deep path', '/app/kibana/foo'],
['deep path', '/app/kibana/foo/bar'],
['base path', '/base/app/kibana'],
['protocol', 'http://localhost:5601/app/kibana', PROTOCOL_ERROR],
['protocol', 'https://localhost:5601/app/kibana', PROTOCOL_ERROR],
['protocol', 'mailto:[email protected]', PROTOCOL_ERROR],
['protocol', 'javascript:alert("hi")', PROTOCOL_ERROR], // eslint-disable-line no-script-url
['hostname', 'localhost/app/kibana', PATH_ERROR], // according to spec, this is not a valid URL -- you cannot specify a hostname without a protocol
['hostname and port', 'local.host:5601/app/kibana', PROTOCOL_ERROR], // parser detects 'local.host' as the protocol
['hostname and auth', 'user:[email protected]/app/kibana', PROTOCOL_ERROR], // parser detects 'user' as the protocol
['path traversal', '/app/../../not-kibana', PATH_ERROR], // fails because there are >2 path parts
['path traversal', '/../not-kibana', PATH_ERROR], // fails because first path part is not 'app'
['deep path', '/app/kibana/foo', PATH_ERROR], // fails because there are >2 path parts
['deeper path', '/app/kibana/foo/bar', PATH_ERROR], // fails because there are >2 path parts
['base path', '/base/app/kibana', PATH_ERROR], // fails because there are >2 path parts
['path with an extra leading slash', '//foo/app/kibana', HOSTNAME_ERROR], // parser detects 'foo' as the hostname
['path with an extra leading slash', '///app/kibana', HOSTNAME_ERROR], // parser detects '' as the hostname
['path without app', '/foo/kibana', PATH_ERROR], // fails because first path part is not 'app'
['path without appId', '/app/', PATH_ERROR], // fails because there is only one path part (leading and trailing slashes are trimmed)
];

invalid.forEach(([desc, url]) => {
it(`fails when url has ${desc}`, () => {
try {
shortUrlAssertValid(url);
throw new Error(`expected assertion to throw`);
} catch (err) {
if (!err || !err.isBoom) {
throw err;
}
}
invalid.forEach(([desc, url, error]) => {
it(`fails when url has ${desc as string}`, () => {
expect(() => shortUrlAssertValid(url as string)).toThrowError(error);
});
});

const valid = [
'/app/kibana',
'/app/kibana/', // leading and trailing slashes are trimmed
'/app/monitoring#angular/route',
'/app/text#document-id',
'/app/some?with=query',
Expand Down
12 changes: 8 additions & 4 deletions src/plugins/share/server/routes/lib/short_url_assert_valid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,22 @@ import { trim } from 'lodash';
import Boom from '@hapi/boom';

export function shortUrlAssertValid(url: string) {
const { protocol, hostname, pathname } = parse(url);
const { protocol, hostname, pathname } = parse(
url,
false /* parseQueryString */,
true /* slashesDenoteHost */
);

if (protocol) {
if (protocol !== null) {
throw Boom.notAcceptable(`Short url targets cannot have a protocol, found "${protocol}"`);
}

if (hostname) {
if (hostname !== null) {
throw Boom.notAcceptable(`Short url targets cannot have a hostname, found "${hostname}"`);
}

const pathnameParts = trim(pathname === null ? undefined : pathname, '/').split('/');
if (pathnameParts.length !== 2) {
if (pathnameParts.length !== 2 || pathnameParts[0] !== 'app' || !pathnameParts[1]) {
throw Boom.notAcceptable(
`Short url target path must be in the format "/app/{{appId}}", found "${pathname}"`
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import _ from 'lodash';
import { startsWith } from 'lodash';
import moment from 'moment';

export function timeShift(resp, panel, series) {
Expand All @@ -26,13 +26,15 @@ export function timeShift(resp, panel, series) {
const matches = series.offset_time.match(/^([+-]?[\d]+)([shmdwMy]|ms)$/);

if (matches) {
const offsetValue = Number(matches[1]);
const offsetValue = matches[1];
const offsetUnit = matches[2];
const offset = moment.duration(offsetValue, offsetUnit).valueOf();

results.forEach((item) => {
if (_.startsWith(item.id, series.id)) {
item.data = item.data.map(([time, value]) => [time + offset, value]);
if (startsWith(item.id, series.id)) {
item.data = item.data.map((row) => [
moment.utc(row[0]).add(offsetValue, offsetUnit).valueOf(),
row[1],
]);
}
});
}
Expand Down
3 changes: 2 additions & 1 deletion test/api_integration/apis/saved_objects/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ function getLogMock() {
export default ({ getService }: FtrProviderContext) => {
const esClient = getService('es');

describe('Kibana index migration', () => {
// FLAKY: https://github.com/elastic/kibana/issues/84445
describe.skip('Kibana index migration', () => {
before(() => esClient.indices.delete({ index: '.migrate-*' }));

it('Migrates an existing index that has never been migrated before', async () => {
Expand Down
17 changes: 17 additions & 0 deletions test/functional/apps/discover/_indexpattern_without_timefield.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import { FtrProviderContext } from '../../ftr_provider_context';

export default function ({ getService, getPageObjects }: FtrProviderContext) {
const browser = getService('browser');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const security = getService('security');
Expand Down Expand Up @@ -50,5 +51,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
throw new Error('Expected timepicker to exist');
}
});
it('should switch between with and without timefield using the browser back button', async () => {
await PageObjects.discover.selectIndexPattern('without-timefield');
if (await PageObjects.timePicker.timePickerExists()) {
throw new Error('Expected timepicker not to exist');
}

await PageObjects.discover.selectIndexPattern('with-timefield');
if (!(await PageObjects.timePicker.timePickerExists())) {
throw new Error('Expected timepicker to exist');
}
// Navigating back to discover
await browser.goBack();
if (await PageObjects.timePicker.timePickerExists()) {
throw new Error('Expected timepicker not to exist');
}
});
});
}
66 changes: 1 addition & 65 deletions x-pack/plugins/alerts/public/alert_api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { AlertType } from '../common';
import { httpServiceMock } from '../../../../src/core/public/mocks';
import { loadAlert, loadAlertState, loadAlertType, loadAlertTypes } from './alert_api';
import { loadAlert, loadAlertType, loadAlertTypes } from './alert_api';
import uuid from 'uuid';

const http = httpServiceMock.createStartContract();
Expand Down Expand Up @@ -114,67 +114,3 @@ describe('loadAlert', () => {
expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}`);
});
});

describe('loadAlertState', () => {
test('should call get API with base parameters', async () => {
const alertId = uuid.v4();
const resolvedValue = {
alertTypeState: {
some: 'value',
},
alertInstances: {
first_instance: {},
second_instance: {},
},
};
http.get.mockResolvedValueOnce(resolvedValue);

expect(await loadAlertState({ http, alertId })).toEqual(resolvedValue);
expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`);
});

test('should parse AlertInstances', async () => {
const alertId = uuid.v4();
const resolvedValue = {
alertTypeState: {
some: 'value',
},
alertInstances: {
first_instance: {
state: {},
meta: {
lastScheduledActions: {
group: 'first_group',
date: '2020-02-09T23:15:41.941Z',
},
},
},
},
};
http.get.mockResolvedValueOnce(resolvedValue);

expect(await loadAlertState({ http, alertId })).toEqual({
...resolvedValue,
alertInstances: {
first_instance: {
state: {},
meta: {
lastScheduledActions: {
group: 'first_group',
date: new Date('2020-02-09T23:15:41.941Z'),
},
},
},
},
});
expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`);
});

test('should handle empty response from api', async () => {
const alertId = uuid.v4();
http.get.mockResolvedValueOnce('');

expect(await loadAlertState({ http, alertId })).toEqual({});
expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`);
});
});
41 changes: 7 additions & 34 deletions x-pack/plugins/alerts/public/alert_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,9 @@
*/

import { HttpSetup } from 'kibana/public';
import * as t from 'io-ts';
import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
import { findFirst } from 'fp-ts/lib/Array';
import { isNone } from 'fp-ts/lib/Option';

import { i18n } from '@kbn/i18n';
import { BASE_ALERT_API_PATH, alertStateSchema } from '../common';
import { Alert, AlertType, AlertTaskState } from '../common';
import { BASE_ALERT_API_PATH } from '../common';
import type { Alert, AlertType } from '../common';

export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise<AlertType[]> {
return await http.get(`${BASE_ALERT_API_PATH}/list_alert_types`);
Expand All @@ -26,10 +20,10 @@ export async function loadAlertType({
http: HttpSetup;
id: AlertType['id'];
}): Promise<AlertType> {
const maybeAlertType = findFirst<AlertType>((type) => type.id === id)(
await http.get(`${BASE_ALERT_API_PATH}/list_alert_types`)
);
if (isNone(maybeAlertType)) {
const maybeAlertType = ((await http.get(
`${BASE_ALERT_API_PATH}/list_alert_types`
)) as AlertType[]).find((type) => type.id === id);
if (!maybeAlertType) {
throw new Error(
i18n.translate('xpack.alerts.loadAlertType.missingAlertTypeError', {
defaultMessage: 'Alert type "{id}" is not registered.',
Expand All @@ -39,7 +33,7 @@ export async function loadAlertType({
})
);
}
return maybeAlertType.value;
return maybeAlertType;
}

export async function loadAlert({
Expand All @@ -51,24 +45,3 @@ export async function loadAlert({
}): Promise<Alert> {
return await http.get(`${BASE_ALERT_API_PATH}/alert/${alertId}`);
}

type EmptyHttpResponse = '';
export async function loadAlertState({
http,
alertId,
}: {
http: HttpSetup;
alertId: string;
}): Promise<AlertTaskState> {
return await http
.get(`${BASE_ALERT_API_PATH}/alert/${alertId}/state`)
.then((state: AlertTaskState | EmptyHttpResponse) => (state ? state : {}))
.then((state: AlertTaskState) => {
return pipe(
alertStateSchema.decode(state),
fold((e: t.Errors) => {
throw new Error(`Alert "${alertId}" has invalid state`);
}, t.identity)
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ export function ServiceNodeMetrics({ match }: ServiceNodeMetricsProps) {
const { urlParams, uiFilters } = useUrlParams();
const { serviceName, serviceNodeName } = match.params;
const { agentName } = useAgentName();
const { data } = useServiceMetricCharts(urlParams, agentName);
const { data } = useServiceMetricCharts(
urlParams,
agentName,
serviceNodeName
);
const { start, end } = urlParams;

const { data: { host, containerId } = INITIAL_DATA, status } = useFetcher(
Expand Down Expand Up @@ -177,25 +181,6 @@ export function ServiceNodeMetrics({ match }: ServiceNodeMetricsProps) {
</EuiFlexItem>
</MetadataFlexGroup>
)}
{agentName && (
<ChartPointerEventContextProvider>
<EuiFlexGrid columns={2} gutterSize="s">
{data.charts.map((chart) => (
<EuiFlexItem key={chart.key}>
<EuiPanel>
<MetricsChart
start={start}
end={end}
chart={chart}
fetchStatus={status}
/>
</EuiPanel>
</EuiFlexItem>
))}
</EuiFlexGrid>
<EuiSpacer size="xxl" />
</ChartPointerEventContextProvider>
)}
<SearchBar />
<EuiPage>
{agentName && (
Expand Down
6 changes: 4 additions & 2 deletions x-pack/plugins/apm/public/hooks/useServiceMetricCharts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ const INITIAL_DATA: MetricsChartsByAgentAPIResponse = {

export function useServiceMetricCharts(
urlParams: IUrlParams,
agentName?: string
agentName?: string,
serviceNodeName?: string
) {
const { serviceName } = useParams<{ serviceName?: string }>();
const { start, end } = urlParams;
Expand All @@ -30,6 +31,7 @@ export function useServiceMetricCharts(
params: {
path: { serviceName },
query: {
serviceNodeName,
start,
end,
agentName,
Expand All @@ -39,7 +41,7 @@ export function useServiceMetricCharts(
});
}
},
[serviceName, start, end, agentName, uiFilters]
[serviceName, start, end, agentName, serviceNodeName, uiFilters]
);

return {
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export async function getDefaultMetricsCharts(
serviceName: string
) {
const charts = await Promise.all([
getCPUChartData(setup, serviceName),
getMemoryChartData(setup, serviceName),
getCPUChartData({ setup, serviceName }),
getMemoryChartData({ setup, serviceName }),
]);

return { charts };
Expand Down
Loading

0 comments on commit 30ae309

Please sign in to comment.