Skip to content

Commit

Permalink
Merge branch 'master' into telemetry_plugins_migrated_test
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Jan 4, 2021
2 parents a924695 + e9b43cd commit 3d60e44
Show file tree
Hide file tree
Showing 252 changed files with 4,165 additions and 7,052 deletions.
2 changes: 1 addition & 1 deletion NOTICE.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Kibana source code with Kibana X-Pack source code
Copyright 2012-2020 Elasticsearch B.V.
Copyright 2012-2021 Elasticsearch B.V.

---
Pretty handling of logarithmic axes.
Expand Down
6 changes: 6 additions & 0 deletions docs/user/dashboard/url-drilldown.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ Example:

`{{split event.value ","}}`

|encodeURIComponent
a|Escapes string using built in `encodeURIComponent` function.

|encodeURIQuery
a|Escapes string using built in `encodeURIComponent` function, while keeping "@", ":", "$", ",", and ";" characters as is.

|===


Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
"@hapi/good-squeeze": "6.0.0",
"@hapi/h2o2": "^9.0.2",
"@hapi/hapi": "^20.0.3",
"@hapi/hoek": "^9.1.0",
"@hapi/hoek": "^9.1.1",
"@hapi/inert": "^6.0.3",
"@hapi/podium": "^4.1.1",
"@hapi/statehood": "^7.0.3",
Expand Down
44 changes: 44 additions & 0 deletions packages/kbn-i18n/GUIDELINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,50 @@ Splitting sentences into several keys often inadvertently presumes a grammar, a

### Unit tests

#### How to test `FormattedMessage` and `i18n.translate()` components.

To make `FormattedMessage` component work properly, wrapping it with `I18nProvider` is required. In development/production app, this is done in the ancestor components and developers don't have to worry about that.

But when unit-testing them, no other component provides that wrapping. That's why `shallowWithI18nProvider` and `mountWithI18nProvider` helpers are created.

For example, there is a component that has `FormattedMessage` inside, like `SaveModal` component:

```js
// ...
export const SaveModal = (props) => {
return (
<div>
{/* Other things. */}
<EuiButton>
<FormattedMessage
id="kbn.dashboard.topNav.saveModal.saveButtonText"
defaultMessage="Save"
/>
</EuiButton>
{/* More other things. */}
</div>
)
}
```

To test `SaveModal` component, it should be wrapped with `I18nProvider` by using `shallowWithI18nProvider`:

```js
// ...
it('should render normally', async () => {
const component = shallowWithI18nProvider(
<SaveModal dashboard={{}}/>
);

expect(component).toMatchSnapshot();
});
// ...
```

If a component uses only `i18n.translate()`, it doesn't need `I18nProvider`. In that case, you can test them with `shallow` and `mount` functions that `enzyme` providers out of the box.

#### How to test `injectI18n` HOC components.

Testing React component that uses the `injectI18n` higher-order component is more complicated because `injectI18n()` creates a wrapper component around the original component.

With shallow rendering only top level component is rendered, that is a wrapper itself, not the original component. Since we want to test the rendering of the original component, we need to access it via the wrapper's `WrappedComponent` property. Its value will be the component we passed into `injectI18n()`.
Expand Down
2 changes: 2 additions & 0 deletions src/core/public/doc_links/doc_links_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,9 @@ export class DocLinksService {
createRoleMapping: `${ELASTICSEARCH_DOCS}security-api-put-role-mapping.html`,
createApiKey: `${ELASTICSEARCH_DOCS}security-api-create-api-key.html`,
createPipeline: `${ELASTICSEARCH_DOCS}put-pipeline-api.html`,
createTransformRequest: `${ELASTICSEARCH_DOCS}put-transform.html#put-transform-request-body`,
openIndex: `${ELASTICSEARCH_DOCS}indices-open-close.html`,
updateTransform: `${ELASTICSEARCH_DOCS}update-transform.html`,
},
},
});
Expand Down
21 changes: 1 addition & 20 deletions src/core/server/http/cookie_session_storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@

import { Request, Server } from '@hapi/hapi';
import hapiAuthCookie from '@hapi/cookie';
// @ts-expect-error no TS definitions
import Statehood from '@hapi/statehood';

import { KibanaRequest, ensureRawRequest } from './router';
import { SessionStorageFactory, SessionStorage } from './session_storage';
Expand Down Expand Up @@ -148,7 +146,7 @@ export async function createCookieSessionStorageFactory<T>(
path: basePath === undefined ? '/' : basePath,
clearInvalid: false,
isHttpOnly: true,
isSameSite: cookieOptions.sameSite === 'None' ? false : cookieOptions.sameSite ?? false,
isSameSite: cookieOptions.sameSite ?? false,
},
validateFunc: async (req: Request, session: T | T[]) => {
const result = cookieOptions.validate(session);
Expand All @@ -159,23 +157,6 @@ export async function createCookieSessionStorageFactory<T>(
},
});

// A hack to support SameSite: 'None'.
// Remove it after update Hapi to v19 that supports SameSite: 'None' out of the box.
if (cookieOptions.sameSite === 'None') {
log.debug('Patching Statehood.prepareValue');
const originalPrepareValue = Statehood.prepareValue;
Statehood.prepareValue = function kibanaStatehoodPrepareValueWrapper(
name: string,
value: unknown,
options: any
) {
if (name === cookieOptions.name) {
options.isSameSite = cookieOptions.sameSite;
}
return originalPrepareValue(name, value, options);
};
}

return {
asScoped(request: KibanaRequest) {
return new ScopedCookieSessionStorage<T>(log, server, ensureRawRequest(request));
Expand Down
28 changes: 10 additions & 18 deletions src/core/server/http/http_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { Server, ServerRoute } from '@hapi/hapi';
import { Server } from '@hapi/hapi';
import HapiStaticFiles from '@hapi/inert';
import url from 'url';
import uuid from 'uuid';
Expand Down Expand Up @@ -167,21 +167,28 @@ export class HttpServer {
for (const router of this.registeredRouters) {
for (const route of router.getRoutes()) {
this.log.debug(`registering route handler for [${route.path}]`);
// Hapi does not allow payload validation to be specified for 'head' or 'get' requests
const validate = isSafeMethod(route.method) ? undefined : { payload: true };
const { authRequired, tags, body = {}, timeout } = route.options;
const { accepts: allow, maxBytes, output, parse } = body;

const kibanaRouteOptions: KibanaRouteOptions = {
xsrfRequired: route.options.xsrfRequired ?? !isSafeMethod(route.method),
};

const routeOpts: ServerRoute = {
this.server.route({
handler: route.handler,
method: route.method,
path: route.path,
options: {
auth: this.getAuthOption(authRequired),
app: kibanaRouteOptions,
tags: tags ? Array.from(tags) : undefined,
// TODO: This 'validate' section can be removed once the legacy platform is completely removed.
// We are telling Hapi that NP routes can accept any payload, so that it can bypass the default
// validation applied in ./http_tools#getServerOptions
// (All NP routes are already required to specify their own validation in order to access the payload)
validate,
// @ts-expect-error Types are outdated and doesn't allow `payload.multipart` to be `true`
payload: [allow, maxBytes, output, parse, timeout?.payload].some((x) => x !== undefined)
? {
Expand All @@ -197,22 +204,7 @@ export class HttpServer {
socket: timeout?.idleSocket ?? this.config!.socketTimeout,
},
},
};

// Hapi does not allow payload validation to be specified for 'head' or 'get' requests
if (!isSafeMethod(route.method)) {
// TODO: This 'validate' section can be removed once the legacy platform is completely removed.
// We are telling Hapi that NP routes can accept any payload, so that it can bypass the default
// validation applied in ./http_tools#getServerOptions
// (All NP routes are already required to specify their own validation in order to access the payload)
// TODO: Move the setting of the validate option back up to being set at `routeOpts` creation-time once
// https://github.com/hapijs/hoek/pull/365 is merged and released in @hapi/hoek v9.1.1. At that point I
// imagine the ts-error below will go away as well.
// @ts-expect-error "Property 'validate' does not exist on type 'RouteOptions'" <-- ehh?!? yes it does!
routeOpts.options!.validate = { payload: true };
}

this.server.route(routeOpts);
});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -908,7 +908,8 @@ describe('migration actions', () => {
});
});

describe('createIndex', () => {
// FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/87160
describe.skip('createIndex', () => {
afterAll(async () => {
await client.indices.delete({ index: 'yellow_then_green_index' });
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import React from 'react';
import { I18nProvider } from '@kbn/i18n/react';
import { parse, ParsedQuery } from 'query-string';
import { render, unmountComponentAtNode } from 'react-dom';
import { Switch, Route, RouteComponentProps, HashRouter } from 'react-router-dom';
import { Switch, Route, RouteComponentProps, HashRouter, Redirect } from 'react-router-dom';

import { DashboardListing } from './listing';
import { DashboardApp } from './dashboard_app';
Expand Down Expand Up @@ -202,6 +202,9 @@ export async function mountApp({
render={renderDashboard}
/>
<Route exact path={DashboardConstants.LANDING_PAGE_PATH} render={renderListingPage} />
<Route path="/">
<Redirect to={DashboardConstants.LANDING_PAGE_PATH} />
</Route>
</Switch>
</HashRouter>
</KibanaContextProvider>
Expand Down
14 changes: 11 additions & 3 deletions src/plugins/data/public/ui/filter_bar/filter_item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,27 @@ export function FilterItem(props: Props) {

useEffect(() => {
const index = props.filter.meta.index;
let isSubscribed = true;
if (index) {
getIndexPatterns()
.get(index)
.then((indexPattern) => {
setIndexPatternExists(!!indexPattern);
if (isSubscribed) {
setIndexPatternExists(!!indexPattern);
}
})
.catch(() => {
setIndexPatternExists(false);
if (isSubscribed) {
setIndexPatternExists(false);
}
});
} else {
} else if (isSubscribed) {
// Allow filters without an index pattern and don't validate them.
setIndexPatternExists(true);
}
return () => {
isSubscribed = false;
};
}, [props.filter.meta.index]);

function handleBadgeClick(e: MouseEvent<HTMLInputElement>) {
Expand Down
13 changes: 1 addition & 12 deletions src/plugins/discover/public/application/angular/context_app.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
<kbn-top-nav
app-name="'context'"
show-search-bar="true"
show-filter-bar="true"
show-query-bar="false"
show-save-query="false"
show-date-picker="false"
index-patterns="[contextApp.indexPattern]"
use-default-behaviors="true"
>
</kbn-top-nav>

<!-- Context App Legacy -->
<context-app-legacy
filter="contextApp.actions.addFilter"
Expand All @@ -29,4 +17,5 @@
successor-available="contextApp.state.rows.successors.length"
successor-status="contextApp.state.loadingStatus.successors.status"
on-change-successor-count="contextApp.actions.fetchGivenSuccessorRows"
top-nav-menu="contextApp.topNavMenu"
></context-app-legacy>
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,14 @@ getAngularModule().directive('contextApp', function ContextApp() {
});

function ContextAppController($scope, Private) {
const { filterManager, indexPatterns, uiSettings } = getServices();
const { filterManager, indexPatterns, uiSettings, navigation } = getServices();
const queryParameterActions = getQueryParameterActions(filterManager, indexPatterns);
const queryActions = Private(QueryActionsProvider);
this.state = createInitialState(
parseInt(uiSettings.get(CONTEXT_STEP_SETTING), 10),
getFirstSortableField(this.indexPattern, uiSettings.get(CONTEXT_TIE_BREAKER_FIELDS_SETTING))
);
this.topNavMenu = navigation.ui.TopNavMenu;

this.actions = _.mapValues(
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';

export const TopNavMenuMock = () => <div>Hello World</div>;
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { DocTableLegacy } from '../../angular/doc_table/create_doc_table_react';
import { findTestSubject } from '@elastic/eui/lib/test';
import { ActionBar } from '../../angular/context/components/action_bar/action_bar';
import { ContextErrorMessage } from '../context_error_message';
import { TopNavMenuMock } from './__mocks__/top_nav_menu';

describe('ContextAppLegacy test', () => {
const hit = {
Expand Down Expand Up @@ -64,6 +65,17 @@ describe('ContextAppLegacy test', () => {
onChangeSuccessorCount: jest.fn(),
predecessorStatus: 'loaded',
successorStatus: 'loaded',
topNavMenu: TopNavMenuMock,
};
const topNavProps = {
appName: 'context',
showSearchBar: true,
showQueryBar: false,
showFilterBar: true,
showSaveQuery: false,
showDatePicker: false,
indexPatterns: [indexPattern],
useDefaultBehaviors: true,
};

it('renders correctly', () => {
Expand All @@ -72,6 +84,9 @@ describe('ContextAppLegacy test', () => {
const loadingIndicator = findTestSubject(component, 'contextApp_loadingIndicator');
expect(loadingIndicator.length).toBe(0);
expect(component.find(ActionBar).length).toBe(2);
const topNavMenu = component.find(TopNavMenuMock);
expect(topNavMenu.length).toBe(1);
expect(topNavMenu.props()).toStrictEqual(topNavProps);
});

it('renders loading indicator', () => {
Expand All @@ -82,6 +97,7 @@ describe('ContextAppLegacy test', () => {
const loadingIndicator = findTestSubject(component, 'contextApp_loadingIndicator');
expect(loadingIndicator.length).toBe(1);
expect(component.find(ActionBar).length).toBe(2);
expect(component.find(TopNavMenuMock).length).toBe(1);
});

it('renders error message', () => {
Expand All @@ -90,6 +106,7 @@ describe('ContextAppLegacy test', () => {
props.reason = 'something went wrong';
const component = mountWithIntl(<ContextAppLegacy {...props} />);
expect(component.find(DocTableLegacy).length).toBe(0);
expect(component.find(TopNavMenuMock).length).toBe(0);
const errorMessage = component.find(ContextErrorMessage);
expect(errorMessage.length).toBe(1);
});
Expand Down
Loading

0 comments on commit 3d60e44

Please sign in to comment.