diff --git a/src/index.ts b/src/index.ts index 5520a774..808abac0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ export { convert } from './convert'; -export type { AsyncAPIDocument, ConvertVersion, ConvertOptions } from './interfaces'; +export type { AsyncAPIDocument, AsyncAPIConvertVersion, OpenAPIConvertVersion, ConvertOptions } from './interfaces'; diff --git a/src/openapi.ts b/src/openapi.ts index 7c8f629d..b7437a63 100644 --- a/src/openapi.ts +++ b/src/openapi.ts @@ -1,4 +1,4 @@ -import { sortObjectKeys, isRefObject, isPlainObject } from "./utils"; +import { sortObjectKeys, isRefObject, isPlainObject, removeEmptyObjects } from "./utils"; import { AsyncAPIDocument, ConvertOpenAPIFunction, ConvertOptions, OpenAPIDocument } from "./interfaces"; export const converters: Record = { @@ -7,14 +7,16 @@ export const converters: Record = { function from_openapi_to_asyncapi(openapi: OpenAPIDocument, options?: ConvertOptions): AsyncAPIDocument { const asyncapi: Partial = { - asyncapi: openapi.openapi, + asyncapi: '3.0.0', info: convertInfoObject(openapi.info, openapi), - servers: isPlainObject(openapi.servers) ? convertServerObjects(openapi.servers, openapi) : undefined, - channels: convertPathsToChannels(openapi.paths), - operations: convertPathsToOperations(openapi.paths, 'server'), - components: convertComponents(openapi.components), + servers: convertServerObjects(openapi.servers, openapi), + channels: isPlainObject(openapi.paths) ? convertPathsToChannels(openapi.paths) : undefined, + operations: isPlainObject(openapi.paths) ? convertPathsToOperations(openapi.paths, 'server') : undefined, + components: isPlainObject(openapi.components) ? convertComponents(openapi.components): undefined, }; + removeEmptyObjects(asyncapi); + return sortObjectKeys( asyncapi as AsyncAPIDocument, ['asyncapi', 'info', 'defaultContentType', 'servers', 'channels', 'operations', 'components'] @@ -24,8 +26,8 @@ function from_openapi_to_asyncapi(openapi: OpenAPIDocument, options?: ConvertOpt function convertInfoObject(info: OpenAPIDocument['info'], openapi: OpenAPIDocument): AsyncAPIDocument['info'] { return sortObjectKeys({ ...info, - tags: openapi.tags || undefined, - externalDocs: openapi.externalDocs || undefined, + tags: [openapi.tags], + externalDocs: openapi.externalDocs, }, [ "title", "version", @@ -41,7 +43,9 @@ function convertInfoObject(info: OpenAPIDocument['info'], openapi: OpenAPIDocume function convertServerObjects(servers: Record, openapi: OpenAPIDocument): AsyncAPIDocument['servers'] { const newServers: Record = {}; const security: Record = openapi.security; - Object.entries(servers).forEach(([serverName, server]: [string, any]) => { + Object.entries(servers).forEach(([index, server]: [string, any]) => { + + const serverName = generateServerName(server.url); if (isRefObject(server)) { newServers[serverName] = server; return; @@ -72,19 +76,24 @@ function convertServerObjects(servers: Record, openapi: OpenAPIDocu return newServers; } +function generateServerName(url: string): string { + const { host, pathname } = resolveServerUrl(url); + const baseName = host.split('.').slice(-2).join('.'); + const pathSegment = pathname ? pathname.split('/')[1] : ''; + return `${baseName}${pathSegment ? `_${pathSegment}` : ''}`.replace(/[^a-zA-Z0-9_]/g, '_'); +} + function resolveServerUrl(url: string): { host: string; pathname?: string; protocol: string; } { let [maybeProtocol, maybeHost] = url.split("://"); - console.log("maybeProtocol", maybeProtocol, "maybeshost:", maybeHost) if (!maybeHost) { maybeHost = maybeProtocol; } const [host, ...pathnames] = maybeHost.split("/"); - console.log("host", host, "pathnames", pathnames) - console.log(`/${pathnames.join("/")}`) + if (pathnames.length) { return { host, diff --git a/src/utils.ts b/src/utils.ts index 8970ef45..7ef17ae3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -141,3 +141,17 @@ function untilde(str: string) { return sub; }); } + +export function removeEmptyObjects(obj: Record): Record { + Object.keys(obj).forEach(key => { + if (obj[key] && typeof obj[key] === 'object') { + removeEmptyObjects(obj[key]); + if (Object.keys(obj[key]).length === 0) { + delete obj[key]; + } + } else if (obj[key] === undefined) { + delete obj[key]; + } + }); + return obj; +} \ No newline at end of file diff --git a/test/input/openapi/no-channel-operation.yml b/test/input/openapi/no-channel-operation.yml index 6ecc2e97..0f0c4e30 100644 --- a/test/input/openapi/no-channel-operation.yml +++ b/test/input/openapi/no-channel-operation.yml @@ -26,13 +26,6 @@ servers: default: '8443' basePath: default: v2 - -security: - - {} - - petstore_auth: - - write:pets - - read:pets - tags: name: pet description: Pets operations diff --git a/test/output/openapi-to-asyncapi/no-channel-parameter.yml b/test/output/openapi-to-asyncapi/no-channel-parameter.yml index 8b4f905b..1a96efd1 100644 --- a/test/output/openapi-to-asyncapi/no-channel-parameter.yml +++ b/test/output/openapi-to-asyncapi/no-channel-parameter.yml @@ -1,5 +1,5 @@ asyncapi: 3.0.0 -info: +info: title: Sample Pet Store App version: 1.0.1 description: This is a sample server for a pet store. @@ -12,14 +12,16 @@ info: name: Apache 2.0 url: 'https://www.apache.org/licenses/LICENSE-2.0.html' tags: - name: pet - description: Pets operations + - name: pet + description: Pets operations externalDocs: description: Find more info here url: 'https://example.com' - servers: - - url: 'https://{username}.gigantic-server.com:{port}/{basePath}' + gigantic_server_com__port___basePath_: + host: '{username}.gigantic-server.com:{port}' + pathname: '/{basePath}' + protocol: https description: The production API server variables: username: @@ -34,9 +36,3 @@ servers: default: '8443' basePath: default: v2 - -security: - - {} - - petstore_auth: - - 'write:pets' - - 'read:pets'