Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[data views] Use versioned router for REST routes #158608

Merged
merged 49 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
3d659b6
version create data view
mattkime May 27, 2023
29bbb0c
change version to date
mattkime May 27, 2023
bc46a64
partial progress
mattkime May 27, 2023
588e47c
version all rest routes
mattkime May 27, 2023
c9100b5
version all rest routes
mattkime May 27, 2023
96a43df
fix internal version
mattkime May 27, 2023
3f436ba
version alllll the things
mattkime May 28, 2023
0b64be4
fix fields_for_wildcard
mattkime May 28, 2023
6426e9a
fix test, create type problem
mattkime May 28, 2023
bcd8c30
fix type
mattkime May 28, 2023
5f87326
remove comment
mattkime May 28, 2023
cca761d
Merge branch 'main' into data_views_versioned_router
mattkime May 30, 2023
594080b
cleanup
mattkime May 30, 2023
27a81f7
Merge branch 'data_views_versioned_router' of github.com:mattkime/kib…
mattkime May 30, 2023
4483f98
Update src/plugins/data_views/server/rest_api_routes/default_data_vie…
mattkime May 30, 2023
efef50c
Merge branch 'main' into data_views_versioned_router
mattkime May 30, 2023
068cf4c
add response validation to create data view
mattkime May 30, 2023
a905277
Merge branch 'data_views_versioned_router' of github.com:mattkime/kib…
mattkime May 30, 2023
0c71ac0
add response validation to create data view
mattkime May 31, 2023
41029cf
response schemas and stronger typing for REST endpoints
mattkime May 31, 2023
1ccb4de
response schemas and stronger typing for REST endpoints
mattkime May 31, 2023
ab586b6
response schemas and stronger typing for REST endpoints
mattkime May 31, 2023
d2d6579
fix integration test
mattkime May 31, 2023
59c18e8
Merge branch 'main' into data_views_versioned_router
mattkime May 31, 2023
87b1f10
fix get data views schema
mattkime May 31, 2023
8f68670
fix get data views schema
mattkime May 31, 2023
0831610
runtime field routes
mattkime Jun 1, 2023
d82b2af
scripted fields
mattkime Jun 1, 2023
1c1a224
partial progress on better schema and rest response type
mattkime Jun 2, 2023
d543eb6
set response schema and type for private apis
mattkime Jun 2, 2023
eb28f00
last couple of response types and schemas
mattkime Jun 2, 2023
de51ba6
Merge branch 'main' into data_views_versioned_router
mattkime Jun 3, 2023
34f7612
add version to has_data request
mattkime Jun 3, 2023
481bd07
Merge branch 'data_views_versioned_router' of github.com:mattkime/kib…
mattkime Jun 3, 2023
692c23f
Merge branch 'main' into data_views_versioned_router
mattkime Jun 5, 2023
1898e54
response validation fix
mattkime Jun 5, 2023
59e3a2b
scripted field typefix
mattkime Jun 5, 2023
45019d9
fix delete schema
mattkime Jun 5, 2023
fd7d2ec
Merge branch 'main' into data_views_versioned_router
mattkime Jun 6, 2023
cce8311
api fixes
mattkime Jun 7, 2023
ff77f04
remove unneeded test
mattkime Jun 7, 2023
2b2bc05
Merge branch 'main' into data_views_versioned_router
mattkime Jun 12, 2023
f49f8cd
restore test, check for empty body
mattkime Jun 12, 2023
d325913
fix api functional tests
mattkime Jun 12, 2023
ceda6a4
functional test fixes
mattkime Jun 12, 2023
6b282b4
move some functional tests to ts
mattkime Jun 12, 2023
83dc781
fix functional tests
mattkime Jun 12, 2023
d7698ba
fix filter functional test
mattkime Jun 12, 2023
ed710db
add test for runtime partial update
mattkime Jun 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/http/core-http-server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export type {
VersionedRouteRequestValidation,
VersionedRouteResponseValidation,
ApiVersion,
FullValidationConfig,
VersionedRoute,
VersionedRouteConfig,
VersionedRouteRegistrar,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
export type {
ApiVersion,
AddVersionOpts,
FullValidationConfig,
VersionedRouteRequestValidation,
VersionedRouteResponseValidation,
VersionedRoute,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export interface VersionedRouteResponseValidation {
* Versioned route validation
* @experimental
*/
interface FullValidationConfig<P, Q, B> {
export interface FullValidationConfig<P, Q, B> {
/**
* Validation to run against route inputs: params, query and body
* @experimental
Expand Down
127 changes: 77 additions & 50 deletions src/plugins/data_views/common/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,52 +14,6 @@ export const serializedFieldFormatSchema = schema.object({
params: schema.maybe(schema.any()),
});

export const fieldSpecSchemaFields = {
name: schema.string({
maxLength: 1000,
}),
type: schema.string({
defaultValue: 'string',
maxLength: 1000,
}),
count: schema.maybe(
schema.number({
min: 0,
})
),
script: schema.maybe(
schema.string({
maxLength: 1000000,
})
),
format: schema.maybe(serializedFieldFormatSchema),
esTypes: schema.maybe(schema.arrayOf(schema.string())),
scripted: schema.maybe(schema.boolean()),
subType: schema.maybe(
schema.object({
multi: schema.maybe(
schema.object({
parent: schema.string(),
})
),
nested: schema.maybe(
schema.object({
path: schema.string(),
})
),
})
),
customLabel: schema.maybe(schema.string()),
shortDotsEnable: schema.maybe(schema.boolean()),
};

export const fieldSpecSchema = schema.object(fieldSpecSchemaFields, {
// Allow and ignore unknowns to make fields transient.
// Because `fields` have a bunch of calculated fields
// this allows to retrieve an index pattern and then to re-create by using the retrieved payload
unknowns: 'ignore',
});

export const RUNTIME_FIELD_TYPES2 = [
'keyword',
'long',
Expand All @@ -76,8 +30,7 @@ export const runtimeFieldNonCompositeFieldsSpecTypeSchema = schema.oneOf(
]
);

export const primitiveRuntimeFieldSchema = schema.object({
type: runtimeFieldNonCompositeFieldsSpecTypeSchema,
const primitiveRuntimeFieldSchemaShared = {
script: schema.maybe(
schema.object({
source: schema.string(),
Expand All @@ -90,10 +43,19 @@ export const primitiveRuntimeFieldSchema = schema.object({
min: 0,
})
),
};

export const primitiveRuntimeFieldSchema = schema.object({
type: runtimeFieldNonCompositeFieldsSpecTypeSchema,
...primitiveRuntimeFieldSchemaShared,
});

export const compositeRuntimeFieldSchema = schema.object({
type: schema.literal('composite') as Type<RuntimeType>,
const primitiveRuntimeFieldSchemaUpdate = schema.object({
type: schema.maybe(runtimeFieldNonCompositeFieldsSpecTypeSchema),
...primitiveRuntimeFieldSchemaShared,
});

const compositeRuntimeFieldSchemaShared = {
script: schema.maybe(
schema.object({
source: schema.string(),
Expand All @@ -114,9 +76,74 @@ export const compositeRuntimeFieldSchema = schema.object({
})
)
),
};

export const compositeRuntimeFieldSchema = schema.object({
type: schema.literal('composite') as Type<RuntimeType>,
...compositeRuntimeFieldSchemaShared,
});

const compositeRuntimeFieldSchemaUpdate = schema.object({
type: schema.maybe(schema.literal('composite') as Type<RuntimeType>),
...compositeRuntimeFieldSchemaShared,
});

export const runtimeFieldSchema = schema.oneOf([
primitiveRuntimeFieldSchema,
compositeRuntimeFieldSchema,
]);

export const runtimeFieldSchemaUpdate = schema.oneOf([
primitiveRuntimeFieldSchemaUpdate,
compositeRuntimeFieldSchemaUpdate,
]);

export const fieldSpecSchemaFields = {
name: schema.string({
maxLength: 1000,
}),
type: schema.string({
defaultValue: 'string',
maxLength: 1000,
}),
count: schema.maybe(
schema.number({
min: 0,
})
),
script: schema.maybe(
schema.string({
maxLength: 1000000,
})
),
format: schema.maybe(serializedFieldFormatSchema),
esTypes: schema.maybe(schema.arrayOf(schema.string())),
scripted: schema.maybe(schema.boolean()),
subType: schema.maybe(
schema.object({
multi: schema.maybe(
schema.object({
parent: schema.string(),
})
),
nested: schema.maybe(
schema.object({
path: schema.string(),
})
),
})
),
customLabel: schema.maybe(schema.string()),
shortDotsEnable: schema.maybe(schema.boolean()),
searchable: schema.maybe(schema.boolean()),
aggregatable: schema.maybe(schema.boolean()),
readFromDocValues: schema.maybe(schema.boolean()),
runtimeField: schema.maybe(runtimeFieldSchema),
};

export const fieldSpecSchema = schema.object(fieldSpecSchemaFields, {
// Allow and ignore unknowns to make fields transient.
// Because `fields` have a bunch of calculated fields
// this allows to retrieve an index pattern and then to re-create by using the retrieved payload
unknowns: 'ignore',
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { GetFieldsOptions, IDataViewsApiClient } from '../../common';
import { FieldsForWildcardResponse } from '../../common/types';

const API_BASE_URL: string = `/api/index_patterns/`;
const version = '1';

/**
* Data Views API Client - client implementation
Expand All @@ -29,8 +30,8 @@ export class DataViewsApiClient implements IDataViewsApiClient {

private _request<T = unknown>(url: string, query?: {}, body?: string): Promise<T | undefined> {
const request = body
? this.http.post<T>(url, { query, body })
: this.http.fetch<T>(url, { query });
? this.http.post<T>(url, { query, body, version })
: this.http.fetch<T>(url, { query, version });
return request.catch((resp) => {
if (resp.body.statusCode === 404 && resp.body.attributes?.code === 'no_matching_indices') {
throw new DataViewMissingIndices(resp.body.message);
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data_views/public/services/has_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class HasData {
// Data Views

private getHasDataViews = async ({ http }: { http: HttpStart }): Promise<HasDataViewsResponse> =>
http.get<HasDataViewsResponse>(`/internal/data_views/has_data_views`);
http.get<HasDataViewsResponse>(`/internal/data_views/has_data_views`, { version: '1' });

private hasDataViews = (http: HttpStart): Promise<boolean> => {
return this.getHasDataViews({ http })
Expand Down
12 changes: 12 additions & 0 deletions src/plugins/data_views/server/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,15 @@ export const SERVICE_KEY_LEGACY = 'index_pattern';
* Service keys as type
*/
export type SERVICE_KEY_TYPE = typeof SERVICE_KEY | typeof SERVICE_KEY_LEGACY;

/**
* Initial REST version date
*/

export const INITIAL_REST_VERSION = '2023-10-31';

/**
* Initial REST version internal
*/

export const INITIAL_REST_VERSION_INTERNAL = '1';
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@

import { estypes } from '@elastic/elasticsearch';
import { schema } from '@kbn/config-schema';
import {
IRouter,
RequestHandler,
RouteValidatorFullConfig,
StartServicesAccessor,
} from '@kbn/core/server';
import { IndexPatternsFetcher } from '../fetcher';
import type { DataViewsServerPluginStart, DataViewsServerPluginStartDependencies } from '../types';
import { IRouter, RequestHandler, StartServicesAccessor } from '@kbn/core/server';
import { FullValidationConfig } from '@kbn/core-http-server';
import { INITIAL_REST_VERSION_INTERNAL as version } from '../../constants';
import { IndexPatternsFetcher } from '../../fetcher';
import type {
DataViewsServerPluginStart,
DataViewsServerPluginStartDependencies,
} from '../../types';
import type { FieldDescriptorRestResponse } from '../route_types';

/**
* Accepts one of the following:
Expand All @@ -38,6 +39,7 @@ export const parseFields = (fields: string | string[]): string[] => {
};

const path = '/api/index_patterns/_fields_for_wildcard';
const access = 'internal';

type IBody = { index_filter?: estypes.QueryDslQueryContainer } | undefined;
interface IQuery {
Expand All @@ -50,21 +52,61 @@ interface IQuery {
fields?: string[];
}

const validate: RouteValidatorFullConfig<{}, IQuery, IBody> = {
query: schema.object({
pattern: schema.string(),
meta_fields: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], {
defaultValue: [],
}),
type: schema.maybe(schema.string()),
rollup_index: schema.maybe(schema.string()),
allow_no_index: schema.maybe(schema.boolean()),
include_unmapped: schema.maybe(schema.boolean()),
fields: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])),
const querySchema = schema.object({
pattern: schema.string(),
meta_fields: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], {
defaultValue: [],
}),
// not available to get request
body: schema.maybe(schema.object({ index_filter: schema.any() })),
type: schema.maybe(schema.string()),
rollup_index: schema.maybe(schema.string()),
allow_no_index: schema.maybe(schema.boolean()),
include_unmapped: schema.maybe(schema.boolean()),
fields: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])),
});

const fieldSubTypeSchema = schema.object({
multi: schema.maybe(schema.object({ parent: schema.string() })),
nested: schema.maybe(schema.object({ path: schema.string() })),
});

const FieldDescriptorSchema = schema.object({
aggregatable: schema.boolean(),
name: schema.string(),
readFromDocValues: schema.boolean(),
searchable: schema.boolean(),
type: schema.string(),
esTypes: schema.maybe(schema.arrayOf(schema.string())),
subType: fieldSubTypeSchema,
metadata_field: schema.maybe(schema.boolean()),
fixedInterval: schema.maybe(schema.arrayOf(schema.string())),
timeZone: schema.maybe(schema.arrayOf(schema.string())),
timeSeriesMetric: schema.maybe(
schema.oneOf([
schema.literal('histogram'),
schema.literal('summary'),
schema.literal('counter'),
schema.literal('gauge'),
])
),
timeSeriesDimension: schema.maybe(schema.boolean()),
});

const validate: FullValidationConfig<any, any, any> = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious what the types marked any are for?

Copy link
Contributor Author

@mattkime mattkime Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

export interface RouteValidatorConfig<P, Q, B> {
  /**
   * Validation logic for the URL params
   * @public
   */
  params?: RouteValidationSpec<P>;
  /**
   * Validation logic for the Query params
   * @public
   */
  query?: RouteValidationSpec<Q>;
  /**
   * Validation logic for the body payload
   * @public
   */
  body?: RouteValidationSpec<B>;
}

...I could probably do a better job using these

request: {
query: querySchema,
// not available to get request
body: schema.maybe(schema.object({ index_filter: schema.any() })),
},
response: {
200: {
body: schema.object({
fields: schema.arrayOf(FieldDescriptorSchema),
indices: schema.arrayOf(schema.string()),
}),
},
},
};

const handler: RequestHandler<{}, IQuery, IBody> = async (context, request, response) => {
const { asCurrentUser } = (await context.core).elasticsearch.client;
const indexPatterns = new IndexPatternsFetcher(asCurrentUser);
Expand Down Expand Up @@ -103,8 +145,10 @@ const handler: RequestHandler<{}, IQuery, IBody> = async (context, request, resp
...(parsedFields.length > 0 ? { fields: parsedFields } : {}),
});

const body: { fields: FieldDescriptorRestResponse[]; indices: string[] } = { fields, indices };

return response.ok({
body: { fields, indices },
body,
headers: {
'content-type': 'application/json',
},
Expand Down Expand Up @@ -136,7 +180,10 @@ export const registerFieldForWildcard = (
DataViewsServerPluginStart
>
) => {
router.put({ path, validate }, handler);
router.post({ path, validate }, handler);
router.get({ path, validate }, handler);
// handler
router.versioned.put({ path, access }).addVersion({ version, validate }, handler);
router.versioned.post({ path, access }).addVersion({ version, validate }, handler);
router.versioned
.get({ path, access })
.addVersion({ version, validate: { request: { query: querySchema } } }, handler);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import type { MockedKeys } from '@kbn/utility-types-jest';
import { CoreSetup, RequestHandlerContext } from '@kbn/core/server';
import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
import { registerHasDataViewsRoute } from './has_data_views';
import { registerHasDataViewsRoute, handler } from './has_data_views';

describe('preview has_data_views route', () => {
let mockCoreSetup: MockedKeys<CoreSetup>;
Expand Down Expand Up @@ -54,8 +54,6 @@ describe('preview has_data_views route', () => {

registerHasDataViewsRoute(mockCoreSetup.http.createRouter());

const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value;
const handler = mockRouter.get.mock.calls[0][1];
await handler(mockContext as unknown as RequestHandlerContext, mockRequest, mockResponse);

expect(mockSOClient.find.mock.calls[0][0]).toMatchInlineSnapshot(`
Expand Down
Loading