Skip to content

Commit

Permalink
Merge branch 'master' into threshold-dupes
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Nov 30, 2020
2 parents 2c61c26 + d21e4f5 commit a855a19
Show file tree
Hide file tree
Showing 62 changed files with 1,092 additions and 625 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -902,8 +902,9 @@ The most significant changes on the Kibana side for the consumers are the follow
===== User client accessor
Internal /current user client accessors has been renamed and are now
properties instead of functions:
** `callAsInternalUser('ping')` -> `asInternalUser.ping()`
** `callAsCurrentUser('ping')` -> `asCurrentUser.ping()`

* `callAsInternalUser('ping')` -> `asInternalUser.ping()`
* `callAsCurrentUser('ping')` -> `asCurrentUser.ping()`
* the API now reflects the `Client`’s instead of leveraging the
string-based endpoint names the `LegacyAPICaller` was using.

Expand Down
7 changes: 6 additions & 1 deletion src/core/server/http/base_path_proxy_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,13 @@ export class BasePathProxyServer {
const isGet = request.method === 'get';
const isBasepathLike = oldBasePath.length === 3;

const newUrl = Url.format({
pathname: `${this.httpConfig.basePath}/${kbnPath}`,
query: request.query,
});

return isGet && isBasepathLike && shouldRedirectFromOldBasePath(kbnPath)
? responseToolkit.redirect(`${this.httpConfig.basePath}/${kbnPath}`)
? responseToolkit.redirect(newUrl)
: responseToolkit.response('Not Found').code(404);
},
method: '*',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ kibana_vars=(
xpack.code.security.gitProtocolWhitelist
xpack.encryptedSavedObjects.encryptionKey
xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys
xpack.fleet.agents.elasticsearch.host
xpack.fleet.agents.kibana.host
xpack.fleet.agents.tlsCheckDisabled
xpack.graph.enabled
xpack.graph.canEditDrillDownUrls
xpack.graph.savePolicy
Expand Down
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
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const applicationUsageSchema = {
logs: commonSchema,
metrics: commonSchema,
infra: commonSchema, // It's a forward app so we'll likely never report it
ingestManager: commonSchema,
fleet: commonSchema,
lens: commonSchema,
maps: commonSchema,
ml: commonSchema,
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:foo@bar.net'],
['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:pass@localhost.net/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:foo@bar.net', 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:pass@localhost.net/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
2 changes: 1 addition & 1 deletion src/plugins/telemetry/schema/oss_plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@
}
}
},
"ingestManager": {
"fleet": {
"properties": {
"clicks_total": {
"type": "long"
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)
);
});
}
Loading

0 comments on commit a855a19

Please sign in to comment.