Skip to content

Commit

Permalink
Merge branch 'main' of github.com:elastic/kibana into cases-files-config
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathan-buttner committed Apr 3, 2023
2 parents 07a428e + 3948f61 commit 3d737c0
Show file tree
Hide file tree
Showing 153 changed files with 4,337 additions and 1,149 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ packages/core/http/core-http-router-server-mocks @elastic/kibana-core
packages/core/http/core-http-server @elastic/kibana-core
packages/core/http/core-http-server-internal @elastic/kibana-core
packages/core/http/core-http-server-mocks @elastic/kibana-core
packages/core/http/core-http-versioned-router-server-internal @elastic/kibana-core
packages/core/i18n/core-i18n-browser @elastic/kibana-core
packages/core/i18n/core-i18n-browser-internal @elastic/kibana-core
packages/core/i18n/core-i18n-browser-mocks @elastic/kibana-core
Expand Down Expand Up @@ -462,6 +461,7 @@ x-pack/plugins/ml @elastic/ml-ui
x-pack/packages/ml/query_utils @elastic/ml-ui
x-pack/packages/ml/route_utils @elastic/ml-ui
x-pack/packages/ml/string_hash @elastic/ml-ui
x-pack/packages/ml/trained_models_utils @elastic/ml-ui
x-pack/packages/ml/url_state @elastic/ml-ui
packages/kbn-monaco @elastic/appex-sharedux
x-pack/plugins/monitoring_collection @elastic/infra-monitoring-ui
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,6 @@
"@kbn/core-http-router-server-internal": "link:packages/core/http/core-http-router-server-internal",
"@kbn/core-http-server": "link:packages/core/http/core-http-server",
"@kbn/core-http-server-internal": "link:packages/core/http/core-http-server-internal",
"@kbn/core-http-versioned-router-server-internal": "link:packages/core/http/core-http-versioned-router-server-internal",
"@kbn/core-i18n-browser": "link:packages/core/i18n/core-i18n-browser",
"@kbn/core-i18n-browser-internal": "link:packages/core/i18n/core-i18n-browser-internal",
"@kbn/core-i18n-server": "link:packages/core/i18n/core-i18n-server",
Expand Down Expand Up @@ -477,6 +476,7 @@
"@kbn/ml-query-utils": "link:x-pack/packages/ml/query_utils",
"@kbn/ml-route-utils": "link:x-pack/packages/ml/route_utils",
"@kbn/ml-string-hash": "link:x-pack/packages/ml/string_hash",
"@kbn/ml-trained-models-utils": "link:x-pack/packages/ml/trained_models_utils",
"@kbn/ml-url-state": "link:x-pack/packages/ml/url_state",
"@kbn/monaco": "link:packages/kbn-monaco",
"@kbn/monitoring-collection-plugin": "link:x-pack/plugins/monitoring_collection",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { HttpResponse, HttpFetchOptionsWithPath } from '@kbn/core-http-brow

import { Fetch } from './fetch';
import { BasePath } from './base_path';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';

function delay<T>(duration: number) {
return new Promise<T>((r) => setTimeout(r, duration));
Expand Down Expand Up @@ -479,6 +480,14 @@ describe('Fetch', () => {

expect(ndjson).toEqual(content);
});

it('should pass through version as a header', async () => {
fetchMock.get('*', { body: {} });
await fetchInstance.fetch('/my/path', { asResponse: true, version: '99' });
expect(fetchMock.lastOptions()!.headers).toEqual(
expect.objectContaining({ [ELASTIC_HTTP_VERSION_HEADER.toLowerCase()]: '99' })
);
});
});

