Skip to content

Commit

Permalink
Merge branch 'master' into dev/search-server-session-wip
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Nov 26, 2020
2 parents 14f47c3 + 51f75a5 commit 84de299
Show file tree
Hide file tree
Showing 93 changed files with 1,849 additions and 804 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
13 changes: 13 additions & 0 deletions src/cli/cluster/cluster_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ export class ClusterManager {
this.inReplMode = !!opts.repl;
this.basePathProxy = basePathProxy;

if (!this.basePathProxy) {
this.log.warn(
'===================================================================================================='
);
this.log.warn(
'no-base-path',
'Running Kibana in dev mode with --no-base-path disables several useful features and is not recommended'
);
this.log.warn(
'===================================================================================================='
);
}

// run @kbn/optimizer and write it's state to kbnOptimizerReady$
if (opts.disableOptimizer) {
this.kbnOptimizerReady$.next(true);
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
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,17 @@ test('Add to library is not compatible when embeddable is not in a dashboard con
test('Add to library replaces embeddableId and retains panel count', async () => {
const dashboard = embeddable.getRoot() as IContainer;
const originalPanelCount = Object.keys(dashboard.getInput().panels).length;
const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels));

const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts });
await action.execute({ embeddable });
expect(Object.keys(container.getInput().panels).length).toEqual(originalPanelCount);
expect(Object.keys(container.getInput().panels)).toContain(embeddable.id);
const newPanel = container.getInput().panels[embeddable.id!];

const newPanelId = Object.keys(container.getInput().panels).find(
(key) => !originalPanelKeySet.has(key)
);
expect(newPanelId).toBeDefined();
const newPanel = container.getInput().panels[newPanelId!];
expect(newPanel.type).toEqual(embeddable.type);
});

