diff --git a/packages/kbn-generate-console-definitions/src/generate_availability.ts b/packages/kbn-generate-console-definitions/src/generate_availability.ts index fe995e270a07..96e4f521440d 100644 --- a/packages/kbn-generate-console-definitions/src/generate_availability.ts +++ b/packages/kbn-generate-console-definitions/src/generate_availability.ts @@ -6,15 +6,15 @@ * Side Public License, v 1. */ -import { AutocompleteAvailability } from './types'; +import type { EndpointDescription } from '@kbn/console-plugin/common/types'; import type { SpecificationTypes } from './types'; const DEFAULT_ENDPOINT_AVAILABILITY = true; export const generateAvailability = ( endpoint: SpecificationTypes.Endpoint -): AutocompleteAvailability => { - const availability: AutocompleteAvailability = { +): EndpointDescription['availability'] => { + const availability: EndpointDescription['availability'] = { stack: DEFAULT_ENDPOINT_AVAILABILITY, serverless: DEFAULT_ENDPOINT_AVAILABILITY, }; diff --git a/packages/kbn-generate-console-definitions/src/generate_console_definitions.ts b/packages/kbn-generate-console-definitions/src/generate_console_definitions.ts index 42afed13eed2..f30faa2e1d92 100644 --- a/packages/kbn-generate-console-definitions/src/generate_console_definitions.ts +++ b/packages/kbn-generate-console-definitions/src/generate_console_definitions.ts @@ -9,15 +9,16 @@ import fs from 'fs'; import Path, { join } from 'path'; import { ToolingLog } from '@kbn/tooling-log'; +import type { + DefinitionUrlParams, + EndpointDefinition, + EndpointDescription, +} from '@kbn/console-plugin/common/types'; import { generateQueryParams } from './generate_query_params'; import { generateAvailability } from './generate_availability'; -import type { - AutocompleteBodyParams, - AutocompleteDefinition, - AutocompleteUrlParams, - SpecificationTypes, -} from './types'; -import { findTypeDefinition } from './utils'; +import type { SpecificationTypes } from './types'; +import { findTypeDefinition } from './helpers'; +import { generateUrlComponents } from './generate_url_components'; const generateMethods = (endpoint: SpecificationTypes.Endpoint): string[] => { // this array consists of arrays of strings @@ -42,11 +43,14 @@ const generatePatterns = (endpoint: SpecificationTypes.Endpoint): string[] => { const generateDocumentation = (endpoint: SpecificationTypes.Endpoint): string => { return endpoint.docUrl; }; - -const generateParams = ( +interface GeneratedParameters { + urlParams: DefinitionUrlParams; + urlComponents: DefinitionUrlParams; +} +const generateParameters = ( endpoint: SpecificationTypes.Endpoint, schema: SpecificationTypes.Model -): { urlParams: AutocompleteUrlParams; bodyParams: AutocompleteBodyParams } | undefined => { +): GeneratedParameters | undefined => { const { request } = endpoint; if (!request) { return; @@ -55,27 +59,22 @@ const generateParams = ( if (!requestType) { return; } - const urlParams = generateQueryParams(requestType as SpecificationTypes.Request, schema); - const bodyParams = generateBodyParams(requestType); - return { urlParams, bodyParams }; -}; -const generateBodyParams = ( - requestType: SpecificationTypes.TypeDefinition -): AutocompleteBodyParams => { - return {}; + const urlParams = generateQueryParams(requestType as SpecificationTypes.Request, schema); + const urlComponents = generateUrlComponents(requestType as SpecificationTypes.Request, schema); + return { urlParams, urlComponents }; }; const addParams = ( - definition: AutocompleteDefinition, - params: { urlParams: AutocompleteUrlParams; bodyParams: AutocompleteBodyParams } -): AutocompleteDefinition => { - const { urlParams, bodyParams } = params; - if (urlParams && Object.keys(urlParams).length > 0) { + definition: EndpointDescription, + params: GeneratedParameters +): EndpointDescription => { + const { urlParams, urlComponents } = params; + if (Object.keys(urlParams).length > 0) { definition.url_params = urlParams; } - if (bodyParams && Object.keys(bodyParams).length > 0) { - definition.data_autocomplete_rules = bodyParams; + if (Object.keys(urlComponents).length > 0) { + definition.url_components = urlComponents; } return definition; }; @@ -83,13 +82,13 @@ const addParams = ( const generateDefinition = ( endpoint: SpecificationTypes.Endpoint, schema: SpecificationTypes.Model -): AutocompleteDefinition => { +): EndpointDescription => { const methods = generateMethods(endpoint); const patterns = generatePatterns(endpoint); const documentation = generateDocumentation(endpoint); const availability = generateAvailability(endpoint); - let definition: AutocompleteDefinition = {}; - const params = generateParams(endpoint, schema); + let definition: EndpointDescription = {}; + const params = generateParameters(endpoint, schema); if (params) { definition = addParams(definition, params); } @@ -117,7 +116,7 @@ export function generateConsoleDefinitions({ const { name } = endpoint; log.info(name); const definition = generateDefinition(endpoint, schema); - const fileContent: { [name: string]: AutocompleteDefinition } = { + const fileContent: EndpointDefinition = { [name]: definition, }; fs.writeFileSync( diff --git a/packages/kbn-generate-console-definitions/src/generate_query_params.test.ts b/packages/kbn-generate-console-definitions/src/generate_query_params.test.ts index 3d658ba60f17..e41f657d1a47 100644 --- a/packages/kbn-generate-console-definitions/src/generate_query_params.test.ts +++ b/packages/kbn-generate-console-definitions/src/generate_query_params.test.ts @@ -8,52 +8,9 @@ import { SpecificationTypes } from './types'; import { generateQueryParams } from './generate_query_params'; -import { UrlParamValue } from './types/autocomplete_definition_types'; +import { getMockProperty, mockRequestType, mockSchema } from './helpers/test_helpers'; 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', @@ -106,270 +63,4 @@ describe('generateQueryParams', () => { 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: '', - }); - }); }); diff --git a/packages/kbn-generate-console-definitions/src/generate_query_params.ts b/packages/kbn-generate-console-definitions/src/generate_query_params.ts index 5310e85d6893..e3f0a0bbeedf 100644 --- a/packages/kbn-generate-console-definitions/src/generate_query_params.ts +++ b/packages/kbn-generate-console-definitions/src/generate_query_params.ts @@ -43,19 +43,15 @@ * */ -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); +import type { DefinitionUrlParams } from '@kbn/console-plugin/common/types'; +import type { SpecificationTypes } from './types'; +import { convertUrlProperties } from './helpers'; export const generateQueryParams = ( requestType: SpecificationTypes.Request, schema: SpecificationTypes.Model -): AutocompleteUrlParams => { - let urlParams = {} as AutocompleteUrlParams; +): DefinitionUrlParams => { + let urlParams: DefinitionUrlParams = {}; const { types } = schema; const { attachedBehaviors, query } = requestType; // if there are any attached behaviors, iterate over each and find its type @@ -67,153 +63,13 @@ export const generateQueryParams = ( 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); + urlParams = convertUrlProperties(properties, urlParams, schema); } } } // iterate over properties in query and add it to url params - urlParams = convertProperties(query, urlParams, schema); + urlParams = convertUrlProperties(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); -}; diff --git a/packages/kbn-generate-console-definitions/src/generate_url_components.test.ts b/packages/kbn-generate-console-definitions/src/generate_url_components.test.ts new file mode 100644 index 000000000000..13139f22ffef --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/generate_url_components.test.ts @@ -0,0 +1,109 @@ +/* + * 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 { generateUrlComponents } from './generate_url_components'; +import { getMockProperty, mockRequestType, mockSchema } from './helpers/test_helpers'; +import { SpecificationTypes } from './types'; + +describe('generateUrlComponents', () => { + it('generates url components from path', () => { + const urlComponentProperty1 = getMockProperty({ + propertyName: 'property1', + typeName: { name: 'EnumType1', namespace: 'test.namespace' }, + }); + const enumType1: SpecificationTypes.Enum = { + kind: 'enum', + members: [ + { + name: 'value1', + }, + { + name: 'value2', + }, + ], + name: { + name: 'EnumType1', + namespace: 'test.namespace', + }, + specLocation: '', + }; + const urlComponentProperty2 = getMockProperty({ + propertyName: 'property2', + typeName: { name: 'EnumType2', namespace: 'test.namespace' }, + }); + const enumType2: SpecificationTypes.Enum = { + kind: 'enum', + members: [ + { + name: 'anotherValue1', + }, + { + name: 'anotherValue2', + }, + ], + name: { + name: 'EnumType2', + namespace: 'test.namespace', + }, + specLocation: '', + }; + const requestType: SpecificationTypes.Request = { + ...mockRequestType, + path: [urlComponentProperty1, urlComponentProperty2], + }; + const schema: SpecificationTypes.Model = { ...mockSchema, types: [enumType1, enumType2] }; + const urlComponents = generateUrlComponents(requestType, schema); + expect(urlComponents).toEqual({ + property1: ['value1', 'value2'], + property2: ['anotherValue1', 'anotherValue2'], + }); + }); + + it('removes url components without any values (empty string)', () => { + const requestType: SpecificationTypes.Request = { + ...mockRequestType, + path: [getMockProperty({ propertyName: 'emptyStringProperty' })], + }; + const urlComponents = generateUrlComponents(requestType, mockSchema); + expect(urlComponents).toEqual({}); + }); + + it('removes url components without any values (empty array)', () => { + const emptyArrayProperty = getMockProperty({ + propertyName: 'emptyArrayProperty', + type: { + kind: 'union_of', + items: [ + { + kind: 'instance_of', + type: { + name: 'string', + namespace: '_builtins', + }, + }, + { + kind: 'array_of', + value: { + kind: 'instance_of', + type: { + name: 'string', + namespace: '_builtins', + }, + }, + }, + ], + }, + }); + const requestType: SpecificationTypes.Request = { + ...mockRequestType, + path: [emptyArrayProperty], + }; + const urlComponents = generateUrlComponents(requestType, mockSchema); + expect(urlComponents).toEqual({}); + }); +}); diff --git a/packages/kbn-generate-console-definitions/src/generate_url_components.ts b/packages/kbn-generate-console-definitions/src/generate_url_components.ts new file mode 100644 index 000000000000..bec99f109736 --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/generate_url_components.ts @@ -0,0 +1,27 @@ +/* + * 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 { DefinitionUrlParams } from '@kbn/console-plugin/common/types'; +import type { SpecificationTypes } from './types'; +import { convertUrlProperties } from './helpers'; + +export const generateUrlComponents = ( + request: SpecificationTypes.Request, + schema: SpecificationTypes.Model +): DefinitionUrlParams => { + let urlComponents: DefinitionUrlParams = {}; + const { path } = request; + urlComponents = convertUrlProperties(path, urlComponents, schema); + // remove empty strings and empty arrays + Object.entries(urlComponents).forEach(([paramName, paramValue]) => { + if (!paramValue || (paramValue as []).length === 0) { + delete urlComponents[paramName]; + } + }); + return urlComponents; +}; diff --git a/packages/kbn-generate-console-definitions/src/helpers/convert_url_properties.test.ts b/packages/kbn-generate-console-definitions/src/helpers/convert_url_properties.test.ts new file mode 100644 index 000000000000..c345d8abd21d --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/helpers/convert_url_properties.test.ts @@ -0,0 +1,272 @@ +/* + * 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 { getMockProperty, mockSchema } from './test_helpers'; +import { convertUrlProperties } from './convert_url_properties'; +import { SpecificationTypes } from '../types'; + +describe('convertUrlProperties', () => { + 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 urlParams = convertUrlProperties( + [stringProperty, numberProperty, booleanProperty], + {}, + mockSchema + ); + expect(urlParams).toEqual({ + stringProperty: '', + numberProperty: '', + booleanProperty: '__flag__', + }); + }); + + it('adds serverDefault value if any', () => { + const propertyWithDefault = getMockProperty({ + propertyName: 'propertyWithDefault', + serverDefault: 'default', + }); + const urlParams = convertUrlProperties([propertyWithDefault], {}, 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 schema = { ...mockSchema, types: [enumType] }; + const urlParams = convertUrlProperties([enumProperty], {}, 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 schema: SpecificationTypes.Model = { ...mockSchema, types: [typeAliasType] }; + const urlParams = convertUrlProperties([typeAliasProperty], {}, 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 urlParams = convertUrlProperties( + [stringProperty, numberProperty, booleanProperty], + {}, + 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 schema: SpecificationTypes.Model = { ...mockSchema, types: [enumType] }; + const urlParams = convertUrlProperties([unionProperty], {}, 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 urlParams = convertUrlProperties([unionProperty], {}, 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 urlParams = convertUrlProperties([unionProperty], {}, 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 urlParams = convertUrlProperties([unionProperty], {}, mockSchema); + // check that no `undefined` values are added + const value = urlParams.unionProperty as []; + 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 urlParams = convertUrlProperties([unknownTypeProperty], {}, mockSchema); + expect(urlParams).toEqual({ + unknownTypeProperty: '', + }); + }); +}); diff --git a/packages/kbn-generate-console-definitions/src/helpers/convert_url_properties.ts b/packages/kbn-generate-console-definitions/src/helpers/convert_url_properties.ts new file mode 100644 index 000000000000..8f603328afd9 --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/helpers/convert_url_properties.ts @@ -0,0 +1,158 @@ +/* + * 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 { DefinitionUrlParams } from '@kbn/console-plugin/common/types'; +import type { SpecificationTypes } from '../types'; +import { findTypeDefinition } from './find_type_definition'; + +const booleanFlagString = '__flag__'; +const trueValueString = String(true); +const falseValueString = String(false); +// use unknown for now since DefinitionUrlParams is Record +// TODO update with more concrete types +type UrlParamValue = unknown; + +export const convertUrlProperties = ( + properties: SpecificationTypes.Property[], + urlParams: DefinitionUrlParams, + schema: SpecificationTypes.Model +): DefinitionUrlParams => { + 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); +}; diff --git a/packages/kbn-generate-console-definitions/src/utils.ts b/packages/kbn-generate-console-definitions/src/helpers/find_type_definition.ts similarity index 92% rename from packages/kbn-generate-console-definitions/src/utils.ts rename to packages/kbn-generate-console-definitions/src/helpers/find_type_definition.ts index 29c24e63fa58..bcd9585010ff 100644 --- a/packages/kbn-generate-console-definitions/src/utils.ts +++ b/packages/kbn-generate-console-definitions/src/helpers/find_type_definition.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { SpecificationTypes } from './types'; +import type { SpecificationTypes } from '../types'; export const findTypeDefinition = ( schema: SpecificationTypes.Model, typeName: SpecificationTypes.TypeName diff --git a/packages/kbn-generate-console-definitions/src/helpers/index.ts b/packages/kbn-generate-console-definitions/src/helpers/index.ts new file mode 100644 index 000000000000..28342b36cfc2 --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/helpers/index.ts @@ -0,0 +1,10 @@ +/* + * 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 { findTypeDefinition } from './find_type_definition'; +export { convertUrlProperties } from './convert_url_properties'; diff --git a/packages/kbn-generate-console-definitions/src/helpers/test_helpers.ts b/packages/kbn-generate-console-definitions/src/helpers/test_helpers.ts new file mode 100644 index 000000000000..d50a3aed74e5 --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/helpers/test_helpers.ts @@ -0,0 +1,52 @@ +/* + * 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'; + +export const mockRequestType: SpecificationTypes.Request = { + body: { kind: 'no_body' }, + kind: 'request', + name: { + name: 'TestRequest', + namespace: 'test.namespace', + }, + path: [], + query: [], + specLocation: '', +}; + +export 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', + }, + }, + }; +}; + +export const mockSchema: SpecificationTypes.Model = { + endpoints: [], + types: [], +}; diff --git a/packages/kbn-generate-console-definitions/src/types/autocomplete_definition_types.ts b/packages/kbn-generate-console-definitions/src/types/autocomplete_definition_types.ts deleted file mode 100644 index 67bcbc5d131a..000000000000 --- a/packages/kbn-generate-console-definitions/src/types/autocomplete_definition_types.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 AutocompleteAvailability { - stack: boolean; - serverless: boolean; -} - -export interface AutocompleteDefinition { - documentation?: string; - methods?: string[]; - patterns?: string[]; - url_params?: AutocompleteUrlParams; - data_autocomplete_rules?: AutocompleteBodyParams; - availability?: AutocompleteAvailability; -} diff --git a/packages/kbn-generate-console-definitions/src/types/index.ts b/packages/kbn-generate-console-definitions/src/types/index.ts index ab2b036d39cf..f6816c9e7c31 100644 --- a/packages/kbn-generate-console-definitions/src/types/index.ts +++ b/packages/kbn-generate-console-definitions/src/types/index.ts @@ -6,11 +6,4 @@ * Side Public License, v 1. */ -export type { - AutocompleteDefinition, - AutocompleteUrlParams, - AutocompleteBodyParams, - AutocompleteAvailability, -} from './autocomplete_definition_types'; - export * as SpecificationTypes from './specification_types'; diff --git a/src/plugins/console/common/types/autocomplete_definitions.ts b/src/plugins/console/common/types/autocomplete_definitions.ts index a7bb4cdf9a1f..97a74ce0de66 100644 --- a/src/plugins/console/common/types/autocomplete_definitions.ts +++ b/src/plugins/console/common/types/autocomplete_definitions.ts @@ -9,15 +9,51 @@ export type EndpointsAvailability = 'stack' | 'serverless'; export interface EndpointDescription { + /** + * HTTP request methods this endpoint accepts: GET, POST, DELETE etc + */ methods?: string[]; + + /** + * URLs paths this endpoint accepts, can contain parameters in curly braces. + * For example, /_cat/indices/{index} + */ patterns?: string | string[]; - url_params?: Record; + + /** + * List of possible values for parameters used in patterns. + */ + url_components?: DefinitionUrlParams; + + /** + * Query parameters for this endpoint. + */ + url_params?: DefinitionUrlParams; + + /** + * Request body parameters for this endpoint. + */ data_autocomplete_rules?: Record; - url_components?: Record; + + /** + * A priority number when the same endpoint name is used. + */ priority?: number; + + /** + * An url of the documentation page of this endpoint. + * Can contain a parameter {branch}. + */ + documentation?: string; + + /** + * If the endpoint is available different environments (stack, serverless). + */ availability?: Record; } +export type DefinitionUrlParams = Record; + export interface EndpointDefinition { [endpointName: string]: EndpointDescription; } diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.state.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.state.json new file mode 100644 index 000000000000..3f3e5c9b6ceb --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.state.json @@ -0,0 +1,16 @@ +{ + "cluster.state": { + "url_components": { + "metrics": [ + "_all", + "blocks", + "master_node", + "metadata", + "nodes", + "routing_nodes", + "routing_table", + "version" + ] + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.stats.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.stats.json new file mode 100644 index 000000000000..4b4789b74451 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.stats.json @@ -0,0 +1,24 @@ +{ + "indices.stats": { + "url_components": { + "metrics": [ + "_all", + "bulk", + "completion", + "docs", + "fielddata", + "flush", + "get", + "indexing", + "merge", + "query_cache", + "refresh", + "request_cache", + "search", + "segments", + "store", + "warmer" + ] + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/nodes.stats.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/nodes.stats.json new file mode 100644 index 000000000000..13cb14bcd567 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/nodes.stats.json @@ -0,0 +1,39 @@ +{ + "nodes.stats": { + "url_components": { + "metrics": [ + "_all", + "breaker", + "discovery", + "fs", + "http", + "indexing_pressure", + "indices", + "jvm", + "os", + "process", + "thread_pool", + "transport" + ], + "index_metric": [ + "_all", + "bulk", + "completion", + "docs", + "fielddata", + "flush", + "get", + "indexing", + "merge", + "query_cache", + "refresh", + "request_cache", + "search", + "segments", + "shard_stats", + "store", + "warmer" + ] + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/nodes.usage.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/nodes.usage.json new file mode 100644 index 000000000000..789573ecd0f2 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/nodes.usage.json @@ -0,0 +1,10 @@ +{ + "nodes.usage": { + "url_components": { + "metrics": [ + "_all", + "rest_actions" + ] + } + } +} diff --git a/src/plugins/console/server/services/spec_definitions_service.test.ts b/src/plugins/console/server/services/spec_definitions_service.test.ts index 8580c51bb844..f839f56f3c4d 100644 --- a/src/plugins/console/server/services/spec_definitions_service.test.ts +++ b/src/plugins/console/server/services/spec_definitions_service.test.ts @@ -9,9 +9,9 @@ import globby from 'globby'; import fs from 'fs'; import { SpecDefinitionsService } from '.'; -import { EndpointDefinition, EndpointsAvailability } from '../../common/types'; +import type { EndpointDefinition, EndpointsAvailability } from '../../common/types'; -const mockReadFilySync = jest.spyOn(fs, 'readFileSync'); +const mockReadFileSync = jest.spyOn(fs, 'readFileSync'); const mockGlobbySync = jest.spyOn(globby, 'sync'); const mockJsLoadersGetter = jest.fn(); @@ -57,7 +57,7 @@ describe('SpecDefinitionsService', () => { // mock the function that lists files in the definitions folders mockGlobbySync.mockImplementation(() => []); // mock the function that reads files - mockReadFilySync.mockImplementation(() => ''); + mockReadFileSync.mockImplementation(() => ''); // mock the function that returns the list of js definitions loaders mockJsLoadersGetter.mockImplementation(() => []); }); @@ -117,7 +117,7 @@ describe('SpecDefinitionsService', () => { return []; }); - mockReadFilySync.mockImplementation((path) => { + mockReadFileSync.mockImplementation((path) => { if (path.toString() === '/generated/endpoint1.json') { return JSON.stringify(getMockEndpoint({ endpointName: 'endpoint1' })); } @@ -162,7 +162,7 @@ describe('SpecDefinitionsService', () => { return []; }); - mockReadFilySync.mockImplementation((path) => { + mockReadFileSync.mockImplementation((path) => { if (path.toString() === '/generated/endpoint1.json') { return JSON.stringify(getMockEndpoint({ endpointName: 'endpoint1' })); } @@ -219,7 +219,7 @@ describe('SpecDefinitionsService', () => { return []; }); - mockReadFilySync.mockImplementation((path) => { + mockReadFileSync.mockImplementation((path) => { if (path.toString() === 'manual_endpoint.json') { return JSON.stringify(getMockEndpoint({ endpointName: 'manual_endpoint' })); } @@ -250,7 +250,7 @@ describe('SpecDefinitionsService', () => { return []; }); - mockReadFilySync.mockImplementation((path) => { + mockReadFileSync.mockImplementation((path) => { if (path.toString() === 'generated_endpoint.json') { return JSON.stringify(getMockEndpoint({ endpointName: 'test', methods: ['GET'] })); } @@ -288,7 +288,7 @@ describe('SpecDefinitionsService', () => { return []; }); - mockReadFilySync.mockImplementation((path) => { + mockReadFileSync.mockImplementation((path) => { if (path.toString() === '/generated/endpoint1.json') { return JSON.stringify( getMockEndpoint({ @@ -330,7 +330,7 @@ describe('SpecDefinitionsService', () => { return []; }); - mockReadFilySync.mockImplementation((path) => { + mockReadFileSync.mockImplementation((path) => { if (path.toString() === '/generated/endpoint1.json') { return JSON.stringify( getMockEndpoint({ diff --git a/src/plugins/console/server/services/spec_definitions_service.ts b/src/plugins/console/server/services/spec_definitions_service.ts index 2e93c2f7a0a3..91df8241ba2b 100644 --- a/src/plugins/console/server/services/spec_definitions_service.ts +++ b/src/plugins/console/server/services/spec_definitions_service.ts @@ -12,6 +12,7 @@ import { basename, join } from 'path'; import normalizePath from 'normalize-path'; import { readFileSync } from 'fs'; +import { EndpointDefinition, EndpointDescription, EndpointsAvailability } from '../../common/types'; import { AUTOCOMPLETE_DEFINITIONS_FOLDER, GENERATED_SUBFOLDER, @@ -19,11 +20,6 @@ import { OVERRIDES_SUBFOLDER, } from '../../common/constants'; import { jsSpecLoaders } from '../lib'; -import type { - EndpointsAvailability, - EndpointDescription, - EndpointDefinition, -} from '../../common/types'; export interface SpecDefinitionsDependencies { endpointsAvailability: EndpointsAvailability;