describe('interception', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/core/http/core-http-browser-internal/src/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
HttpResponse,
HttpFetchOptionsWithPath,
} from '@kbn/core-http-browser';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { HttpFetchError } from './http_fetch_error';
import { HttpInterceptController } from './http_intercept_controller';
import { interceptRequest, interceptResponse } from './intercept';
Expand Down Expand Up @@ -110,6 +111,7 @@ export class Fetch {

private createRequest(options: HttpFetchOptionsWithPath): Request {
const context = this.params.executionContext.withGlobalContext(options.context);
const { version } = options;
// Merge and destructure options out that are not applicable to the Fetch API.
const {
query,
Expand All @@ -128,6 +130,7 @@ export class Fetch {
'Content-Type': 'application/json',
...options.headers,
'kbn-version': this.params.kibanaVersion,
[ELASTIC_HTTP_VERSION_HEADER]: version,
...(!isEmpty(context) ? new ExecutionContextContainer(context).toHeader() : {}),
}),
};
Expand Down
4 changes: 4 additions & 0 deletions packages/core/http/core-http-browser/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import type { Observable } from 'rxjs';
import type { MaybePromise } from '@kbn/utility-types';
import type { KibanaExecutionContext } from '@kbn/core-execution-context-common';
import type { ApiVersion } from '@kbn/core-http-common';

/** @public */
export interface HttpSetup {
Expand Down Expand Up @@ -280,6 +281,9 @@ export interface HttpFetchOptions extends HttpRequestInit {
asResponse?: boolean;

context?: KibanaExecutionContext;

/** @experimental */
version?: ApiVersion;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/core/http/core-http-browser/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
],
"kbn_references": [
"@kbn/utility-types",
"@kbn/core-execution-context-common"
"@kbn/core-execution-context-common",
"@kbn/core-http-common"
],
"exclude": [
"target/**/*",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/http/core-http-common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
*/

export type { IExternalUrlPolicy } from './src/external_url_policy';

export type { ApiVersion } from './src/versioning';
export { ELASTIC_HTTP_VERSION_HEADER } from './src/versioning';
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
* Side Public License, v 1.
*/

module.exports = {
preset: '@kbn/test/jest_node',
rootDir: '../../../..',
roots: ['<rootDir>/packages/core/http/core-http-versioned-router-server-internal'],
};
/**
* A Kibana HTTP API version
* @note assumption that version will be monotonically increasing number where: version > 0.
* @experimental
*/
export type ApiVersion = `${number}`;

/** @internal */
export const ELASTIC_HTTP_VERSION_HEADER = 'elastic-api-version' as const;
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,3 @@ export {
isKibanaResponse,
KibanaResponse,
} from './src/response';
export { RouteValidator } from './src/validator';
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ import {
RawRequest,
FakeRawRequest,
} from '@kbn/core-http-server';
import { RouteValidator } from './validator';
import { isSafeMethod } from './route';
import { KibanaSocket } from './socket';
import { RouteValidator } from './validator';

const requestSymbol = Symbol('request');

Expand Down
15 changes: 13 additions & 2 deletions packages/core/http/core-http-router-server-internal/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ import type {
RouterRoute,
IRouter,
RequestHandler,
VersionedRouter,
IRouterWithVersion,
} from '@kbn/core-http-server';
import { validBodyOutput } from '@kbn/core-http-server';
import { RouteValidator } from './validator';
import { CoreVersionedRouter } from './versioned_router';
import { CoreKibanaRequest } from './request';
import { kibanaResponseFactory } from './response';
import { HapiResponseAdapter } from './response_adapter';
import { wrapErrors } from './error_wrapper';
import { RouteValidator } from './validator';

export type ContextEnhancer<
P,
Expand Down Expand Up @@ -120,7 +123,7 @@ function validOptions(
* @internal
*/
export class Router<Context extends RequestHandlerContextBase = RequestHandlerContextBase>
implements IRouter<Context>
implements IRouterWithVersion<Context>
{
public routes: Array<Readonly<RouterRoute>> = [];
public get: IRouter<Context>['get'];
Expand Down Expand Up @@ -202,6 +205,14 @@ export class Router<Context extends RequestHandlerContextBase = RequestHandlerCo
return hapiResponseAdapter.toInternalError();
}
}

private versionedRouter: undefined | VersionedRouter<Context> = undefined;
public get versioned(): VersionedRouter<Context> {
if (this.versionedRouter === undefined) {
this.versionedRouter = CoreVersionedRouter.from({ router: this });
}
return this.versionedRouter;
}
}

const convertEsUnauthorized = (e: EsNotAuthorizedError): ErrorHttpResponseOptions => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,56 @@
* Side Public License, v 1.
*/

import { hapiMocks } from '@kbn/hapi-mocks';
import { schema } from '@kbn/config-schema';
import type { IRouter, RequestHandler } from '@kbn/core-http-server';
import { httpServiceMock, httpServerMock } from '@kbn/core-http-server-mocks';
import { VERSION_HEADER } from './core_versioned_route';
import type { ApiVersion } from '@kbn/core-http-common';
import type { IRouter, KibanaResponseFactory, RequestHandler } from '@kbn/core-http-server';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { createRouter } from './mocks';
import { CoreVersionedRouter } from '.';
import { kibanaResponseFactory } from '@kbn/core-http-router-server-internal';
import { passThroughValidation } from './core_versioned_route';
import { CoreKibanaRequest } from '../request';

const createRequest = (
{
version,
body,
params,
query,
}: { version: undefined | ApiVersion; body?: object; params?: object; query?: object } = {
version: '1',
}
) =>
CoreKibanaRequest.from(
hapiMocks.createRequest({
payload: body,
params,
query,
headers: { [ELASTIC_HTTP_VERSION_HEADER]: version },
app: { requestId: 'fakeId' },
}),
passThroughValidation
);

describe('Versioned route', () => {
let router: IRouter;
let responseFactory: jest.Mocked<KibanaResponseFactory>;
const handlerFn: RequestHandler = async (ctx, req, res) => res.ok({ body: { foo: 1 } });
beforeEach(() => {
router = httpServiceMock.createRouter();
responseFactory = {
custom: jest.fn(({ body, statusCode }) => ({
options: {},
status: statusCode,
payload: body,
})),
badRequest: jest.fn(({ body }) => ({ status: 400, payload: body, options: {} })),
ok: jest.fn(({ body } = {}) => ({
options: {},
status: 200,
payload: body,
})),
} as any;
router = createRouter();
});

it('can register multiple handlers', () => {
Expand Down Expand Up @@ -48,6 +86,31 @@ describe('Versioned route', () => {
);
});

it('only allows versions that are numbers greater than 0', () => {
const versionedRouter = CoreVersionedRouter.from({ router });
expect(() =>
versionedRouter
.get({ path: '/test/{id}', access: 'internal' })
.addVersion({ version: 'foo' as ApiVersion, validate: false }, handlerFn)
).toThrowError(
`Invalid version number. Received "foo", expected any finite, whole number greater than 0.`
);
expect(() =>
versionedRouter
.get({ path: '/test/{id}', access: 'internal' })
.addVersion({ version: '-1', validate: false }, handlerFn)
).toThrowError(
`Invalid version number. Received "-1", expected any finite, whole number greater than 0.`
);
expect(() =>
versionedRouter
.get({ path: '/test/{id}', access: 'internal' })
.addVersion({ version: '1.1', validate: false }, handlerFn)
).toThrowError(
`Invalid version number. Received "1.1", expected any finite, whole number greater than 0.`
);
});

it('runs request and response validations', async () => {
let handler: RequestHandler;

Expand All @@ -57,7 +120,7 @@ describe('Versioned route', () => {
let validatedOutputBody = false;

(router.post as jest.Mock).mockImplementation((opts: unknown, fn) => (handler = fn));
const versionedRouter = CoreVersionedRouter.from({ router });
const versionedRouter = CoreVersionedRouter.from({ router, validateResponses: true });
versionedRouter.post({ path: '/test/{id}', access: 'internal' }).addVersion(
{
version: '1',
Expand Down Expand Up @@ -103,13 +166,13 @@ describe('Versioned route', () => {

const kibanaResponse = await handler!(
{} as any,
httpServerMock.createKibanaRequest({
headers: { [VERSION_HEADER]: '1' },
createRequest({
version: '1',
body: { foo: 1 },
params: { foo: 1 },
query: { foo: 1 },
}),
kibanaResponseFactory
responseFactory
);

expect(kibanaResponse.status).toBe(200);
Expand All @@ -126,18 +189,14 @@ describe('Versioned route', () => {
versionedRouter.post({ access: 'internal', path: '/test/{id}' });

await expect(
handler!(
{} as any,
httpServerMock.createKibanaRequest({
headers: { [VERSION_HEADER]: '999' },
}),
kibanaResponseFactory
)
).resolves.toEqual({
options: {},
payload: 'No version "999" available for [post] [/test/{id}]. Available versions are: "none"',
status: 406,
});
handler!({} as any, createRequest({ version: '999' }), responseFactory)
).resolves.toEqual(
expect.objectContaining({
payload:
'No version "999" available for [post] [/test/{id}]. Available versions are: <none>',
status: 406,
})
);
});

it('returns the expected output if no version was provided to versioned route', async () => {
Expand All @@ -150,17 +209,10 @@ describe('Versioned route', () => {
.addVersion({ validate: false, version: '1' }, handlerFn);

await expect(
handler!(
{} as any,
httpServerMock.createKibanaRequest({
headers: {},
}),
kibanaResponseFactory
)
handler!({} as any, createRequest({ version: undefined }), responseFactory)
).resolves.toEqual({
options: {},
payload:
'Version expected at [post] [/test/{id}]. Please specify a version using the "Elastic-Api-Version" header. Available versions are: "1"',
payload: `Version expected at [post] [/test/{id}]. Please specify a version using the "${ELASTIC_HTTP_VERSION_HEADER}" header. Available versions are: [1]`,
status: 406,
});
});
Expand All @@ -179,11 +231,11 @@ describe('Versioned route', () => {
await expect(
handler!(
{} as any,
httpServerMock.createKibanaRequest({
headers: { [VERSION_HEADER]: '1' },
createRequest({
version: '1',
body: {},
}),
kibanaResponseFactory
responseFactory
)
).resolves.toEqual({
options: {},
Expand Down
Loading

0 comments on commit 3d737c0

Please sign in to comment.