Skip to content

Commit

Permalink
add openapi conversion support
Browse files Browse the repository at this point in the history
  • Loading branch information
Gmin2 committed Aug 18, 2024
1 parent 95cbd2c commit 51064e8
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 11 deletions.
8 changes: 5 additions & 3 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,22 +308,24 @@ _See code: [src/commands/config/versions.ts](https://github.com/asyncapi/cli/blo

## `asyncapi convert [SPEC-FILE]`

Convert asyncapi documents older to newer versions
Convert asyncapi documents older to newer versions or or OpenAPI documents to AsyncAPI

```
USAGE
$ asyncapi convert [SPEC-FILE] [-h] [-o <value>] [-t <value>]
$ asyncapi convert [SPEC-FILE] [-h] [-o <value>] [-t <value>] [-p <value>]
ARGUMENTS
SPEC-FILE spec path, url, or context-name
FLAGS
-h, --help Show CLI help.
-o, --output=<value> path to the file where the result is saved
-p, --perspective=<option> [default: server] Perspective to use when converting OpenAPI to AsyncAPI (client or server). Note: This option is only applicable for OpenAPI to AsyncAPI conversions.
<options: client|server>
-t, --target-version=<value> [default: 3.0.0] asyncapi version to convert to
DESCRIPTION
Convert asyncapi documents older to newer versions
Convert asyncapi documents older to newer versions or or OpenAPI documents to AsyncAPI
```

_See code: [src/commands/convert.ts](https://github.com/asyncapi/cli/blob/v2.3.1/src/commands/convert.ts)_
Expand Down
23 changes: 16 additions & 7 deletions src/commands/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import Command from '../core/base';
import { ValidationError } from '../core/errors/validation-error';
import { load } from '../core/models/SpecificationFile';
import { SpecificationFileNotFound } from '../core/errors/specification-file';
import { convert } from '@asyncapi/converter';
import type { AsyncAPIConvertVersion } from '@asyncapi/converter';
import { convert, convertOpenAPI } from '@asyncapi/converter';

Check failure on line 8 in src/commands/convert.ts

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Module '"@asyncapi/converter"' has no exported member 'convertOpenAPI'.
import type { AsyncAPIConvertVersion, OpenAPIConvertVersion } from '@asyncapi/converter';
import { cyan, green } from 'picocolors';

// @ts-ignore
Expand All @@ -16,7 +16,7 @@ import { convertFlags } from '../core/flags/convert.flags';
const latestVersion = Object.keys(specs.schemas).pop() as string;

export default class Convert extends Command {
static description = 'Convert asyncapi documents older to newer versions';
static description = 'Convert asyncapi documents older to newer versions or OpenAPI documents to AsyncAPI';

static flags = convertFlags(latestVersion);

Expand All @@ -36,13 +36,22 @@ export default class Convert extends Command {
// eslint-disable-next-line sonarjs/no-duplicate-string
this.metricsMetadata.to_version = flags['target-version'];

// Determine if the input is OpenAPI or AsyncAPI
const specJson = this.specFile.toJson();
const isOpenAPI = 'openapi' in specJson;

// CONVERSION
convertedFile = convert(this.specFile.text(), flags['target-version'] as AsyncAPIConvertVersion);
if (convertedFile) {
if (isOpenAPI) {
convertedFile = convertOpenAPI(this.specFile.text(), specJson.openapi as OpenAPIConvertVersion, {
perspective: flags['perspective'] as 'client' | 'server'
});
this.log(`🎉 The OpenAPI document has been successfully converted to AsyncAPI version ${green(flags['target-version'])}!`);
} else {
convertedFile = convert(this.specFile.text(), flags['target-version'] as AsyncAPIConvertVersion);
if (this.specFile.getFilePath()) {
this.log(`🎉 The ${cyan(this.specFile.getFilePath())} file has been successfully converted to version ${green(flags['target-version'])}!!`);
this.log(`🎉 The ${cyan(this.specFile.getFilePath())} file has been successfully converted to version ${green(flags['target-version'])}!`);
} else if (this.specFile.getFileURL()) {
this.log(`🎉 The URL ${cyan(this.specFile.getFileURL())} has been successfully converted to version ${green(flags['target-version'])}!!`);
this.log(`🎉 The URL ${cyan(this.specFile.getFileURL())} has been successfully converted to version ${green(flags['target-version'])}!`);
}
}

Expand Down
8 changes: 7 additions & 1 deletion src/core/flags/convert.flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ export const convertFlags = (latestVersion: string) => {
return {
help: Flags.help({ char: 'h' }),
output: Flags.string({ char: 'o', description: 'path to the file where the result is saved' }),
'target-version': Flags.string({ char: 't', description: 'asyncapi version to convert to', default: latestVersion })
'target-version': Flags.string({ char: 't', description: 'asyncapi version to convert to (for both asyncapi and openapi conversions)', default: latestVersion }),
perspective: Flags.string({
char: 'p',
description: 'perspective to use when converting OpenAPI to AsyncAPI (client or server). Note: This option is only applicable for OpenAPI to AsyncAPI conversions.',
options: ['client', 'server'],
default: 'server'
})
};
};
137 changes: 137 additions & 0 deletions test/fixtures/openapi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
openapi: 3.0.0
info:
title: Callbacks, Links, and Content Types API
version: 1.0.0
description: An API showcasing callbacks, links, and various content types
servers:
- url: https://api.example.com/v1
paths:
/webhooks:
post:
summary: Subscribe to webhook
operationId: subscribeWebhook
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
callbackUrl:
type: string
format: uri
responses:
'201':
description: Subscription created
callbacks:
onEvent:
'{$request.body#/callbackUrl}':
post:
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
eventType:
type: string
eventData:
type: object
responses:
'200':
description: Webhook processed
/users/{userId}:
get:
summary: Get a user
operationId: getUser
parameters:
- in: path
name: userId
required: true
schema:
type: string
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/User'
links:
userPosts:
operationId: getUserPosts
parameters:
userId: '$response.body#/id'
/users/{userId}/posts:
get:
summary: Get user posts
operationId: getUserPosts
parameters:
- in: path
name: userId
required: true
schema:
type: string
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Post'
/upload:
post:
summary: Upload a file
operationId: uploadFile
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
responses:
'200':
description: Successful upload
content:
application/json:
schema:
type: object
properties:
fileId:
type: string
/stream:
get:
summary: Get a data stream
operationId: getStream
responses:
'200':
description: Successful response
content:
application/octet-stream:
schema:
type: string
format: binary
components:
schemas:
User:
type: object
properties:
id:
type: string
name:
type: string
Post:
type: object
properties:
id:
type: string
title:
type: string
content:
type: string
62 changes: 62 additions & 0 deletions test/integration/convert.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { expect } from '@oclif/test';
const testHelper = new TestHelper();
const filePath = './test/fixtures/specification.yml';
const JSONFilePath = './test/fixtures/specification.json';
const openAPIFilePath = './test/fixtures/openapi.yml';

describe('convert', () => {
describe('with file paths', () => {
Expand Down Expand Up @@ -178,4 +179,65 @@ describe('convert', () => {
done();
});
});
describe('with OpenAPI input', () => {
beforeEach(() => {
testHelper.createDummyContextFile();
});

afterEach(() => {
testHelper.deleteDummyContextFile();
});

test
.stderr()
.stdout()
.command(['convert', openAPIFilePath])
.it('works when OpenAPI file path is passed', (ctx, done) => {
expect(ctx.stdout).to.contain('The OpenAPI document has been successfully converted to AsyncAPI version 3.0.0!');
expect(ctx.stderr).to.equal('');
done();
});

test
.stderr()
.stdout()
.command(['convert', openAPIFilePath, '-p=client'])
.it('works when OpenAPI file path is passed with client perspective', (ctx, done) => {
expect(ctx.stdout).to.contain('The OpenAPI document has been successfully converted to AsyncAPI version 3.0.0!');
expect(ctx.stderr).to.equal('');
done();
});

test
.stderr()
.stdout()
.command(['convert', openAPIFilePath, '-p=server'])
.it('works when OpenAPI file path is passed with server perspective', (ctx, done) => {
expect(ctx.stdout).to.contain('The OpenAPI document has been successfully converted to AsyncAPI version 3.0.0!');
expect(ctx.stderr).to.equal('');
done();
});

test
.stderr()
.stdout()
.command(['convert', openAPIFilePath, '-p=invalid'])
.it('should throw error if invalid perspective is passed', (ctx, done) => {
expect(ctx.stdout).to.equal('');
expect(ctx.stderr).to.contain('Error: Expected --perspective=invalid to be one of: client, server');
done();
});

test
.stderr()
.stdout()
.command(['convert', openAPIFilePath, '-o=./test/fixtures/openapi_converted_output.yml'])
.it('works when OpenAPI file is converted and output is saved', (ctx, done) => {
expect(ctx.stdout).to.contain('🎉 The OpenAPI document has been successfully converted to AsyncAPI version 3.0.0!');
expect(fs.existsSync('./test/fixtures/openapi_converted_output.yml')).to.equal(true);
expect(ctx.stderr).to.equal('');
fs.unlinkSync('./test/fixtures/openapi_converted_output.yml');
done();
});
});
});

0 comments on commit 51064e8

Please sign in to comment.