Expand All @@ -158,10 +163,15 @@ test('Add to library returns reference type input', async () => {
mockedByReferenceInput: { savedObjectId: 'testSavedObjectId', id: embeddable.id },
mockedByValueInput: { attributes: complicatedAttributes, id: embeddable.id } as EmbeddableInput,
});
const dashboard = embeddable.getRoot() as IContainer;
const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels));
const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts });
await action.execute({ embeddable });
expect(Object.keys(container.getInput().panels)).toContain(embeddable.id);
const newPanel = container.getInput().panels[embeddable.id!];
const newPanelId = Object.keys(container.getInput().panels).find(
(key) => !originalPanelKeySet.has(key)
);
expect(newPanelId).toBeDefined();
const newPanel = container.getInput().panels[newPanelId!];
expect(newPanel.type).toEqual(embeddable.type);
expect(newPanel.explicitInput.attributes).toBeUndefined();
expect(newPanel.explicitInput.savedObjectId).toBe('testSavedObjectId');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import { i18n } from '@kbn/i18n';
import _ from 'lodash';
import uuid from 'uuid';
import { ActionByType, IncompatibleActionError } from '../../ui_actions_plugin';
import { ViewMode, PanelState, IEmbeddable } from '../../embeddable_plugin';
import {
Expand Down Expand Up @@ -89,9 +88,9 @@ export class AddToLibraryAction implements ActionByType<typeof ACTION_ADD_TO_LIB

const newPanel: PanelState<EmbeddableInput> = {
type: embeddable.type,
explicitInput: { ...newInput, id: uuid.v4() },
explicitInput: { ...newInput },
};
dashboard.replacePanel(panelToReplace, newPanel);
dashboard.replacePanel(panelToReplace, newPanel, true);

const title = i18n.translate('dashboard.panel.addToLibrary.successMessage', {
defaultMessage: `Panel '{panelTitle}' was added to the visualize library`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,16 @@ test('Unlink is not compatible when embeddable is not in a dashboard container',
test('Unlink replaces embeddableId and retains panel count', async () => {
const dashboard = embeddable.getRoot() as IContainer;
const originalPanelCount = Object.keys(dashboard.getInput().panels).length;
const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels));
const action = new UnlinkFromLibraryAction({ toasts: coreStart.notifications.toasts });
await action.execute({ embeddable });
expect(Object.keys(container.getInput().panels).length).toEqual(originalPanelCount);
expect(Object.keys(container.getInput().panels)).toContain(embeddable.id);
const newPanel = container.getInput().panels[embeddable.id!];

const newPanelId = Object.keys(container.getInput().panels).find(
(key) => !originalPanelKeySet.has(key)
);
expect(newPanelId).toBeDefined();
const newPanel = container.getInput().panels[newPanelId!];
expect(newPanel.type).toEqual(embeddable.type);
});

Expand All @@ -159,10 +164,15 @@ test('Unlink unwraps all attributes from savedObject', async () => {
mockedByReferenceInput: { savedObjectId: 'testSavedObjectId', id: embeddable.id },
mockedByValueInput: { attributes: complicatedAttributes, id: embeddable.id },
});
const dashboard = embeddable.getRoot() as IContainer;
const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels));
const action = new UnlinkFromLibraryAction({ toasts: coreStart.notifications.toasts });
await action.execute({ embeddable });
expect(Object.keys(container.getInput().panels)).toContain(embeddable.id);
const newPanel = container.getInput().panels[embeddable.id!];
const newPanelId = Object.keys(container.getInput().panels).find(
(key) => !originalPanelKeySet.has(key)
);
expect(newPanelId).toBeDefined();
const newPanel = container.getInput().panels[newPanelId!];
expect(newPanel.type).toEqual(embeddable.type);
expect(newPanel.explicitInput.attributes).toEqual(complicatedAttributes);
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import { i18n } from '@kbn/i18n';
import _ from 'lodash';
import uuid from 'uuid';
import { ActionByType, IncompatibleActionError } from '../../ui_actions_plugin';
import { ViewMode, PanelState, IEmbeddable } from '../../embeddable_plugin';
import {
Expand Down Expand Up @@ -88,9 +87,9 @@ export class UnlinkFromLibraryAction implements ActionByType<typeof ACTION_UNLIN

const newPanel: PanelState<EmbeddableInput> = {
type: embeddable.type,
explicitInput: { ...newInput, id: uuid.v4() },
explicitInput: { ...newInput },
};
dashboard.replacePanel(panelToReplace, newPanel);
dashboard.replacePanel(panelToReplace, newPanel, true);

const title = embeddable.getTitle()
? i18n.translate('dashboard.panel.unlinkFromLibrary.successMessageWithTitle', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,30 @@ export class DashboardContainer extends Container<InheritedChildInput, Dashboard

public replacePanel(
previousPanelState: DashboardPanelState<EmbeddableInput>,
newPanelState: Partial<PanelState>
newPanelState: Partial<PanelState>,
generateNewId?: boolean
) {
// Because the embeddable type can change, we have to operate at the container level here
return this.updateInput({
panels: {
let panels;
if (generateNewId) {
// replace panel can be called with generateNewId in order to totally destroy and recreate the embeddable
panels = { ...this.input.panels };
delete panels[previousPanelState.explicitInput.id];
const newId = uuid.v4();
panels[newId] = {
...previousPanelState,
...newPanelState,
gridData: {
...previousPanelState.gridData,
i: newId,
},
explicitInput: {
...newPanelState.explicitInput,
id: newId,
},
};
} else {
// Because the embeddable type can change, we have to operate at the container level here
panels = {
...this.input.panels,
[previousPanelState.explicitInput.id]: {
...previousPanelState,
Expand All @@ -190,7 +209,11 @@ export class DashboardContainer extends Container<InheritedChildInput, Dashboard
id: previousPanelState.explicitInput.id,
},
},
},
};
}

return this.updateInput({
panels,
lastReloadRequestTime: new Date().getTime(),
});
}
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 @@ -260,7 +260,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 @@ -305,8 +306,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
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
import { VisualizeServices } from '../types';
import { VisualizeEditorCommon } from './visualize_editor_common';
import { VisualizeAppProps } from '../app';
import { VisualizeConstants } from '../..';

export const VisualizeByValueEditor = ({ onAppLeave }: VisualizeAppProps) => {
const [originatingApp, setOriginatingApp] = useState<string>();
Expand All @@ -52,7 +53,8 @@ export const VisualizeByValueEditor = ({ onAppLeave }: VisualizeAppProps) => {
setValueInput(valueInputValue);
setEmbeddableId(embeddableIdValue);
if (!valueInputValue) {
history.back();
// if there is no value input to load, redirect to the visualize listing page.
services.history.replace(VisualizeConstants.LANDING_PAGE_PATH);
}
}, [services]);

Expand Down
Loading

0 comments on commit 84de299

Please sign in to comment.