Skip to content
This repository has been archived by the owner on Feb 5, 2025. It is now read-only.

feat(plugin): added new optional extention x-eventcatalog-message-action #35

Merged
merged 2 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/four-pigs-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@eventcatalog/generator-openapi": minor
---

feat(plugin): added new optional extention x-eventcatalog-message-action
23 changes: 16 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,15 @@ export default async (_: any, options: Props) => {
const processMessagesForOpenAPISpec = async (pathToSpec: string, document: OpenAPI.Document) => {
const operations = await getOperationsByType(pathToSpec);
const version = document.info.version;
let receives = [];
let receives = [],
sends = [];

// Go through all messages
for (const operation of operations) {
const { requestBodiesAndResponses, ...message } = await buildMessage(pathToSpec, document, operation);
let messageMarkdown = message.markdown;
const messageType = operation.type;
const messageAction = operation.action;

console.log(chalk.blue(`Processing message: ${message.name} (v${version})`));

Expand All @@ -197,11 +199,18 @@ const processMessagesForOpenAPISpec = async (pathToSpec: string, document: OpenA
// Write the message to the catalog
await writeMessage({ ...message, markdown: messageMarkdown }, { path: message.name });

// messages will always be messages the service receives
receives.push({
id: message.id,
version: message.version,
});
// If the message send or recieved by the service?
if (messageAction === 'sends') {
sends.push({
id: message.id,
version: message.version,
});
} else {
receives.push({
id: message.id,
version: message.version,
});
}

// Does the message have a request body or responses?
if (requestBodiesAndResponses?.requestBody) {
Expand Down Expand Up @@ -234,7 +243,7 @@ const processMessagesForOpenAPISpec = async (pathToSpec: string, document: OpenA
console.log(chalk.yellow(` - Use operationIds to give better unique names for EventCatalog`));
}
}
return { receives, sends: [] };
return { receives, sends };
};

const getParsedSpecFile = (service: Service, document: OpenAPI.Document) => {
Expand Down
34 changes: 34 additions & 0 deletions src/test/openapi-files/petstore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,28 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
/pets/{petId}/vaccinated:
post:
summary: Notify that a pet has been vaccinated
operationId: petVaccinated
tags:
- pets
x-eventcatalog-message-action: sends # This is a sends operation
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Vaccination'
required: true
responses:
'200':
description: Notification that the pet has been vaccinated successfully
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Pet:
Expand Down Expand Up @@ -153,6 +175,18 @@ components:
adopterName:
type: string
description: Name of the person who adopted the pet
Vaccination:
type: object
required:
- petId
- vaccine
properties:
petId:
type: integer
format: int64
vaccine:
type: string
description: Name of the vaccine administered
Error:
type: object
required:
Expand Down
63 changes: 38 additions & 25 deletions src/test/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,38 +578,51 @@ describe('OpenAPI EventCatalog Plugin', () => {
);
});

it('messages marked as "events" using the custom `x-ec-message-type` header in an OpenAPI are documented in EventCatalog as events ', async () => {
const { getEvent } = utils(catalogDir);
describe('OpenAPI eventcatalog extensions', () => {
it('messages marked as "events" using the custom `x-eventcatalog-message-type` header in an OpenAPI are documented in EventCatalog as events ', async () => {
const { getEvent } = utils(catalogDir);

await plugin(config, { services: [{ path: join(openAPIExamples, 'petstore.yml'), id: 'swagger-petstore' }] });
await plugin(config, { services: [{ path: join(openAPIExamples, 'petstore.yml'), id: 'swagger-petstore' }] });

const event = await getEvent('petAdopted');
const event = await getEvent('petAdopted');

expect(event).toEqual(
expect.objectContaining({
id: 'petAdopted',
name: 'petAdopted',
version: '1.0.0',
summary: 'Notify that a pet has been adopted',
})
);
});
expect(event).toEqual(
expect.objectContaining({
id: 'petAdopted',
name: 'petAdopted',
version: '1.0.0',
summary: 'Notify that a pet has been adopted',
})
);
});

it('messages marked as "commands" using the custom `x-ec-message-type` header in an OpenAPI are documented in EventCatalog as commands ', async () => {
const { getCommand } = utils(catalogDir);
it('messages marked as "commands" using the custom `x-eventcatalog-message-type` header in an OpenAPI are documented in EventCatalog as commands ', async () => {
const { getCommand } = utils(catalogDir);

await plugin(config, { services: [{ path: join(openAPIExamples, 'petstore.yml'), id: 'swagger-petstore' }] });
await plugin(config, { services: [{ path: join(openAPIExamples, 'petstore.yml'), id: 'swagger-petstore' }] });

const event = await getCommand('createPets');
const event = await getCommand('createPets');

expect(event).toEqual(
expect.objectContaining({
id: 'createPets',
name: 'createPets',
version: '1.0.0',
summary: 'Create a pet',
})
);
expect(event).toEqual(
expect.objectContaining({
id: 'createPets',
name: 'createPets',
version: '1.0.0',
summary: 'Create a pet',
})
);
});

it('messages marked as "sends" using the custom `x-eventcatalog-message-action` header in an OpenAPI are mapped against the service as messages the service sends ', async () => {
const { getService } = utils(catalogDir);

await plugin(config, { services: [{ path: join(openAPIExamples, 'petstore.yml'), id: 'swagger-petstore' }] });

const service = await getService('swagger-petstore');

expect(service.sends).toHaveLength(1);
expect(service.sends).toEqual([{ id: 'petVaccinated', version: '1.0.0' }]);
});
});

it('when the message already exists in EventCatalog but the versions do not match, the existing message is versioned', async () => {
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type Operation = {
summary?: string;
description?: string;
type: string;
action: string;
externalDocs?: OpenAPIV3_1.ExternalDocumentationObject;
tags: string[];
};
Expand Down
2 changes: 2 additions & 0 deletions src/utils/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,15 @@ export async function getOperationsByType(openApiPath: string) {

// Check if the x-eventcatalog-message-type field is set
const messageType = openAPIOperation['x-eventcatalog-message-type'] || DEFAULT_MESSAGE_TYPE;
const messageAction = openAPIOperation['x-eventcatalog-message-action'] === 'sends' ? 'sends' : 'receives';

const operation = {
path: path,
method: method.toUpperCase(),
operationId: openAPIOperation.operationId,
externalDocs: openAPIOperation.externalDocs,
type: messageType,
action: messageAction,
description: openAPIOperation.description,
summary: openAPIOperation.summary,
tags: openAPIOperation.tags || [],
Expand Down