mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Console] Add logic for query params (#160515)
## Summary Fixes https://github.com/elastic/kibana/issues/160528 Follow up to https://github.com/elastic/kibana/pull/159241 This PR adds logic for query parameters to the new script generating Console autocomplete definitions from ES specification. The logic is explained in details in the [file](https://github.com/elastic/kibana/pull/160515/files#diff-b6853462c38db4e237dbb3cdec9d9f6659aa3fdc5f96a6193f2c4bac1439db43) but here is a short version: - Currently, the autocomplete engine only works with url params of 2 types: boolean (`__flag__`) and a list of options (for example `['all', 'open', 'hidden']`). The script will convert all of the url params from the specification into this format: a boolean or a list. If there are no set options for a parameter, but a default value is known, then it will be converted into a list with 1 option, for example `['random']` so that the autocomplete engine will display it as a single suggestion. We also need to convert any numbers to strings, because they won't be displayed otherwise. - Endpoints in the specification have a property `request` which in turn has 2 properties describing url params: `attachedBehaviours` and `query`. Both object contain an array of `property`'s each describing a url param. `property` is configured with either a built in type (`string`, `number`, `boolean`) or defined type, for example `ExpandWildcards`. By finding the type `ExpandWildcards` in the specification, we can convert this type to a list of options `['open', 'all', 'none', 'hidden']`. ### How to test Similar to https://github.com/elastic/kibana/pull/159241, you need to re-generenate the definitions and see if local changes to definitions files make sense. 1. Checkout the ES specification [repo](https://github.com/elastic/elasticsearch-specification) 2. Run the command `node scripts/generate_console_definitions.js --source <ES_SPECIFICATION_REPO> --emptyDest` 3. Check the changes in the folder `KIBANA_REPO/src/plugins/console/server/lib/spec_definitions/json/generated` #### Intended changes to the definitions files - Most of endpoints have 4 default url params that previously were not in the definitions files but added to all endpoints in this [file](https://github.com/elastic/kibana/blob/main/src/plugins/console/public/lib/autocomplete/url_params.js). These params are configured in the interface `CommonQueryParameters` in the specification (see this [file](https://github.com/elastic/elasticsearch-specification/blob/main/specification/_spec_utils/behaviors.ts)). <details> The interface in the specification ```js export interface CommonQueryParameters { error_trace?: boolean filter_path?: string | string[] human?: boolean pretty?: boolean } ``` The converted url params ```json "error_trace": "__flag__", "filter_path": [], "human": "__flag__", "pretty": "__flag__", ``` </details> - Previously existing `url_components` property in the definitions is deleted and this change will be addressed separately. (not sure it is currently working but added a task to the meta issue) - Previously numbers were configured as `0` or `0.0` but that is not currently displayed in suggestions. Instead, the new script converts numbers to strings and if any default value is present, it will be displayed as a suggestion. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
54dc40ff69
commit
ee6ca657ee
8 changed files with 1130 additions and 68 deletions
|
@ -1,3 +1,10 @@
|
|||
# @kbn/generate-console-definitions
|
||||
# Generate console definitions
|
||||
This package is a script to generate definitions used in Console to display autocomplete suggestions. The script is
|
||||
a new implementation of `kbn-spec-to-console` package: The old script uses [JSON specs](https://github.com/elastic/elasticsearch/tree/main/rest-api-spec) from the Elasticsearch repo as the source, whereas this script uses the Elasticsearch specification [repo](https://github.com/elastic/elasticsearch-specification) as the source.
|
||||
|
||||
## Instructions
|
||||
1. Checkout the Elasticsearch specification [repo](https://github.com/elastic/elasticsearch-specification).
|
||||
2. Run the command `node scripts/generate_console_definitions.js --source <ES_SPECIFICATION_REPO> --emptyDest`
|
||||
This command will use the folder `<ES_SPECIFICATION_REPO>` as the source and the constant [`AUTOCOMPLETE_DEFINITIONS_FOLDER`](https://github.com/elastic/kibana/blob/main/src/plugins/console/common/constants/autocomplete_definitions.ts) as the destination. Based on the value of the constant, the autocomplete definitions will be generated in the folder `<KIBANA_REPO>/src/plugins/server/lib/spec_definitions/json/generated`. Using the flag `--emptyDest` will remove any existing files in the destination folder.
|
||||
3. It's possible to generate the definitions into a different folder. For that pass an option to the command `--dest <DEFINITIONS_FOLDER>` and also update the constant [`AUTOCOMPLETE_DEFINITIONS_FOLDER`](https://github.com/elastic/kibana/blob/main/src/plugins/console/common/constants/autocomplete_definitions.ts) so that the Console server will load the definitions from this folder.
|
||||
|
||||
Empty package generated by @kbn/generate
|
||||
|
|
|
@ -9,51 +9,16 @@
|
|||
import fs from 'fs';
|
||||
import Path, { join } from 'path';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import { generateQueryParams } from './generate_query_params';
|
||||
import type {
|
||||
AutocompleteBodyParams,
|
||||
AutocompleteDefinition,
|
||||
AutocompleteUrlParams,
|
||||
SpecificationTypes,
|
||||
} from './types';
|
||||
import { findTypeDefinition } from './utils';
|
||||
|
||||
interface EndpointRequest {
|
||||
name: string;
|
||||
namespace: string;
|
||||
}
|
||||
|
||||
interface Endpoint {
|
||||
name: string;
|
||||
urls: Array<{
|
||||
methods: string[];
|
||||
path: string;
|
||||
}>;
|
||||
docUrl: string;
|
||||
request: null | EndpointRequest;
|
||||
}
|
||||
|
||||
interface SchemaType {
|
||||
name: {
|
||||
name: string;
|
||||
namespace: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface Schema {
|
||||
endpoints: Endpoint[];
|
||||
types: SchemaType[];
|
||||
}
|
||||
|
||||
interface UrlParams {
|
||||
[key: string]: number | string;
|
||||
}
|
||||
|
||||
interface BodyParams {
|
||||
[key: string]: number | string;
|
||||
}
|
||||
|
||||
interface Definition {
|
||||
documentation?: string;
|
||||
methods: string[];
|
||||
patterns: string[];
|
||||
url_params?: UrlParams;
|
||||
data_autocomplete_rules?: BodyParams;
|
||||
}
|
||||
|
||||
const generateMethods = (endpoint: Endpoint): string[] => {
|
||||
const generateMethods = (endpoint: SpecificationTypes.Endpoint): string[] => {
|
||||
// this array consists of arrays of strings
|
||||
const methodsArray = endpoint.urls.map((url) => url.methods);
|
||||
// flatten to return array of strings
|
||||
|
@ -62,7 +27,7 @@ const generateMethods = (endpoint: Endpoint): string[] => {
|
|||
return [...new Set(flattenMethodsArray)];
|
||||
};
|
||||
|
||||
const generatePatterns = (endpoint: Endpoint): string[] => {
|
||||
const generatePatterns = (endpoint: SpecificationTypes.Endpoint): string[] => {
|
||||
return endpoint.urls.map(({ path }) => {
|
||||
let pattern = path;
|
||||
// remove leading / if present
|
||||
|
@ -73,42 +38,37 @@ const generatePatterns = (endpoint: Endpoint): string[] => {
|
|||
});
|
||||
};
|
||||
|
||||
const generateDocumentation = (endpoint: Endpoint): string => {
|
||||
const generateDocumentation = (endpoint: SpecificationTypes.Endpoint): string => {
|
||||
return endpoint.docUrl;
|
||||
};
|
||||
|
||||
const generateParams = (
|
||||
endpoint: Endpoint,
|
||||
schema: Schema
|
||||
): { urlParams: UrlParams; bodyParams: BodyParams } | undefined => {
|
||||
endpoint: SpecificationTypes.Endpoint,
|
||||
schema: SpecificationTypes.Model
|
||||
): { urlParams: AutocompleteUrlParams; bodyParams: AutocompleteBodyParams } | undefined => {
|
||||
const { request } = endpoint;
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
const requestType = schema.types.find(
|
||||
({ name: { name, namespace } }) => name === request.name && namespace === request.namespace
|
||||
);
|
||||
const requestType = findTypeDefinition(schema, request);
|
||||
if (!requestType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const urlParams = generateUrlParams(requestType);
|
||||
const urlParams = generateQueryParams(requestType as SpecificationTypes.Request, schema);
|
||||
const bodyParams = generateBodyParams(requestType);
|
||||
return { urlParams, bodyParams };
|
||||
};
|
||||
|
||||
const generateUrlParams = (requestType: SchemaType): UrlParams => {
|
||||
return {};
|
||||
};
|
||||
|
||||
const generateBodyParams = (requestType: SchemaType): BodyParams => {
|
||||
const generateBodyParams = (
|
||||
requestType: SpecificationTypes.TypeDefinition
|
||||
): AutocompleteBodyParams => {
|
||||
return {};
|
||||
};
|
||||
|
||||
const addParams = (
|
||||
definition: Definition,
|
||||
params: { urlParams: UrlParams; bodyParams: BodyParams }
|
||||
): Definition => {
|
||||
definition: AutocompleteDefinition,
|
||||
params: { urlParams: AutocompleteUrlParams; bodyParams: AutocompleteBodyParams }
|
||||
): AutocompleteDefinition => {
|
||||
const { urlParams, bodyParams } = params;
|
||||
if (urlParams && Object.keys(urlParams).length > 0) {
|
||||
definition.url_params = urlParams;
|
||||
|
@ -119,15 +79,19 @@ const addParams = (
|
|||
return definition;
|
||||
};
|
||||
|
||||
const generateDefinition = (endpoint: Endpoint, schema: Schema): Definition => {
|
||||
const generateDefinition = (
|
||||
endpoint: SpecificationTypes.Endpoint,
|
||||
schema: SpecificationTypes.Model
|
||||
): AutocompleteDefinition => {
|
||||
const methods = generateMethods(endpoint);
|
||||
const patterns = generatePatterns(endpoint);
|
||||
const documentation = generateDocumentation(endpoint);
|
||||
let definition: Definition = { methods, patterns, documentation };
|
||||
let definition: AutocompleteDefinition = {};
|
||||
const params = generateParams(endpoint, schema);
|
||||
if (params) {
|
||||
definition = addParams(definition, params);
|
||||
}
|
||||
definition = { ...definition, methods, patterns, documentation };
|
||||
|
||||
return definition;
|
||||
};
|
||||
|
@ -143,7 +107,7 @@ export function generateConsoleDefinitions({
|
|||
}) {
|
||||
const pathToSchemaFile = Path.resolve(specsRepo, 'output/schema/schema.json');
|
||||
log.info('loading the ES specification schema file');
|
||||
const schema = JSON.parse(fs.readFileSync(pathToSchemaFile, 'utf8')) as Schema;
|
||||
const schema = JSON.parse(fs.readFileSync(pathToSchemaFile, 'utf8')) as SpecificationTypes.Model;
|
||||
|
||||
const { endpoints } = schema;
|
||||
log.info(`iterating over endpoints array: ${endpoints.length} endpoints`);
|
||||
|
@ -151,7 +115,7 @@ export function generateConsoleDefinitions({
|
|||
const { name } = endpoint;
|
||||
log.info(name);
|
||||
const definition = generateDefinition(endpoint, schema);
|
||||
const fileContent: { [name: string]: Definition } = {
|
||||
const fileContent: { [name: string]: AutocompleteDefinition } = {
|
||||
[name]: definition,
|
||||
};
|
||||
fs.writeFileSync(
|
||||
|
|
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { SpecificationTypes } from './types';
|
||||
import { generateQueryParams } from './generate_query_params';
|
||||
import { UrlParamValue } from './types/autocomplete_definition_types';
|
||||
|
||||
describe('generateQueryParams', () => {
|
||||
const mockRequestType: SpecificationTypes.Request = {
|
||||
body: { kind: 'no_body' },
|
||||
kind: 'request',
|
||||
name: {
|
||||
name: 'TestRequest',
|
||||
namespace: 'test.namespace',
|
||||
},
|
||||
path: [],
|
||||
query: [],
|
||||
specLocation: '',
|
||||
};
|
||||
|
||||
const getMockProperty = ({
|
||||
propertyName,
|
||||
typeName,
|
||||
serverDefault,
|
||||
type,
|
||||
}: {
|
||||
propertyName: string;
|
||||
typeName?: SpecificationTypes.TypeName;
|
||||
serverDefault?: SpecificationTypes.Property['serverDefault'];
|
||||
type?: SpecificationTypes.ValueOf;
|
||||
}): SpecificationTypes.Property => {
|
||||
return {
|
||||
description: 'Description',
|
||||
name: propertyName,
|
||||
required: false,
|
||||
serverDefault: serverDefault ?? undefined,
|
||||
type: type ?? {
|
||||
kind: 'instance_of',
|
||||
type: typeName ?? {
|
||||
name: 'string',
|
||||
namespace: '_builtins',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const mockSchema: SpecificationTypes.Model = {
|
||||
endpoints: [],
|
||||
types: [],
|
||||
};
|
||||
|
||||
it('iterates over attachedBehaviours', () => {
|
||||
const behaviour1: SpecificationTypes.Interface = {
|
||||
kind: 'interface',
|
||||
name: {
|
||||
name: 'behaviour1',
|
||||
namespace: 'test.namespace',
|
||||
},
|
||||
properties: [getMockProperty({ propertyName: 'property1' })],
|
||||
specLocation: '',
|
||||
};
|
||||
const behaviour2: SpecificationTypes.Interface = {
|
||||
kind: 'interface',
|
||||
name: {
|
||||
name: 'behaviour2',
|
||||
namespace: 'test.namespace',
|
||||
},
|
||||
properties: [
|
||||
getMockProperty({ propertyName: 'property2' }),
|
||||
getMockProperty({ propertyName: 'property3' }),
|
||||
],
|
||||
specLocation: '',
|
||||
};
|
||||
const schema: SpecificationTypes.Model = {
|
||||
...mockSchema,
|
||||
types: [behaviour1, behaviour2],
|
||||
};
|
||||
const requestType: SpecificationTypes.Request = {
|
||||
...mockRequestType,
|
||||
attachedBehaviors: ['behaviour1', 'behaviour2'],
|
||||
};
|
||||
const urlParams = generateQueryParams(requestType, schema);
|
||||
expect(urlParams).toEqual({
|
||||
property1: '',
|
||||
property2: '',
|
||||
property3: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('iterates over query properties', () => {
|
||||
const requestType = {
|
||||
...mockRequestType,
|
||||
query: [
|
||||
getMockProperty({ propertyName: 'property1' }),
|
||||
getMockProperty({ propertyName: 'property2' }),
|
||||
],
|
||||
};
|
||||
const urlParams = generateQueryParams(requestType, mockSchema);
|
||||
expect(urlParams).toEqual({
|
||||
property1: '',
|
||||
property2: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('converts builtin types', () => {
|
||||
const stringProperty = getMockProperty({
|
||||
propertyName: 'stringProperty',
|
||||
typeName: { name: 'string', namespace: '_builtins' },
|
||||
});
|
||||
const numberProperty = getMockProperty({
|
||||
propertyName: 'numberProperty',
|
||||
typeName: { name: 'number', namespace: '_builtins' },
|
||||
});
|
||||
const booleanProperty = getMockProperty({
|
||||
propertyName: 'booleanProperty',
|
||||
typeName: { name: 'boolean', namespace: '_builtins' },
|
||||
});
|
||||
const requestType = {
|
||||
...mockRequestType,
|
||||
query: [stringProperty, numberProperty, booleanProperty],
|
||||
};
|
||||
const urlParams = generateQueryParams(requestType, mockSchema);
|
||||
expect(urlParams).toEqual({
|
||||
stringProperty: '',
|
||||
numberProperty: '',
|
||||
booleanProperty: '__flag__',
|
||||
});
|
||||
});
|
||||
|
||||
it('adds serverDefault value if any', () => {
|
||||
const propertyWithDefault = getMockProperty({
|
||||
propertyName: 'propertyWithDefault',
|
||||
serverDefault: 'default',
|
||||
});
|
||||
const requestType = { ...mockRequestType, query: [propertyWithDefault] };
|
||||
const urlParams = generateQueryParams(requestType, mockSchema);
|
||||
expect(urlParams).toEqual({
|
||||
propertyWithDefault: ['default'],
|
||||
});
|
||||
});
|
||||
|
||||
it('converts an enum property', () => {
|
||||
const enumProperty = getMockProperty({
|
||||
propertyName: 'enumProperty',
|
||||
typeName: { name: 'EnumType', namespace: 'test.namespace' },
|
||||
});
|
||||
const enumType: SpecificationTypes.Enum = {
|
||||
kind: 'enum',
|
||||
members: [
|
||||
{
|
||||
name: 'enum1',
|
||||
},
|
||||
{
|
||||
name: 'enum2',
|
||||
},
|
||||
],
|
||||
name: {
|
||||
name: 'EnumType',
|
||||
namespace: 'test.namespace',
|
||||
},
|
||||
specLocation: '',
|
||||
};
|
||||
const requestType = { ...mockRequestType, query: [enumProperty] };
|
||||
const schema = { ...mockSchema, types: [enumType] };
|
||||
const urlParams = generateQueryParams(requestType, schema);
|
||||
expect(urlParams).toEqual({
|
||||
enumProperty: ['enum1', 'enum2'],
|
||||
});
|
||||
});
|
||||
|
||||
it('converts a type alias', () => {
|
||||
const typeAliasProperty = getMockProperty({
|
||||
propertyName: 'typeAliasProperty',
|
||||
typeName: {
|
||||
name: 'SomeTypeAlias',
|
||||
namespace: 'test.namespace',
|
||||
},
|
||||
});
|
||||
const typeAliasType: SpecificationTypes.TypeAlias = {
|
||||
kind: 'type_alias',
|
||||
name: {
|
||||
name: 'SomeTypeAlias',
|
||||
namespace: 'test.namespace',
|
||||
},
|
||||
specLocation: '',
|
||||
type: {
|
||||
kind: 'instance_of',
|
||||
type: {
|
||||
name: 'integer',
|
||||
namespace: '_types',
|
||||
},
|
||||
},
|
||||
};
|
||||
const requestType = { ...mockRequestType, query: [typeAliasProperty] };
|
||||
const schema: SpecificationTypes.Model = { ...mockSchema, types: [typeAliasType] };
|
||||
const urlParams = generateQueryParams(requestType, schema);
|
||||
expect(urlParams).toEqual({
|
||||
typeAliasProperty: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('converts a literal_value to a string', () => {
|
||||
const stringProperty = getMockProperty({
|
||||
propertyName: 'stringProperty',
|
||||
type: { kind: 'literal_value', value: 'stringValue' },
|
||||
});
|
||||
const numberProperty = getMockProperty({
|
||||
propertyName: 'numberProperty',
|
||||
type: { kind: 'literal_value', value: 14 },
|
||||
});
|
||||
const booleanProperty = getMockProperty({
|
||||
propertyName: 'booleanProperty',
|
||||
type: { kind: 'literal_value', value: true },
|
||||
});
|
||||
const requestType = {
|
||||
...mockRequestType,
|
||||
query: [stringProperty, numberProperty, booleanProperty],
|
||||
};
|
||||
const urlParams = generateQueryParams(requestType, mockSchema);
|
||||
expect(urlParams).toEqual({
|
||||
stringProperty: ['stringValue'],
|
||||
numberProperty: ['14'],
|
||||
booleanProperty: ['true'],
|
||||
});
|
||||
});
|
||||
|
||||
describe('converts a union_of', () => {
|
||||
it('flattens the array if one of the items is converted to an array', () => {
|
||||
const enumType: SpecificationTypes.Enum = {
|
||||
kind: 'enum',
|
||||
members: [
|
||||
{
|
||||
name: 'enum1',
|
||||
},
|
||||
{ name: 'enum2' },
|
||||
],
|
||||
name: { name: 'EnumType', namespace: 'test.namespace' },
|
||||
specLocation: '',
|
||||
};
|
||||
const unionProperty = getMockProperty({
|
||||
propertyName: 'unionProperty',
|
||||
type: {
|
||||
kind: 'union_of',
|
||||
items: [
|
||||
{
|
||||
kind: 'instance_of',
|
||||
type: {
|
||||
name: 'EnumType',
|
||||
namespace: 'test.namespace',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const requestType = { ...mockRequestType, query: [unionProperty] };
|
||||
const schema: SpecificationTypes.Model = { ...mockSchema, types: [enumType] };
|
||||
const urlParams = generateQueryParams(requestType, schema);
|
||||
expect(urlParams).toEqual({
|
||||
unionProperty: ['enum1', 'enum2'],
|
||||
});
|
||||
});
|
||||
|
||||
it('removes empty string from the array', () => {
|
||||
const unionProperty = getMockProperty({
|
||||
propertyName: 'unionProperty',
|
||||
type: {
|
||||
kind: 'union_of',
|
||||
items: [
|
||||
{
|
||||
kind: 'instance_of',
|
||||
type: {
|
||||
name: 'string',
|
||||
namespace: '_builtins',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const requestType = { ...mockRequestType, query: [unionProperty] };
|
||||
const urlParams = generateQueryParams(requestType, mockSchema);
|
||||
expect(urlParams).toEqual({
|
||||
unionProperty: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('if one item is a boolean and others are empty, converts to a flag', () => {
|
||||
const unionProperty = getMockProperty({
|
||||
propertyName: 'unionProperty',
|
||||
type: {
|
||||
kind: 'union_of',
|
||||
items: [
|
||||
{
|
||||
kind: 'instance_of',
|
||||
type: {
|
||||
name: 'string',
|
||||
namespace: '_builtins',
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'instance_of',
|
||||
type: {
|
||||
name: 'number',
|
||||
namespace: '_builtins',
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'instance_of',
|
||||
type: {
|
||||
name: 'boolean',
|
||||
namespace: '_builtins',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const requestType = { ...mockRequestType, query: [unionProperty] };
|
||||
const urlParams = generateQueryParams(requestType, mockSchema);
|
||||
expect(urlParams).toEqual({
|
||||
unionProperty: '__flag__',
|
||||
});
|
||||
});
|
||||
|
||||
it('if one item is an unknown type, converts it to an empty string', () => {
|
||||
const unionProperty = getMockProperty({
|
||||
propertyName: 'unionProperty',
|
||||
type: {
|
||||
kind: 'union_of',
|
||||
items: [
|
||||
{
|
||||
kind: 'literal_value',
|
||||
value: 'test',
|
||||
},
|
||||
{
|
||||
kind: 'instance_of',
|
||||
type: {
|
||||
name: 'UnknownType',
|
||||
namespace: 'test.namespace',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const requestType = { ...mockRequestType, query: [unionProperty] };
|
||||
const urlParams = generateQueryParams(requestType, mockSchema);
|
||||
// check that no `undefined` values are added
|
||||
const value = urlParams.unionProperty as UrlParamValue[];
|
||||
expect(value.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('converts an unknown type to an empty string', () => {
|
||||
const unknownTypeProperty = getMockProperty({
|
||||
propertyName: 'unknownTypeProperty',
|
||||
type: {
|
||||
kind: 'instance_of',
|
||||
type: {
|
||||
name: 'UnknownType',
|
||||
namespace: 'test.namespace',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const requestType = { ...mockRequestType, query: [unknownTypeProperty] };
|
||||
const urlParams = generateQueryParams(requestType, mockSchema);
|
||||
expect(urlParams).toEqual({
|
||||
unknownTypeProperty: '',
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Types that are important for query params conversion:
|
||||
* TypeDefinition = Interface | Request | Response | Enum | TypeAlias
|
||||
* ValueOf = InstanceOf | ArrayOf | UnionOf | DictionaryOf | UserDefinedValue | LiteralValue;
|
||||
*
|
||||
* Conversion steps:
|
||||
* 1. The schema has a property `endpoints` which is "Endpoint[]"
|
||||
* 2. Each "Endpoint" has a property `request` which is "TypeName"
|
||||
* 3. Using "TypeName" we find the "TypeDefinition" in the property `types` of the schema
|
||||
* 4. the "TypeDefinition" is cast to "Request"
|
||||
* - "Request" has a property `query` which is "Property[]"
|
||||
* - "Request" has a property `attachedBehaviours` which is "string[]"
|
||||
* With "string" we find a "TypeDefinition" that is "Interface"
|
||||
* This "Interface" has a property `properties` which is "Property[]"
|
||||
* 5. Each "Property" (from both `query` and `attachedBehaviours`) now can be converted
|
||||
* 6. Each "Property" has a property `type` that is "ValueOf"
|
||||
* 7. If "ValueOf" can be one of "InstanceOf", "ArrayOf", "UnionOf", "DictionaryOf", "UserDefinedValue", "LiteralValue"
|
||||
* - "InstanceOf": it has a property `type` which is a "TypeName"
|
||||
* - if "TypeName" has a `namespace` = "_builtins" then it's a primitive type like "string" -> convert according to set rules for primitives
|
||||
* - if "TypeName" has a `namespace` = "_types" then it's a defined type that can be found in the schema
|
||||
* - the found "TypeDefinition" can be either "Enum" or "TypeAlias" (not "Interface", "Request" or "Response")
|
||||
* - if it's "TypeAlias", it has a property `type` which is "ValueOf" -> handle it as "ValueOf" (recursion)
|
||||
* - if it's "Enum", it has a property `members` which is "EnumMember[]" -> convert each "EnumMember" (only need `name` property)
|
||||
* - "ArrayOf": it has a property `value` which is "ValueOf" -> convert as "ValueOf"
|
||||
* - "UnionOf": it has a property `items` which is "ValueOf[]" -> convert each as "ValueOf"
|
||||
* - "DictionaryOf": not used for query params
|
||||
* - "UserDefinedValue": not used for query params
|
||||
* - "LiteralValue": it has `value` that is `string`, `number` or `boolean`
|
||||
*
|
||||
* Autocomplete definitions currently work with 2 url param types:
|
||||
* - "__flag__" for a boolean (suggesting value 'true' and 'false')
|
||||
* - list of options in an array, for example ['30s', '-1', '0'], suggesting all 3 values in a list
|
||||
* If there is only a default value, we need to wrap it in an array, so that this value is displayed in a suggestion (similar to the list).
|
||||
* Numbers need to be converted to strings, otherwise they are not displayed as suggestions.
|
||||
*
|
||||
*/
|
||||
|
||||
import { UrlParamValue } from './types/autocomplete_definition_types';
|
||||
import type { AutocompleteUrlParams, SpecificationTypes } from './types';
|
||||
import { findTypeDefinition } from './utils';
|
||||
|
||||
const booleanFlagString = '__flag__';
|
||||
const trueValueString = String(true);
|
||||
const falseValueString = String(false);
|
||||
|
||||
export const generateQueryParams = (
|
||||
requestType: SpecificationTypes.Request,
|
||||
schema: SpecificationTypes.Model
|
||||
): AutocompleteUrlParams => {
|
||||
let urlParams = {} as AutocompleteUrlParams;
|
||||
const { types } = schema;
|
||||
const { attachedBehaviors, query } = requestType;
|
||||
// if there are any attached behaviors, iterate over each and find its type
|
||||
if (attachedBehaviors) {
|
||||
for (const attachedBehavior of attachedBehaviors) {
|
||||
const foundBehavior = types.find((type) => type.name.name === attachedBehavior);
|
||||
if (foundBehavior) {
|
||||
// attached behaviours are interfaces
|
||||
const behaviorType = foundBehavior as SpecificationTypes.Interface;
|
||||
// if there are any properties in the behavior, iterate over each and add it to url params
|
||||
const { properties } = behaviorType;
|
||||
urlParams = convertProperties(properties, urlParams, schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over properties in query and add it to url params
|
||||
urlParams = convertProperties(query, urlParams, schema);
|
||||
|
||||
return urlParams;
|
||||
};
|
||||
|
||||
const convertProperties = (
|
||||
properties: SpecificationTypes.Property[],
|
||||
urlParams: AutocompleteUrlParams,
|
||||
schema: SpecificationTypes.Model
|
||||
): AutocompleteUrlParams => {
|
||||
for (const property of properties) {
|
||||
const { name, serverDefault, type } = property;
|
||||
// property has `type` which is `ValueOf`
|
||||
const convertedValue = convertValueOf(type, serverDefault, schema);
|
||||
urlParams[name] = convertedValue ?? '';
|
||||
}
|
||||
return urlParams;
|
||||
};
|
||||
|
||||
const convertValueOf = (
|
||||
valueOf: SpecificationTypes.ValueOf,
|
||||
serverDefault: SpecificationTypes.Property['serverDefault'],
|
||||
schema: SpecificationTypes.Model
|
||||
): UrlParamValue | undefined => {
|
||||
const { kind } = valueOf;
|
||||
if (kind === 'instance_of') {
|
||||
return convertInstanceOf(valueOf, serverDefault, schema);
|
||||
} else if (kind === 'array_of') {
|
||||
return convertArrayOf(valueOf, serverDefault, schema);
|
||||
} else if (kind === 'union_of') {
|
||||
return convertUnionOf(valueOf, serverDefault, schema);
|
||||
} else if (kind === 'literal_value') {
|
||||
return convertLiteralValue(valueOf);
|
||||
}
|
||||
// for query params we can ignore 'dictionary_of' and 'user_defined_value'
|
||||
return '';
|
||||
};
|
||||
|
||||
const convertInstanceOf = (
|
||||
type: SpecificationTypes.InstanceOf,
|
||||
serverDefault: SpecificationTypes.Property['serverDefault'],
|
||||
schema: SpecificationTypes.Model
|
||||
): UrlParamValue | undefined => {
|
||||
const { type: typeName } = type;
|
||||
const { name: propertyName, namespace } = typeName;
|
||||
if (namespace === '_builtins') {
|
||||
/**
|
||||
* - `string`
|
||||
* - `boolean`
|
||||
* - `number`
|
||||
* - `null` // ignore for query params
|
||||
* - `void` // ignore for query params
|
||||
* - `binary` // ignore for query params
|
||||
*/
|
||||
|
||||
if (propertyName === 'boolean') {
|
||||
// boolean is converted to a flag param
|
||||
return booleanFlagString;
|
||||
} else {
|
||||
// if default value, convert to string and put in an array
|
||||
return serverDefault ? [serverDefault.toString()] : '';
|
||||
}
|
||||
} else {
|
||||
// if it's a defined type, try to convert it
|
||||
const definedType = findTypeDefinition(schema, typeName);
|
||||
if (definedType) {
|
||||
// TypeDefinition can only be Enum or TypeAlias
|
||||
if (definedType.kind === 'enum') {
|
||||
return convertEnum(definedType as SpecificationTypes.Enum);
|
||||
} else if (definedType.kind === 'type_alias') {
|
||||
const aliasValueOf = definedType.type;
|
||||
return convertValueOf(aliasValueOf, serverDefault, schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const convertArrayOf = (
|
||||
type: SpecificationTypes.ArrayOf,
|
||||
serverDefault: SpecificationTypes.Property['serverDefault'],
|
||||
schema: SpecificationTypes.Model
|
||||
): UrlParamValue | undefined => {
|
||||
const { value } = type;
|
||||
// simply convert the value of an array item
|
||||
return convertValueOf(value, serverDefault, schema);
|
||||
};
|
||||
|
||||
const convertUnionOf = (
|
||||
type: SpecificationTypes.UnionOf,
|
||||
serverDefault: SpecificationTypes.Property['serverDefault'],
|
||||
schema: SpecificationTypes.Model
|
||||
): UrlParamValue | undefined => {
|
||||
const { items } = type;
|
||||
const itemValues = new Set();
|
||||
for (const item of items) {
|
||||
// each item is ValueOf
|
||||
const convertedValue = convertValueOf(item, serverDefault, schema);
|
||||
// flatten array if needed
|
||||
if (convertedValue instanceof Array) {
|
||||
convertedValue.forEach((v) => itemValues.add(v));
|
||||
} else itemValues.add(convertedValue);
|
||||
}
|
||||
|
||||
// if an empty string is in values, delete it
|
||||
if (itemValues.has('')) {
|
||||
itemValues.delete('');
|
||||
}
|
||||
|
||||
// if there is a flag in the values, convert it to "true" + "false"
|
||||
if (itemValues.size > 1 && itemValues.has(booleanFlagString)) {
|
||||
itemValues.delete(booleanFlagString);
|
||||
itemValues.add(trueValueString);
|
||||
itemValues.add(falseValueString);
|
||||
}
|
||||
|
||||
// if only 2 values ("true","false"), convert back to a flag
|
||||
// that can happen if the values before were ("true", "__flag__") or ("false", "__flag__")
|
||||
if (
|
||||
itemValues.size === 2 &&
|
||||
itemValues.has(trueValueString) &&
|
||||
itemValues.has(falseValueString)
|
||||
) {
|
||||
itemValues.clear();
|
||||
itemValues.add(booleanFlagString);
|
||||
}
|
||||
|
||||
// if only 1 element that is a flag, don't put it in an array
|
||||
if (itemValues.size === 1 && itemValues.has(booleanFlagString)) {
|
||||
return itemValues.values().next().value;
|
||||
}
|
||||
return [...itemValues] as UrlParamValue;
|
||||
};
|
||||
|
||||
const convertLiteralValue = (type: SpecificationTypes.LiteralValue): UrlParamValue | undefined => {
|
||||
// convert the value to a string
|
||||
return [type.value.toString()];
|
||||
};
|
||||
|
||||
const convertEnum = (enumDefinition: SpecificationTypes.Enum): UrlParamValue => {
|
||||
const { members } = enumDefinition;
|
||||
// only need the `name` property
|
||||
return members.map((member) => member.name);
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
export type UrlParamValue = number | string | number[] | string[] | boolean;
|
||||
export interface AutocompleteUrlParams {
|
||||
[key: string]: UrlParamValue;
|
||||
}
|
||||
|
||||
export interface AutocompleteBodyParams {
|
||||
[key: string]: number | string;
|
||||
}
|
||||
|
||||
export interface AutocompleteDefinition {
|
||||
documentation?: string;
|
||||
methods?: string[];
|
||||
patterns?: string[];
|
||||
url_params?: AutocompleteUrlParams;
|
||||
data_autocomplete_rules?: AutocompleteBodyParams;
|
||||
}
|
15
packages/kbn-generate-console-definitions/src/types/index.ts
Normal file
15
packages/kbn-generate-console-definitions/src/types/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export type {
|
||||
AutocompleteDefinition,
|
||||
AutocompleteUrlParams,
|
||||
AutocompleteBodyParams,
|
||||
} from './autocomplete_definition_types';
|
||||
|
||||
export * as SpecificationTypes from './specification_types';
|
|
@ -0,0 +1,442 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/**
|
||||
* --------------- THIS FILE IS COPIED FROM ES SPECIFICATION REPO -------------------
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* The name of a type, composed of a simple name and a namespace. Hierarchical namespace elements are separated by
|
||||
* a dot, e.g 'cat.cat_aliases'.
|
||||
*
|
||||
* Builtin namespaces:
|
||||
* - "generic" for type names that are generic parameter values from the enclosing type.
|
||||
* - "internal" for primitive and builtin types (e.g. Id, IndexName, etc)
|
||||
* Builtin types:
|
||||
* - boolean,
|
||||
* - string,
|
||||
* - number: a 64bits floating point number. Additional types will be added for integers.
|
||||
* - null: the null value. Since JS distinguishes undefined and null, some APIs make use of this value.
|
||||
* - object: used to represent "any". We may forbid it at some point. UserDefinedValue should be used for user data.
|
||||
*/
|
||||
export interface TypeName {
|
||||
namespace: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Value types
|
||||
|
||||
// Note: "required" is part of Property. This means we can have optional properties but we can't have null entries in
|
||||
// containers (array and dictionary), which doesn't seem to be needed.
|
||||
//
|
||||
// The 'kind' property is used to tag and disambiguate union type members, and allow type-safe pattern matching in TS:
|
||||
// see https://blog.logrocket.com/pattern-matching-and-type-safety-in-typescript-1da1231a2e34/
|
||||
// and https://medium.com/@fillopeter/pattern-matching-with-typescript-done-right-94049ddd671c
|
||||
|
||||
/**
|
||||
* Type of a value. Used both for property types and nested type definitions.
|
||||
*/
|
||||
export type ValueOf =
|
||||
| InstanceOf
|
||||
| ArrayOf
|
||||
| UnionOf
|
||||
| DictionaryOf
|
||||
| UserDefinedValue
|
||||
| LiteralValue;
|
||||
|
||||
/**
|
||||
* A single value
|
||||
*/
|
||||
export interface InstanceOf {
|
||||
kind: 'instance_of';
|
||||
type: TypeName;
|
||||
/** generic parameters: either concrete types or open parameters from the enclosing type */
|
||||
generics?: ValueOf[];
|
||||
}
|
||||
|
||||
/**
|
||||
* An array
|
||||
*/
|
||||
export interface ArrayOf {
|
||||
kind: 'array_of';
|
||||
value: ValueOf;
|
||||
}
|
||||
|
||||
/**
|
||||
* One of several possible types which don't necessarily have a common superinterface
|
||||
*/
|
||||
export interface UnionOf {
|
||||
kind: 'union_of';
|
||||
items: ValueOf[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A dictionary (or map). The key is a string or a number (or a union thereof), possibly through an alias.
|
||||
*
|
||||
* If `singleKey` is true, then this dictionary can only have a single key. This is a common pattern in ES APIs,
|
||||
* used to associate a value to a field name or some other identifier.
|
||||
*/
|
||||
export interface DictionaryOf {
|
||||
kind: 'dictionary_of';
|
||||
key: ValueOf;
|
||||
value: ValueOf;
|
||||
singleKey: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A user defined value. To be used when bubbling a generic parameter up to the top-level interface is
|
||||
* inconvenient or impossible (e.g. for lists of user-defined values of possibly different types).
|
||||
*
|
||||
* Clients will allow providing a serializer/deserializer when reading/writing properties of this type,
|
||||
* and should also accept raw json.
|
||||
*
|
||||
* Think twice before using this as it defeats the purpose of a strongly typed API, and deserialization
|
||||
* will also require to buffer raw JSON data which may have performance implications.
|
||||
*/
|
||||
export interface UserDefinedValue {
|
||||
kind: 'user_defined_value';
|
||||
}
|
||||
|
||||
/**
|
||||
* A literal value. This is used for tagged unions, where each type member of a union has a 'type'
|
||||
* attribute that defines its kind. This metamodel heavily uses this approach with its 'kind' attributes.
|
||||
*
|
||||
* It may later be used to set a property to a constant value, which is why it accepts not only strings but also
|
||||
* other primitive types.
|
||||
*/
|
||||
export interface LiteralValue {
|
||||
kind: 'literal_value';
|
||||
value: string | number | boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface or request interface property.
|
||||
*/
|
||||
export interface Property {
|
||||
name: string;
|
||||
type: ValueOf;
|
||||
required: boolean;
|
||||
description?: string;
|
||||
docUrl?: string;
|
||||
docId?: string;
|
||||
since?: string;
|
||||
serverDefault?: boolean | string | number | string[] | number[];
|
||||
deprecation?: Deprecation;
|
||||
availability?: Availabilities;
|
||||
stability?: Stability;
|
||||
/**
|
||||
* If specified takes precedence over `name` when generating code. `name` is always the value
|
||||
* to be sent over the wire
|
||||
*/
|
||||
codegenName?: string;
|
||||
/** An optional set of aliases for `name` */
|
||||
aliases?: string[];
|
||||
/** If the enclosing interface is a variants container, is this a property of the container and not a variant? */
|
||||
containerProperty?: boolean;
|
||||
/** If this property has a quirk that needs special attention, give a short explanation about it */
|
||||
esQuirk?: string;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Type definitions
|
||||
|
||||
export type TypeDefinition = Interface | Request | Response | Enum | TypeAlias;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Common attributes for all type definitions
|
||||
*/
|
||||
export interface BaseType {
|
||||
name: TypeName;
|
||||
description?: string;
|
||||
/** Link to public documentation */
|
||||
docUrl?: string;
|
||||
docId?: string;
|
||||
deprecation?: Deprecation;
|
||||
/** If this endpoint has a quirk that needs special attention, give a short explanation about it */
|
||||
esQuirk?: string;
|
||||
kind: string;
|
||||
/** Variant name for externally tagged variants */
|
||||
variantName?: string;
|
||||
/**
|
||||
* Additional identifiers for use by code generators. Usage depends on the actual type:
|
||||
* - on unions (modeled as alias(union_of)), these are identifiers for the union members
|
||||
* - for additional properties, this is the name of the dict that holds these properties
|
||||
* - for additional property, this is the name of the key and value fields that hold the
|
||||
* additional property
|
||||
*/
|
||||
codegenNames?: string[];
|
||||
/**
|
||||
* Location of an item. The path is relative to the "specification" directory, e.g "_types/common.ts#L1-L2"
|
||||
*/
|
||||
specLocation: string;
|
||||
}
|
||||
|
||||
export type Variants = ExternalTag | InternalTag | Container;
|
||||
|
||||
export interface VariantBase {
|
||||
/**
|
||||
* Is this variant type open to extensions? Default to false. Used for variants that can
|
||||
* be extended with plugins. If true, target clients should allow for additional variants
|
||||
* with a variant tag outside the ones defined in the spec and arbitrary data as the value.
|
||||
*/
|
||||
nonExhaustive?: boolean;
|
||||
}
|
||||
|
||||
export interface ExternalTag extends VariantBase {
|
||||
kind: 'external_tag';
|
||||
}
|
||||
|
||||
export interface InternalTag extends VariantBase {
|
||||
kind: 'internal_tag';
|
||||
/* Name of the property that holds the variant tag */
|
||||
tag: string;
|
||||
/* Default value for the variant tag if it's missing */
|
||||
defaultTag?: string;
|
||||
}
|
||||
|
||||
export interface Container extends VariantBase {
|
||||
kind: 'container';
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherits clause (aka extends or implements) for an interface or request
|
||||
*/
|
||||
export interface Inherits {
|
||||
type: TypeName;
|
||||
generics?: ValueOf[];
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface type
|
||||
*/
|
||||
export interface Interface extends BaseType {
|
||||
kind: 'interface';
|
||||
/**
|
||||
* Open generic parameters. The name is that of the parameter, the namespace is an arbitrary value that allows
|
||||
* this fully qualified type name to be used when this open generic parameter is used in property's type.
|
||||
*/
|
||||
generics?: TypeName[];
|
||||
inherits?: Inherits;
|
||||
implements?: Inherits[];
|
||||
|
||||
/**
|
||||
* Behaviors directly implemented by this interface
|
||||
*/
|
||||
behaviors?: Inherits[];
|
||||
|
||||
/**
|
||||
* Behaviors attached to this interface, coming from the interface itself (see `behaviors`)
|
||||
* or from inherits and implements ancestors
|
||||
*/
|
||||
attachedBehaviors?: string[];
|
||||
properties: Property[];
|
||||
/**
|
||||
* The property that can be used as a shortcut for the entire data structure in the JSON.
|
||||
*/
|
||||
shortcutProperty?: string;
|
||||
|
||||
/** Identify containers */
|
||||
variants?: Container;
|
||||
}
|
||||
|
||||
/**
|
||||
* A request type
|
||||
*/
|
||||
export interface Request extends BaseType {
|
||||
// Note: does not extend Interface as properties are split across path, query and body
|
||||
kind: 'request';
|
||||
generics?: TypeName[];
|
||||
/** The parent defines additional body properties that are added to the body, that has to be a PropertyBody */
|
||||
inherits?: Inherits;
|
||||
implements?: Inherits[];
|
||||
/** URL path properties */
|
||||
path: Property[];
|
||||
/** Query string properties */
|
||||
query: Property[];
|
||||
// FIXME: we need an annotation that lists query params replaced by a body property so that we can skip them.
|
||||
// Examples on _search: sort -> sort, _source -> (_source, _source_include, _source_exclude)
|
||||
// Or can we say that implicitly a body property replaces all path params starting with its name?
|
||||
// Is there a priority rule between path and body parameters?
|
||||
//
|
||||
// We can also pull path parameter descriptions on body properties they replace
|
||||
|
||||
/**
|
||||
* Body type. Most often a list of properties (that can extend those of the inherited interface, see above), except for a
|
||||
* few specific cases that use other types such as bulk (array) or create (generic parameter). Or NoBody for requests
|
||||
* that don't have a body.
|
||||
*/
|
||||
body: Body;
|
||||
behaviors?: Inherits[];
|
||||
attachedBehaviors?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A response type
|
||||
*/
|
||||
export interface Response extends BaseType {
|
||||
kind: 'response';
|
||||
generics?: TypeName[];
|
||||
body: Body;
|
||||
behaviors?: Inherits[];
|
||||
attachedBehaviors?: string[];
|
||||
exceptions?: ResponseException[];
|
||||
}
|
||||
|
||||
export interface ResponseException {
|
||||
description?: string;
|
||||
body: Body;
|
||||
statusCodes: number[];
|
||||
}
|
||||
|
||||
export type Body = ValueBody | PropertiesBody | NoBody;
|
||||
|
||||
export interface ValueBody {
|
||||
kind: 'value';
|
||||
value: ValueOf;
|
||||
codegenName?: string;
|
||||
}
|
||||
|
||||
export interface PropertiesBody {
|
||||
kind: 'properties';
|
||||
properties: Property[];
|
||||
}
|
||||
|
||||
export interface NoBody {
|
||||
kind: 'no_body';
|
||||
}
|
||||
|
||||
/**
|
||||
* An enumeration member.
|
||||
*
|
||||
* When enumeration members can become ambiguous when translated to an identifier, the `name` property will be a good
|
||||
* identifier name, and `stringValue` will be the string value to use on the wire.
|
||||
* See DateMathTimeUnit for an example of this, which have members for "m" (minute) and "M" (month).
|
||||
*/
|
||||
export interface EnumMember {
|
||||
/** The identifier to use for this enum */
|
||||
name: string;
|
||||
/** An optional set of aliases for `name` */
|
||||
aliases?: string[];
|
||||
/**
|
||||
* If specified takes precedence over `name` when generating code. `name` is always the value
|
||||
* to be sent over the wire
|
||||
*/
|
||||
codegenName?: string;
|
||||
description?: string;
|
||||
deprecation?: Deprecation;
|
||||
since?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enumeration
|
||||
*/
|
||||
export interface Enum extends BaseType {
|
||||
kind: 'enum';
|
||||
/**
|
||||
* If the enum is open, it means that other than the specified values it can accept an arbitrary value.
|
||||
* If this property is not present, it means that the enum is not open (in other words, is closed).
|
||||
*/
|
||||
isOpen?: boolean;
|
||||
members: EnumMember[];
|
||||
}
|
||||
|
||||
/**
|
||||
* An alias for an existing type.
|
||||
*/
|
||||
export interface TypeAlias extends BaseType {
|
||||
kind: 'type_alias';
|
||||
type: ValueOf;
|
||||
/** generic parameters: either concrete types or open parameters from the enclosing type */
|
||||
generics?: TypeName[];
|
||||
/** Only applicable to `union_of` aliases: identify typed_key unions (external) and variant inventories (internal) */
|
||||
variants?: InternalTag | ExternalTag;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
export enum Stability {
|
||||
stable = 'stable',
|
||||
beta = 'beta',
|
||||
experimental = 'experimental',
|
||||
}
|
||||
export enum Visibility {
|
||||
public = 'public',
|
||||
feature_flag = 'feature_flag',
|
||||
private = 'private',
|
||||
}
|
||||
|
||||
export interface Deprecation {
|
||||
version: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface Availabilities {
|
||||
stack?: Availability;
|
||||
serverless?: Availability;
|
||||
}
|
||||
|
||||
export interface Availability {
|
||||
since?: string;
|
||||
featureFlag?: string;
|
||||
stability?: Stability;
|
||||
visibility?: Visibility;
|
||||
}
|
||||
|
||||
export interface Endpoint {
|
||||
name: string;
|
||||
description: string;
|
||||
docUrl: string;
|
||||
docId?: string;
|
||||
deprecation?: Deprecation;
|
||||
availability: Availabilities;
|
||||
|
||||
/**
|
||||
* If the request value is `null` it means that there is not yet a
|
||||
* request type definition for this endpoint.
|
||||
*/
|
||||
request: TypeName | null;
|
||||
requestBodyRequired: boolean; // Not sure this is useful
|
||||
|
||||
/**
|
||||
* If the response value is `null` it means that there is not yet a
|
||||
* response type definition for this endpoint.
|
||||
*/
|
||||
response: TypeName | null;
|
||||
|
||||
urls: UrlTemplate[];
|
||||
|
||||
/**
|
||||
* The version when this endpoint reached its current stability level.
|
||||
* Missing data means "forever", i.e. before any of the target client versions produced from this spec.
|
||||
*/
|
||||
since?: string;
|
||||
stability?: Stability;
|
||||
visibility?: Visibility;
|
||||
featureFlag?: string;
|
||||
requestMediaType?: string[];
|
||||
responseMediaType?: string[];
|
||||
privileges?: {
|
||||
index?: string[];
|
||||
cluster?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface UrlTemplate {
|
||||
path: string;
|
||||
methods: string[];
|
||||
deprecation?: Deprecation;
|
||||
}
|
||||
|
||||
export interface Model {
|
||||
types: TypeDefinition[];
|
||||
endpoints: Endpoint[];
|
||||
}
|
17
packages/kbn-generate-console-definitions/src/utils.ts
Normal file
17
packages/kbn-generate-console-definitions/src/utils.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { SpecificationTypes } from './types';
|
||||
export const findTypeDefinition = (
|
||||
schema: SpecificationTypes.Model,
|
||||
typeName: SpecificationTypes.TypeName
|
||||
): SpecificationTypes.TypeDefinition | undefined => {
|
||||
return schema.types.find(
|
||||
(type) => type.name.name === typeName.name && type.name.namespace === typeName.namespace
|
||||
);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue