From 3605bc17cc95fbe85f23efec67b8c3f111fad4cb Mon Sep 17 00:00:00 2001 From: Dimitri Mitropoulos Date: Wed, 19 May 2021 22:49:50 -0400 Subject: [PATCH] Feat/o2k improvements (#3396) * makes this `yaml` import match all others for better or for worse, until we upgrade `YAML` (it's now written in TypeScript) we should probably aim for consistency. * adds overloads so that types of package exports it would be FAR preferable to use an object with the arguments instead because then we don't have to fall into all the traps that come with overloads, but I am avoiding changing the actual call signature of these function. * fixes and updates types * corrects mispelling * removes shim types and exports actual types from openapi-2-kong we had everything in place for the rest of the build pipeline to consume these types, but were missing the main exports at the root index.ts file * use actual openapi-2-kong types now that they're available * use existing variable for filePath * extracts o2k-specific helper data to live in o2k evidently, this data is useful for the usage of o2k, therefore o2k should be the thing exporting it. * use KubernetesManifest for name of kubernetes manifest union when first naming this, I didn't have much more context than the property name (documents), so after taking a look, it's clear that these are kubernetes manifests * removes attempt at correctly typing overloads I will return to this, and so I'm leaving it here in a commit rather than squashing it out, but I can't see a way to get it to work without changing the signature in a big way. * fixes error (now that there are yaml types) * makes `K8s` vs `Kubernetes` type terminology consistent part of the motivation for this is that there are significant things in common between the two kinds of configs, making them closer in form is therefore ideal. * updates mistake in name per review feedback --- .eslintrc.js | 2 + .../__snapshots__/inso-snapshot.test.ts.snap | 2 +- packages/insomnia-inso/src/cli.ts | 6 +- .../src/commands/generate-config.test.ts | 15 ++--- .../src/commands/generate-config.ts | 28 ++++----- .../insomnia-inso/src/openapi-2-kong.d.ts | 57 ------------------- .../src/declarative-config/generate.ts | 29 +++++----- .../src/declarative-config/upstreams.test.ts | 1 + packages/openapi-2-kong/src/fixtures.test.ts | 3 +- packages/openapi-2-kong/src/generate.test.ts | 1 - packages/openapi-2-kong/src/generate.ts | 9 ++- packages/openapi-2-kong/src/index.ts | 7 +++ .../openapi-2-kong/src/kubernetes/generate.ts | 23 ++++---- .../src/kubernetes/plugin-helpers.ts | 6 +- .../src/kubernetes/plugins.test.ts | 10 ++-- .../openapi-2-kong/src/kubernetes/plugins.ts | 42 +++++++------- .../src/types/{k8plugins.ts => k8splugins.ts} | 10 ++-- .../src/types/kubernetes-config.ts | 32 +++++++++-- packages/openapi-2-kong/src/types/outputs.ts | 28 ++++----- 19 files changed, 142 insertions(+), 169 deletions(-) delete mode 100644 packages/insomnia-inso/src/openapi-2-kong.d.ts rename packages/openapi-2-kong/src/types/{k8plugins.ts => k8splugins.ts} (66%) 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;