diff --git a/.eslintrc.js b/.eslintrc.js index 6fcc223c66..97ac07e5f4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -97,5 +97,7 @@ module.exports = { '@typescript-eslint/quotes': ['error', 'single', { avoidEscape: true }], 'no-use-before-define': 'off', '@typescript-eslint/no-use-before-define': 'error', + 'no-redeclare': 'off', + '@typescript-eslint/no-redeclare': 'error', }, }; diff --git a/packages/insomnia-inso/src/__snapshots__/inso-snapshot.test.ts.snap b/packages/insomnia-inso/src/__snapshots__/inso-snapshot.test.ts.snap index 73208a7f2f..e8c7c29528 100644 --- a/packages/insomnia-inso/src/__snapshots__/inso-snapshot.test.ts.snap +++ b/packages/insomnia-inso/src/__snapshots__/inso-snapshot.test.ts.snap @@ -91,7 +91,7 @@ Generate configuration from an api spec. Options: -t, --type type of configuration to generate, options are - [kubernetes, declarative] (default: declarative) + [declarative, kubernetes] (default: declarative) --tags comma separated list of tags to apply to each entity -o, --output save the generated config to a file -h, --help display help for command" diff --git a/packages/insomnia-inso/src/cli.ts b/packages/insomnia-inso/src/cli.ts index 5c31a9d2ac..9e824e1b9b 100644 --- a/packages/insomnia-inso/src/cli.ts +++ b/packages/insomnia-inso/src/cli.ts @@ -1,4 +1,4 @@ -import { conversionTypeMap, generateConfig } from './commands/generate-config'; +import { ConversionOption, conversionOptions, generateConfig } from './commands/generate-config'; import { getVersion, logErrorExit1, exit } from './util'; import { reporterTypes, runInsomniaTests, TestReporter } from './commands/run-tests'; import { lintSpecification } from './commands/lint-specification'; @@ -24,7 +24,7 @@ type CreateCommand = (command: string) => commander.Command const makeGenerateCommand = (commandCreator: CreateCommand) => { // inso generate const command = commandCreator('generate').description('Code generation utilities'); - const defaultType: keyof typeof conversionTypeMap = 'declarative'; + const defaultType: ConversionOption = 'declarative'; // inso generate config -t kubernetes config.yaml command @@ -32,7 +32,7 @@ const makeGenerateCommand = (commandCreator: CreateCommand) => { .description('Generate configuration from an api spec.') .option( '-t, --type ', - `type of configuration to generate, options are [${Object.keys(conversionTypeMap).join(', ')}] (default: ${defaultType})`, + `type of configuration to generate, options are [${conversionOptions.join(', ')}] (default: ${defaultType})`, ) .option('--tags ', 'comma separated list of tags to apply to each entity') .option('-o, --output ', 'save the generated config to a file') diff --git a/packages/insomnia-inso/src/commands/generate-config.test.ts b/packages/insomnia-inso/src/commands/generate-config.test.ts index d321e255d0..b74afd6013 100644 --- a/packages/insomnia-inso/src/commands/generate-config.test.ts +++ b/packages/insomnia-inso/src/commands/generate-config.test.ts @@ -1,5 +1,5 @@ import { conversionTypeMap, generateConfig, GenerateConfigOptions } from './generate-config'; -import { ConversionResult, generate as _generate, generateFromString as _generateFromString } from 'openapi-2-kong'; +import { generate as _generate, generateFromString as _generateFromString, KongForKubernetesResult } from 'openapi-2-kong'; import path from 'path'; import { writeFileWithCliOptions as _writeFileWithCliOptions } from '../write-file'; import { globalBeforeAll, globalBeforeEach } from '../jest/before'; @@ -14,7 +14,8 @@ const generate = _generate as jest.MockedFunction; const generateFromString = _generateFromString as jest.MockedFunction; const writeFileWithCliOptions = _writeFileWithCliOptions as jest.MockedFunction; -const mockConversionResult: ConversionResult = { +const mockConversionResult: KongForKubernetesResult = { + // @ts-expect-error -- TSCONVERSION the tests seem to suggest that this is valid, yet it is not allowed by the types. documents: ['a', 'b'], type: 'kong-for-kubernetes', label: '', @@ -43,7 +44,7 @@ describe('generateConfig()', () => { }); expect(generate).not.toHaveBeenCalled(); expect(logger.__getLogs().fatal).toEqual([ - 'Config type "invalid" not unrecognized. Options are [kubernetes, declarative].', + 'Config type "invalid" not unrecognized. Options are [declarative, kubernetes].', ]); }); @@ -108,7 +109,7 @@ describe('generateConfig()', () => { generate.mockResolvedValue(mockConversionResult); const outputPath = 'this-is-the-output-path'; writeFileWithCliOptions.mockResolvedValue(outputPath); - const result = await generateConfig('file.yaml', { + const result = await generateConfig(filePath, { type: 'kubernetes', workingDir: 'test/dir', output: 'output.yaml', @@ -127,7 +128,7 @@ describe('generateConfig()', () => { generate.mockResolvedValue(mockConversionResult); const outputPath = 'this-is-the-output-path'; writeFileWithCliOptions.mockResolvedValue(outputPath); - const absolutePath = path.join(os.tmpdir(), 'dev', 'file.yaml'); + const absolutePath = path.join(os.tmpdir(), 'dev', filePath); const result = await generateConfig(absolutePath, { type: 'kubernetes', workingDir: 'test/dir', @@ -143,7 +144,7 @@ describe('generateConfig()', () => { it('should throw InsoError if there is an error thrown by openapi-2-kong', async () => { const error = new Error('err'); generate.mockRejectedValue(error); - const promise = generateConfig('file.yaml', { type: 'kubernetes' }); + const promise = generateConfig(filePath, { type: 'kubernetes' }); await expect(promise).rejects.toThrowError( new InsoError('There was an error while generating configuration', error), ); @@ -152,7 +153,7 @@ describe('generateConfig()', () => { it('should warn if no valid spec can be found', async () => { // @ts-expect-error intentionally passing in a bad value generate.mockResolvedValue({}); - const result = await generateConfig('file.yaml', { type: 'kubernetes' }); + const result = await generateConfig(filePath, { type: 'kubernetes' }); expect(result).toBe(false); expect(logger.__getLogs().log).toEqual([ 'Could not find a valid specification to generate configuration.', diff --git a/packages/insomnia-inso/src/commands/generate-config.ts b/packages/insomnia-inso/src/commands/generate-config.ts index b424bfde0a..fdb14d1499 100644 --- a/packages/insomnia-inso/src/commands/generate-config.ts +++ b/packages/insomnia-inso/src/commands/generate-config.ts @@ -1,4 +1,4 @@ -import { ConversionResult, ConversionResultType, generate, generateFromString } from 'openapi-2-kong'; +import { ConversionResult, ConversionResultType, DeclarativeConfig, generate, generateFromString, K8sManifest } from 'openapi-2-kong'; import YAML from 'yaml'; import path from 'path'; import type { GlobalOptions } from '../get-options'; @@ -8,18 +8,20 @@ import { writeFileWithCliOptions } from '../write-file'; import { logger } from '../logger'; import { InsoError } from '../errors'; -export const conversionTypeMap = { +export type ConversionOption = 'kubernetes' | 'declarative'; + +export const conversionOptions: ConversionOption[] = [ + 'declarative', + 'kubernetes', +]; + +export const conversionTypeMap: Record = { kubernetes: 'kong-for-kubernetes', declarative: 'kong-declarative-config', } as const; -export const conversionTypes: ConversionResultType[] = [ - 'kong-declarative-config', - 'kong-for-kubernetes', -]; - export type GenerateConfigOptions = GlobalOptions & { - type: keyof (typeof conversionTypeMap); + type: ConversionOption; output?: string; tags?: string, }; @@ -29,7 +31,7 @@ const validateOptions = ( ): options is Partial & Pick => { const { type } = options; if (!type || !conversionTypeMap[type]) { - logger.fatal(`Config type "${type}" not unrecognized. Options are [${Object.keys(conversionTypeMap).join(', ')}].`); + logger.fatal(`Config type "${type}" not unrecognized. Options are [${conversionOptions.join(', ')}].`); return false; } @@ -57,13 +59,13 @@ export const generateConfig = async ( const specFromDb = identifier ? loadApiSpec(db, identifier) : await promptApiSpec(db, !!ci); const generationTags = tags?.split(','); - + const conversionType = conversionTypeMap[type]; try { if (specFromDb?.contents) { logger.trace('Generating config from database contents'); result = await generateFromString( specFromDb.contents, - conversionTypeMap[type], + conversionType, generationTags, ); } else if (identifier) { @@ -72,7 +74,7 @@ export const generateConfig = async ( ? identifier : path.join(workingDir || '.', identifier); logger.trace(`Generating config from file \`${fileName}\``); - result = await generate(fileName, conversionTypeMap[type], generationTags); + result = await generate(fileName, conversionType, generationTags); } } catch (e) { throw new InsoError('There was an error while generating configuration', e); @@ -83,7 +85,7 @@ export const generateConfig = async ( return false; } - const yamlDocs = result.documents.map(document => YAML.stringify(document)); + const yamlDocs = result.documents.map((document: DeclarativeConfig | K8sManifest) => YAML.stringify(document)); // Join the YAML docs with "---" and strip any extra newlines surrounding them const document = yamlDocs.join('\n---\n').replace(/\n+---\n+/g, '\n---\n'); diff --git a/packages/insomnia-inso/src/openapi-2-kong.d.ts b/packages/insomnia-inso/src/openapi-2-kong.d.ts deleted file mode 100644 index 27d3c10459..0000000000 --- a/packages/insomnia-inso/src/openapi-2-kong.d.ts +++ /dev/null @@ -1,57 +0,0 @@ -// TODO(TSCONVERSION) remove this when the conversion for o2k is complete - -declare module 'openapi-2-kong' { - /** TODO(TSCONVERSION) this can be taken from the official types OpenAPIV3.Document */ - interface OpenAPI3Document { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [todo: string]: any; - } - - export type ConversionResultType = 'kong-declarative-config' | 'kong-for-kubernetes' - - export interface DeclarativeConfigResult { - type: 'kong-declarative-config', - label: string, - documents: Object, - warnings: { - severity: number, - message: string, - range: { - /* TODO */ - }, - }[], - } - - export interface KongForKubernetesResult { - type: 'kong-for-kubernetes', - label: string, - documents: Object[], - warnings: { - severity: number, - message: string, - range: { - /* TODO */ - }, - }[], - } - - export type ConversionResult = DeclarativeConfigResult | KongForKubernetesResult; - - export function generateFromString( - spec: string, - type: ConversionResultType, - tags?: string[], - ): Promise - - export function generateFromSpec( - spec: OpenAPI3Document, - type: ConversionResultType, - tags?: string[] - ): Promise - - export function generate( - filename: string, - type: ConversionResultType, - tags?: string[], - ): Promise -} diff --git a/packages/openapi-2-kong/src/declarative-config/generate.ts b/packages/openapi-2-kong/src/declarative-config/generate.ts index cfeeedfb8e..bdf2b07ddc 100644 --- a/packages/openapi-2-kong/src/declarative-config/generate.ts +++ b/packages/openapi-2-kong/src/declarative-config/generate.ts @@ -1,3 +1,4 @@ +import { DeclarativeConfig } from '../types/declarative-config'; import { OpenApi3Spec } from '../types/openapi3'; import { DeclarativeConfigResult } from '../types/outputs'; import { generateServices } from './services'; @@ -6,28 +7,26 @@ import { generateUpstreams } from './upstreams'; export function generateDeclarativeConfigFromSpec( api: OpenApi3Spec, tags: string[], -): DeclarativeConfigResult { - let document = null; - +) { try { - document = { + const document: DeclarativeConfig = { _format_version: '1.1', services: generateServices(api, tags), upstreams: generateUpstreams(api, tags), }; - } catch (err) { - throw new Error('Failed to generate spec: ' + err.message); - } - - // This remover any circular references or weirdness that might result - // from the JS objects used. - // SEE: https://github.com/Kong/studio/issues/93 - return JSON.parse( - JSON.stringify({ + const declarativeConfigResult: DeclarativeConfigResult = { type: 'kong-declarative-config', label: 'Kong Declarative Config', documents: [document], warnings: [], - }), - ); + }; + + // This remover any circular references or weirdness that might result + // from the JS objects used. + // SEE: https://github.com/Kong/studio/issues/93 + const result: DeclarativeConfigResult = JSON.parse(JSON.stringify(declarativeConfigResult)); + return result; + } catch (err) { + throw new Error('Failed to generate spec: ' + err.message); + } } diff --git a/packages/openapi-2-kong/src/declarative-config/upstreams.test.ts b/packages/openapi-2-kong/src/declarative-config/upstreams.test.ts index 21a84cf1cf..5d7f147ba3 100644 --- a/packages/openapi-2-kong/src/declarative-config/upstreams.test.ts +++ b/packages/openapi-2-kong/src/declarative-config/upstreams.test.ts @@ -4,6 +4,7 @@ import { getSpec } from './utils'; const xKongUpstreamDefaults = 'x-kong-upstream-defaults'; +/** This function is written in such a way as to allow mutations in tests but without affecting other tests. */ const getSpecResult = () => JSON.parse( JSON.stringify({ diff --git a/packages/openapi-2-kong/src/fixtures.test.ts b/packages/openapi-2-kong/src/fixtures.test.ts index 1cf044df1a..287f0f37c6 100644 --- a/packages/openapi-2-kong/src/fixtures.test.ts +++ b/packages/openapi-2-kong/src/fixtures.test.ts @@ -15,11 +15,10 @@ describe('fixtures', () => { it(`converts ${fileBase}`, async () => { const result = await generate(inputPath, 'kong-declarative-config'); - // @ts-expect-error -- TSCONVERSION expect(result.documents.length).toBe(1); const document = result.documents[0]; - const expectedObj = JSON.parse(expected) as DeclarativeConfig; + const expectedObj: DeclarativeConfig = JSON.parse(expected); // Make matching friendlier for (const service of expectedObj.services || []) { diff --git a/packages/openapi-2-kong/src/generate.test.ts b/packages/openapi-2-kong/src/generate.test.ts index a61f624060..214efb3f87 100644 --- a/packages/openapi-2-kong/src/generate.test.ts +++ b/packages/openapi-2-kong/src/generate.test.ts @@ -8,7 +8,6 @@ describe('index', () => { it('generates DC from file', async () => { const p = path.join(__dirname, './fixtures/uspto.yaml'); const { - // @ts-expect-error -- TSCONVERSION documents: [dc], } = await generate(p, 'kong-declarative-config'); expect(dc._format_version).toBe('1.1'); diff --git a/packages/openapi-2-kong/src/generate.ts b/packages/openapi-2-kong/src/generate.ts index c115b348c6..8346ec19ee 100644 --- a/packages/openapi-2-kong/src/generate.ts +++ b/packages/openapi-2-kong/src/generate.ts @@ -5,10 +5,15 @@ import { generateKongForKubernetesConfigFromSpec } from './kubernetes'; import SwaggerParser from 'swagger-parser'; import { OpenApi3Spec } from './types/openapi3'; import { ConversionResultType, ConversionResult } from './types/outputs'; -import { parse as YAMLparse } from 'yaml'; +import YAML from 'yaml'; const defaultTags = ['OAS3_import']; +export const conversionTypes: ConversionResultType[] = [ + 'kong-declarative-config', + 'kong-for-kubernetes', +]; + export async function generate( specPath: string, type: ConversionResultType, @@ -63,7 +68,7 @@ export async function parseSpec(spec: string | Record) { try { api = JSON.parse(spec); } catch (err) { - api = YAMLparse(spec); + api = YAML.parse(spec); } } else { api = JSON.parse(JSON.stringify(spec)); diff --git a/packages/openapi-2-kong/src/index.ts b/packages/openapi-2-kong/src/index.ts index cf90bc8484..a55ee5f375 100644 --- a/packages/openapi-2-kong/src/index.ts +++ b/packages/openapi-2-kong/src/index.ts @@ -1,6 +1,13 @@ export { + conversionTypes, generate, generateFromString, generateFromSpec, parseSpec, } from './generate'; + +export * from './types/declarative-config'; +export * from './types/k8splugins'; +export * from './types/kubernetes-config'; +export * from './types/openapi3'; +export * from './types/outputs'; diff --git a/packages/openapi-2-kong/src/kubernetes/generate.ts b/packages/openapi-2-kong/src/kubernetes/generate.ts index 80f6213f6c..ca3621dee1 100644 --- a/packages/openapi-2-kong/src/kubernetes/generate.ts +++ b/packages/openapi-2-kong/src/kubernetes/generate.ts @@ -2,8 +2,8 @@ import { getMethodAnnotationName, getName, HttpMethodType, parseUrl } from '../c import urlJoin from 'url-join'; import { flattenPluginDocuments, getPlugins, prioritizePlugins } from './plugins'; import { pathVariablesToWildcard, resolveUrlVariables } from './variables'; -import { IndexIncrement } from '../types/k8plugins'; -import { KubernetesConfig, KubernetesMethodConfig, K8sMetadata, K8sAnnotations, K8sIngressRule, K8sPath } from '../types/kubernetes-config'; +import { IndexIncrement } from '../types/k8splugins'; +import { K8sConfig, K8sMethodConfig, K8sMetadata, K8sAnnotations, K8sIngressRule, K8sPath } from '../types/kubernetes-config'; import { OpenApi3Spec, OA3Server } from '../types/openapi3'; import { KongForKubernetesResult } from '../types/outputs'; @@ -12,15 +12,15 @@ interface CustomAnnotations { overrideName?: string; } -export function generateKongForKubernetesConfigFromSpec(api: OpenApi3Spec): KongForKubernetesResult { +export function generateKongForKubernetesConfigFromSpec(api: OpenApi3Spec) { const specName = getSpecName(api); // Extract global, server, and path plugins upfront const plugins = getPlugins(api); // Initialize document collections - const ingressDocuments = []; - const methodsThatNeedKongIngressDocuments: Set = new Set(); + const ingressDocuments: K8sConfig[] = []; + const methodsThatNeedKongIngressDocuments = new Set(); let _iterator = 0; const increment = (): number => _iterator++; @@ -47,7 +47,7 @@ export function generateKongForKubernetesConfigFromSpec(api: OpenApi3Spec): Kong // Create metadata const metadata = generateMetadata(api, annotations, increment, specName); // Generate Kong ingress document for a server and path in the doc - const doc: KubernetesConfig = { + const doc: K8sConfig = { apiVersion: 'extensions/v1beta1', kind: 'Ingress', metadata, @@ -68,15 +68,16 @@ export function generateKongForKubernetesConfigFromSpec(api: OpenApi3Spec): Kong const documents = [...methodDocuments, ...pluginDocuments, ...ingressDocuments]; - return { + const result: KongForKubernetesResult = { type: 'kong-for-kubernetes', label: 'Kong for Kubernetes', documents, warnings: [], - } as KongForKubernetesResult; + }; + return result; } -function generateK8sMethodDocuments(method: HttpMethodType): KubernetesMethodConfig { +function generateK8sMethodDocuments(method: HttpMethodType): K8sMethodConfig { return { apiVersion: 'configuration.konghq.com/v1', kind: 'KongIngress', @@ -116,7 +117,7 @@ export function getSpecName(api: OpenApi3Spec): string { export function generateMetadataAnnotations( api: OpenApi3Spec, { pluginNames, overrideName }: CustomAnnotations, -): K8sAnnotations { +) { // This annotation is required by kong-ingress-controller // https://github.com/Kong/kubernetes-ingress-controller/blob/main/docs/references/annotations.md#kubernetesioingressclass const coreAnnotations: K8sAnnotations = { @@ -126,7 +127,7 @@ export function generateMetadataAnnotations( // Only continue if metadata annotations, or plugins, or overrides exist if (metadata?.annotations || pluginNames.length || overrideName) { - const customAnnotations = {}; + const customAnnotations: K8sAnnotations = {}; if (pluginNames.length) { customAnnotations['konghq.com/plugins'] = pluginNames.join(', '); diff --git a/packages/openapi-2-kong/src/kubernetes/plugin-helpers.ts b/packages/openapi-2-kong/src/kubernetes/plugin-helpers.ts index 8858f2fae5..147ce2ea3b 100644 --- a/packages/openapi-2-kong/src/kubernetes/plugin-helpers.ts +++ b/packages/openapi-2-kong/src/kubernetes/plugin-helpers.ts @@ -1,4 +1,4 @@ -import { KubernetesPluginConfig } from '../types/kubernetes-config'; +import { K8sPluginConfig } from '../types/kubernetes-config'; import { XKongPluginKeyAuth, XKongPluginUnknown } from '../types/openapi3'; export const pluginKeyAuth: XKongPluginKeyAuth = { @@ -21,7 +21,7 @@ export const pluginDummy: XKongPluginUnknown<{ foo: 'bar' }> = { }, }; -export const pluginDocWithName = (name: string, pluginType: string): KubernetesPluginConfig => ({ +export const pluginDocWithName = (name: string, pluginType: string): K8sPluginConfig => ({ apiVersion: 'configuration.konghq.com/v1', kind: 'KongPlugin', metadata: { @@ -44,7 +44,7 @@ export const keyAuthPluginDoc = (suffix: string) => ({ plugin: 'key-auth', }); -export const dummyPluginDoc = (suffix: string): KubernetesPluginConfig => ({ +export const dummyPluginDoc = (suffix: string): K8sPluginConfig => ({ apiVersion: 'configuration.konghq.com/v1', config: { foo: 'bar', diff --git a/packages/openapi-2-kong/src/kubernetes/plugins.test.ts b/packages/openapi-2-kong/src/kubernetes/plugins.test.ts index e1bfa033be..ffc86be129 100644 --- a/packages/openapi-2-kong/src/kubernetes/plugins.test.ts +++ b/packages/openapi-2-kong/src/kubernetes/plugins.test.ts @@ -1,6 +1,6 @@ import { flattenPluginDocuments, - generateK8PluginConfig, + generateK8sPluginConfig, getGlobalPlugins, getOperationPlugins, getPathPlugins, @@ -18,7 +18,7 @@ import { pluginDummy, pluginKeyAuth, } from './plugin-helpers'; -import { PathPlugin, OperationPlugin } from '../types/k8plugins'; +import { PathPlugin, OperationPlugin } from '../types/k8splugins'; import { OpenApi3Spec, OA3Server, OA3Paths, OA3PathItem, OA3Components } from '../types/openapi3'; describe('plugins', () => { @@ -424,10 +424,10 @@ describe('plugins', () => { }); }); - describe('generateK8PluginConfig()', () => { + describe('generateK8sPluginConfig()', () => { it('should return empty array if no plugin keys found and not increment', () => { const incrementMock = jest.fn().mockReturnValue(0); - const result = generateK8PluginConfig(spec, 's', incrementMock); + const result = generateK8sPluginConfig(spec, 's', incrementMock); expect(result).toHaveLength(0); expect(incrementMock).not.toHaveBeenCalled(); }); @@ -435,7 +435,7 @@ describe('plugins', () => { it('should attach config onto plugin if it exists and increment', () => { const incrementMock = jest.fn().mockReturnValue(0); // @ts-expect-error -- TSCONVERSION not sure, but this is intentionally different maybe? - const result = generateK8PluginConfig({ ...spec, ...pluginKeyAuth }, 'greg', incrementMock); + const result = generateK8sPluginConfig({ ...spec, ...pluginKeyAuth }, 'greg', incrementMock); expect(result).toEqual([keyAuthPluginDoc('greg0')]); expect(incrementMock).toHaveBeenCalledTimes(1); }); diff --git a/packages/openapi-2-kong/src/kubernetes/plugins.ts b/packages/openapi-2-kong/src/kubernetes/plugins.ts index be9241a6ec..1710859912 100644 --- a/packages/openapi-2-kong/src/kubernetes/plugins.ts +++ b/packages/openapi-2-kong/src/kubernetes/plugins.ts @@ -8,13 +8,13 @@ import { } from '../common'; import { generateSecurityPlugins } from '../declarative-config/security-plugins'; import { DCPlugin } from '../types/declarative-config'; -import { Plugins, IndexIncrement, ServerPlugin, PathPlugin, OperationPlugin } from '../types/k8plugins'; -import { KubernetesPluginConfig } from '../types/kubernetes-config'; +import { Plugins, IndexIncrement, ServerPlugin, PathPlugin, OperationPlugin } from '../types/k8splugins'; +import { K8sPluginConfig } from '../types/kubernetes-config'; import { OpenApi3Spec, OA3Server, OA3Paths, OA3PathItem } from '../types/openapi3'; import { ValueOf } from 'type-fest'; -export function flattenPluginDocuments(plugins: Plugins): KubernetesPluginConfig[] { - const all: KubernetesPluginConfig[] = []; +export function flattenPluginDocuments(plugins: Plugins): K8sPluginConfig[] { + const all: K8sPluginConfig[] = []; const { global, servers, paths } = plugins; all.push(...global); servers.forEach(s => { @@ -52,13 +52,13 @@ export function getPlugins(api: OpenApi3Spec): Plugins { // NOTE: It isn't great that we're relying on declarative-config stuff here but there's // not much we can do about it. If we end up needing this again, it should be factored // out to a higher-level. -export function mapDcPluginsToK8Plugins( +export function mapDcPluginsToK8sPlugins( dcPlugins: DCPlugin[], suffix: string, increment: IndexIncrement, ) { return dcPlugins.map(dcPlugin => { - const k8sPlugin: KubernetesPluginConfig = { + const k8sPlugin: K8sPluginConfig = { apiVersion: 'configuration.konghq.com/v1', kind: 'KongPlugin', metadata: { @@ -77,13 +77,13 @@ export function mapDcPluginsToK8Plugins( export function getGlobalPlugins(api: OpenApi3Spec, increment: IndexIncrement) { const pluginNameSuffix = PluginNameSuffix.global; - const globalK8Plugins = generateK8PluginConfig(api, pluginNameSuffix, increment); - const securityPlugins = mapDcPluginsToK8Plugins( + const globalK8sPlugins = generateK8sPluginConfig(api, pluginNameSuffix, increment); + const securityPlugins = mapDcPluginsToK8sPlugins( generateSecurityPlugins(null, api, []), pluginNameSuffix, increment, ); - return [...globalK8Plugins, ...securityPlugins]; + return [...globalK8sPlugins, ...securityPlugins]; } export function getServerPlugins( @@ -92,7 +92,7 @@ export function getServerPlugins( ) { return servers.map(server => ({ server, - plugins: generateK8PluginConfig(server, PluginNameSuffix.server, increment), + plugins: generateK8sPluginConfig(server, PluginNameSuffix.server, increment), })); } @@ -101,7 +101,7 @@ export function getPathPlugins(paths: OA3Paths, increment: IndexIncrement, api: const pathItem = paths[path]; return { path, - plugins: generateK8PluginConfig(pathItem, PluginNameSuffix.path, increment), + plugins: generateK8sPluginConfig(pathItem, PluginNameSuffix.path, increment), operations: normalizeOperationPlugins(getOperationPlugins(pathItem, increment, api)), }; }); @@ -119,8 +119,8 @@ export function getOperationPlugins( // We know this will always, only be OA3Operation (because of the filter above), but Flow doesn't know that... const operation: Record = pathItem[key]; const pluginNameSuffix = PluginNameSuffix.operation; - const opPlugins = generateK8PluginConfig(operation, pluginNameSuffix, increment); - const securityPlugins = mapDcPluginsToK8Plugins( + const opPlugins = generateK8sPluginConfig(operation, pluginNameSuffix, increment); + const securityPlugins = mapDcPluginsToK8sPlugins( generateSecurityPlugins(operation, api, []), pluginNameSuffix, increment, @@ -144,17 +144,17 @@ const PluginNameSuffix = { type PluginNameSuffixKeys = ValueOf; -export function generateK8PluginConfig( +export function generateK8sPluginConfig( obj: Record, pluginNameSuffix: PluginNameSuffixKeys, increment: IndexIncrement, ) { - const plugins: KubernetesPluginConfig[] = []; + const plugins: K8sPluginConfig[] = []; for (const key of Object.keys(obj).filter(isPluginKey)) { const pData = obj[key]; const name = pData.name || getPluginNameFromKey(key); - const p: KubernetesPluginConfig = { + const p: K8sPluginConfig = { apiVersion: 'configuration.konghq.com/v1', kind: 'KongPlugin', metadata: { @@ -197,13 +197,13 @@ export function normalizeOperationPlugins(operationPlugins: OperationPlugin[]) { } export function prioritizePlugins( - global: KubernetesPluginConfig[], - server: KubernetesPluginConfig[], - path: KubernetesPluginConfig[], - operation: KubernetesPluginConfig[], + global: K8sPluginConfig[], + server: K8sPluginConfig[], + path: K8sPluginConfig[], + operation: K8sPluginConfig[], ) { // Order in priority: operation > path > server > global - const plugins: KubernetesPluginConfig[] = [...operation, ...path, ...server, ...global]; + const plugins: K8sPluginConfig[] = [...operation, ...path, ...server, ...global]; // Select first of each type of plugin return distinctByProperty(plugins, p => p.plugin); } diff --git a/packages/openapi-2-kong/src/types/k8plugins.ts b/packages/openapi-2-kong/src/types/k8splugins.ts similarity index 66% rename from packages/openapi-2-kong/src/types/k8plugins.ts rename to packages/openapi-2-kong/src/types/k8splugins.ts index 45f78055c8..5d5551f60b 100644 --- a/packages/openapi-2-kong/src/types/k8plugins.ts +++ b/packages/openapi-2-kong/src/types/k8splugins.ts @@ -1,25 +1,25 @@ import { HttpMethodType } from '../common'; -import { KubernetesPluginConfig } from './kubernetes-config'; +import { K8sPluginConfig } from './kubernetes-config'; import { OA3Server } from './openapi3'; export interface OperationPlugin { method?: HttpMethodType | null; - plugins: KubernetesPluginConfig[]; + plugins: K8sPluginConfig[]; } export interface PathPlugin { path: string; - plugins: KubernetesPluginConfig[]; + plugins: K8sPluginConfig[]; operations: OperationPlugin[]; } export interface ServerPlugin { server: OA3Server; - plugins: KubernetesPluginConfig[]; + plugins: K8sPluginConfig[]; } export interface Plugins { - global: KubernetesPluginConfig[]; + global: K8sPluginConfig[]; servers: ServerPlugin[]; paths: PathPlugin[]; } diff --git a/packages/openapi-2-kong/src/types/kubernetes-config.ts b/packages/openapi-2-kong/src/types/kubernetes-config.ts index 5a2284c4df..dea772aa0d 100644 --- a/packages/openapi-2-kong/src/types/kubernetes-config.ts +++ b/packages/openapi-2-kong/src/types/kubernetes-config.ts @@ -1,10 +1,24 @@ import { HttpMethodType } from '../common'; -export interface K8sAnnotations { - 'kubernetes.io/ingress.class': 'kong'; - [key: string]: string; +export interface K8sIngressClassAnnotation { + 'kubernetes.io/ingress.class'?: 'kong'; } +export interface K8sPluginsAnnotation { + 'konghq.com/plugins'?: string; +} + +export interface K8sOverrideAnnotation { + 'konghq.com/override'?: string; +} + +export type K8sAnnotations = + & K8sIngressClassAnnotation + & K8sOverrideAnnotation + & K8sPluginsAnnotation + & Record + ; + export interface K8sMetadata { name: string; annotations: K8sAnnotations; @@ -37,7 +51,7 @@ export interface K8sSpec { rules: K8sIngressRule[]; } -export interface KubernetesMethodConfig { +export interface K8sMethodConfig { apiVersion: 'configuration.konghq.com/v1'; kind: 'KongIngress'; metadata: { @@ -48,7 +62,7 @@ export interface KubernetesMethodConfig { }; } -export interface KubernetesPluginConfig { +export interface K8sPluginConfig { apiVersion: 'configuration.konghq.com/v1'; kind: 'KongPlugin'; metadata: { @@ -59,9 +73,15 @@ export interface KubernetesPluginConfig { plugin: string; } -export interface KubernetesConfig { +export interface K8sConfig { apiVersion: 'extensions/v1beta1'; kind: 'Ingress'; metadata: K8sMetadata; spec: K8sSpec; } + +export type K8sManifest = + | K8sConfig + | K8sMethodConfig + | K8sPluginConfig + ; diff --git a/packages/openapi-2-kong/src/types/outputs.ts b/packages/openapi-2-kong/src/types/outputs.ts index 315b660c75..d0dd511d58 100644 --- a/packages/openapi-2-kong/src/types/outputs.ts +++ b/packages/openapi-2-kong/src/types/outputs.ts @@ -1,32 +1,26 @@ import { DeclarativeConfig } from './declarative-config'; -import { - KubernetesConfig, - KubernetesMethodConfig, - KubernetesPluginConfig, -} from './kubernetes-config'; +import { K8sManifest } from './kubernetes-config'; export type ConversionResultType = 'kong-declarative-config' | 'kong-for-kubernetes'; +export interface Warning { + severity: number; + message: string; + range: {}; +} + export interface DeclarativeConfigResult { type: 'kong-declarative-config'; label: string; - documents: DeclarativeConfig; - warnings: { - severity: number; - message: string; - range: {}; - }[]; + documents: DeclarativeConfig[]; + warnings: Warning[]; } export interface KongForKubernetesResult { type: 'kong-for-kubernetes'; label: string; - documents: (KubernetesConfig | KubernetesPluginConfig | KubernetesMethodConfig)[]; - warnings: { - severity: number; - message: string; - range: {}; - }[]; + documents: K8sManifest[]; + warnings: Warning[]; } export type ConversionResult = DeclarativeConfigResult | KongForKubernetesResult;