Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: integrate cupid in cli using the relation command #143

Closed
Closed
26 changes: 26 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
},
"bugs": "https://github.com/asyncapi/cli/issues",
"dependencies": {
"@asyncapi/cupid": "^0.6.10",
"@asyncapi/parser": "^1.11.0",
"@asyncapi/studio": "^0.3.1",
"@fmvilas/oclif-plugin-spaced-commands": "^1.0.4",
Expand Down
75 changes: 75 additions & 0 deletions src/commands/relation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {flags} from '@oclif/command';
import * as cupid from '@asyncapi/cupid';
import Command from '../base';
import { load } from '../models/SpecificationFile';
import { ValidationError } from '../errors/validation-error';
import { SpecificationFileNotFound } from '../errors/specification-file';

export default class Relation extends Command {
static description = 'Visualize your defined event-driven architecture'

static flags = {
help: flags.help({char: 'h'}),
type: flags.string({
char: 't',
description: 'the type of output syntax'
}),
}

static strict = false;

static args = [];

async loadFiles(files: string[]) {
const docs: string[] = [];

await Promise.all(files.map(async (file) => {
try {
const spec = await load(file);
docs.push(await spec.read());
} catch (err) {
if (err instanceof SpecificationFileNotFound) {
this.error(
new ValidationError({
type: 'invalid-file',
filepath: file,
})
);
} else {
this.error(err as Error);
}
}
}));
return docs;
}

async run() {
const { argv, flags } = this.parse(Relation);

const outputType = flags['type'];

let docs;

if (argv.length <= 1) {
throw this.error('Please provide more than one context/filepaths.');
}

try {
docs = await this.loadFiles(argv);
} catch (err) {
this.error(err as Error);
}

let result;
try {
if (outputType) {
result = await cupid.getRelations(docs,{syntax: outputType});
} else {
result = await cupid.getRelations(docs);
}
this.log(result);
} catch (err) {
this.error(err as Error);
}
}
}
67 changes: 67 additions & 0 deletions test/commands/relation.test.ts
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arjungarg07 there has been a recent change in the folder structure for tests in #712. This can be moved into https://github.com/asyncapi/cli/tree/master/test/integration.

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {expect, test} from '@oclif/test';

const outputMermaid = 'graph TD\n server1[(mqtt://localhost:1883)]\nFlightSubscriberService[Flight Subscriber Service]\nFlightSubscriberService -- flight/queue --> server1\nFlightMonitorService[Flight Monitor Service]\nserver1 -- flight/queue --> FlightMonitorService\nFlightMonitorService -- flight/update --> server1\nFlightNotifierService[Flight Notifier Service]\nserver1 -- flight/update --> FlightNotifierService\n';
const outputPlantUML = '@startuml\ntitle Classes - Class Diagram\n\nclass server1 { \n url: mqtt://localhost:1883 \n protocol: mqtt\n}\nFlightMonitorService --|> server1:flight/update\nserver1 --|> FlightNotifierService:flight/update\nFlightSubscriberService --|> server1:flight/queue\nserver1 --|> FlightMonitorService:flight/queue\n@enduml\n';
const outputReactFlow = '[\n {\n id: \'Server1\',\n data: { label: \'mqtt://localhost:1883,mqtt\' },\n position: { x: 250, y: 5 }\n },\n {\n id: \'FlightMonitorService\',\n data: { label: \'Flight Monitor Service\' },\n position: { x: 100, y: 10 }\n },\n {\n id: \'edge1\',\n source: \'FlightMonitorService\',\n target: \'Server1\',\n animated: true,\n label: \'flight/update\',\n type: \'edgeType\',\n arrowHeadType: \'arrowclosed\'\n },\n {\n id: \'FlightNotifierService\',\n data: { label: \'Flight Notifier Service\' },\n position: { x: 100, y: 10 }\n },\n {\n id: \'edge2\',\n source: \'Server1\',\n target: \'FlightNotifierService\',\n animated: true,\n label: \'flight/update\',\n type: \'edgeType\',\n arrowHeadType: \'arrowclosed\'\n },\n {\n id: \'FlightSubscriberService\',\n data: { label: \'Flight Subscriber Service\' },\n position: { x: 100, y: 10 }\n },\n {\n id: \'edge3\',\n source: \'FlightSubscriberService\',\n target: \'Server1\',\n animated: true,\n label: \'flight/queue\',\n type: \'edgeType\',\n arrowHeadType: \'arrowclosed\'\n },\n {\n id: \'edge4\',\n source: \'Server1\',\n target: \'FlightMonitorService\',\n animated: true,\n label: \'flight/queue\',\n type: \'edgeType\',\n arrowHeadType: \'arrowclosed\'\n }\n]\n';

const defaultFilePaths = [
'./test/examples/flightService/monitor.yaml',
'./test/examples/flightService/notifier.yaml',
'./test/examples/flightService/subscriber.yaml',
];
describe('relation', () => {
test
.stderr()
.stdout()
.command([
'relation',
...defaultFilePaths,
'--type=mermaid'
])
.it('works and logs correct output when mermaid syntax is provided', (ctx, done) => {
expect(ctx.stdout).to.equal(outputMermaid);
expect(ctx.stderr).to.equal('');
done();
});

test
.stderr()
.stdout()
.command([
'relation',
...defaultFilePaths,
'--type=plantUML'
])
.it('works and logs correct output when plantUMl syntax is provided', (ctx, done) => {
expect(ctx.stdout).to.equal(outputPlantUML);
expect(ctx.stderr).to.equal('');
done();
});

test
.stderr()
.stdout()
.command([
'relation',
...defaultFilePaths,
'--type=reactFlow'
])
.it('works and logs correct output when reactFlow syntax is provided', (ctx, done) => {
expect(ctx.stdout).to.equal(outputReactFlow);
expect(ctx.stderr).to.equal('');
done();
});

test
.stderr()
.stdout()
.command([
'relation',
'./test/examples/flightService/monitor.yaml',
])
.it('should not work on providing zero or one single contexts/filepaths', (ctx, done) => {
expect(ctx.stdout).to.equal('');
expect(ctx.stderr).to.equal('Error: Please provide more than one context/filepaths.\n');
done();
});
});
35 changes: 35 additions & 0 deletions test/examples/common/messages/flight_queue.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
summary: Requets to queue a flight to be monitored
payload:
type: object
properties:
flight:
type: object
properties:
carrierCode:
type: string
description: 2 to 3-character IATA carrier code
example: "LH"
flightNumber:
type: integer
minimum: 1
description: 1 to 4-digit number of the flight
example: "193"
scheduledDepartureDate:
type: string
format: date-time
description: scheduled departure date of the flight, local to the departure airport.
example: "2020-10-20"
user:
type: object
properties:
userName:
type: string
minimum: 1
description: user name
example: "John Smith"
phoneNumber:
type: string
minimum: 5
description: phone number where notifications will be received.
example: "+13451235"

57 changes: 57 additions & 0 deletions test/examples/common/messages/flight_status.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
summary: Provides flight status on arrival and destination
payload:
type: object
properties:
user:
type: object
properties:
userName:
type: string
minimum: 1
description: user name
example: "John Smith"
phoneNumber:
type: string
minimum: 5
description: phone number where notifications will be received.
example: "+13451235"
departure:
type: object
properties:
iataCode:
type: string
description: 2 to 3-character IATA carrier code
example: "MAD"
scheduledDate:
type: string
format: date-time
description: scheduled datetime of the flight, local to the airport.
example: "2020-10-20 19:15"
gate:
type: string
description: departure gate
example: "2D"
terminal:
type: string
description: airport terminal
example: "4"
arrival:
type: object
properties:
iataCode:
type: string
description: 2 to 3-character IATA carrier code
example: "MAD"
scheduledDate:
type: string
format: date-time
description: scheduled datetime of the flight, local to the airport.
example: "2020-10-20 19:15"
gate:
type: string
description: departure gate
example: "2D"
terminal:
type: string
description: airport terminal
example: "4"
16 changes: 16 additions & 0 deletions test/examples/common/schemas/flight.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
type: object
properties:
carrierCode:
type: string
description: 2 to 3-character IATA carrier code
example: "LH"
flightNumber:
type: integer
minimum: 1
description: 1 to 4-digit number of the flight
example: "193"
scheduledDepartureDate:
type: string
format: date-time
description: scheduled departure date of the flight, local to the departure airport.
example: "2020-10-20"
19 changes: 19 additions & 0 deletions test/examples/common/schemas/segment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
type: object
properties:
iataCode:
type: string
description: 2 to 3-character IATA carrier code
example: "MAD"
scheduledDate:
type: string
format: date-time
description: scheduled datetime of the flight, local to the airport.
example: "2020-10-20 19:15"
gate:
type: string
description: departure gate
example: "2D"
terminal:
type: string
description: airport terminal
example: "4"
12 changes: 12 additions & 0 deletions test/examples/common/schemas/user.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
type: object
properties:
userName:
type: string
minimum: 1
description: user name
example: "John Smith"
phoneNumber:
type: string
minimum: 5
description: phone number where notifications will be received.
example: "+13451235"
36 changes: 36 additions & 0 deletions test/examples/flightService/monitor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Contributed by: Alvaro Navarro Link: https://github.com/amadeus4dev/amadeus-async-flight-status/
asyncapi: '2.0.0'
info:
title: Flight Monitor Service
version: '1.0.0'
description: |
provides real-time flight schedule data including up-to-date departure and arrival times,
terminal and gate information, flight duration and real-time delay status.
license:
name: Apache 2.0
url: 'https://www.apache.org/licenses/LICENSE-2.0'
servers:
development:
url: mqtt://localhost:1883
protocol: mqtt
channels:
flight/update:
description: |
Provides updates from a subscribed flight
subscribe:
summary: Inform about the status of a subscribed flight
message:
$ref: '#/components/messages/flightStatus'
flight/queue:
description: |
Queues a flight in order to retrieve status
publish:
summary: Subscribe about the status of a given flight
message:
$ref: '#/components/messages/flightQueue'
components:
messages:
flightStatus:
$ref: './test/examples/common/messages/flight_status.yaml'
flightQueue:
$ref: './test/examples/common/messages/flight_queue.yaml'
26 changes: 26 additions & 0 deletions test/examples/flightService/notifier.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Contributed by: Alvaro Navarro Link: https://github.com/amadeus4dev/amadeus-async-flight-status/
asyncapi: '2.0.0'
info:
title: Flight Notifier Service
version: '1.0.0'
description: |
Recevies updates from a subscribed flight and notifies via Twilio API.
license:
name: Apache 2.0
url: 'https://www.apache.org/licenses/LICENSE-2.0'
servers:
development:
url: mqtt://localhost:1883
protocol: mqtt
channels:
flight/update:
description: |
Receives updates from a subscribed flight
publish:
summary: Inform about the status of a subscribed flight
message:
$ref: '#/components/messages/flightStatus'
components:
messages:
flightStatus:
$ref: './test/examples/common/messages/flight_status.yaml'
Loading