From e7fb7f63a0f5620aa3059265a9354238e962c8aa Mon Sep 17 00:00:00 2001 From: David Boyne Date: Thu, 6 Feb 2025 16:50:15 +0000 Subject: [PATCH 1/3] feat(plugin): openapi plugin now groups by service --- packages/generator-openapi/src/index.ts | 40 ++++++++++++---- .../generator-openapi/src/test/plugin.test.ts | 47 +++++++++++++++---- .../src/utils/catalog-shorthand.ts | 3 ++ 3 files changed, 72 insertions(+), 18 deletions(-) diff --git a/packages/generator-openapi/src/index.ts b/packages/generator-openapi/src/index.ts index fbdc400..9f1a4a8 100644 --- a/packages/generator-openapi/src/index.ts +++ b/packages/generator-openapi/src/index.ts @@ -12,13 +12,14 @@ import { getMessageTypeUtils } from './utils/catalog-shorthand'; import { OpenAPI } from 'openapi-types'; import checkLicense from './utils/checkLicense'; import yaml from 'js-yaml'; - +import { join } from 'node:path'; type Props = { services: Service[]; domain?: Domain; debug?: boolean; saveParsedSpecFile?: boolean; licenseKey?: string; + writeFilesToRoot?: boolean; }; export default async (_: any, options: Props) => { @@ -65,6 +66,14 @@ export default async (_: any, options: Props) => { let serviceSpecificationsFiles = []; let serviceSpecifications = service.specifications; + // Have to ../ as the SDK will put the files into hard coded folders + let servicePath = options.domain + ? join('../', 'domains', options.domain.id, 'services', service.id) + : join('../', 'services', service.id); + if (options.writeFilesToRoot) { + servicePath = service.id; + } + // Manage domain if (options.domain) { // Try and get the domain @@ -100,7 +109,7 @@ export default async (_: any, options: Props) => { } // Process all messages for the OpenAPI spec - let { sends, receives } = await processMessagesForOpenAPISpec(serviceSpec.path, document); + let { sends, receives } = await processMessagesForOpenAPISpec(serviceSpec.path, document, servicePath, options); let owners = []; let repository = null; @@ -142,7 +151,7 @@ export default async (_: any, options: Props) => { ...(owners ? { owners } : {}), ...(repository ? { repository } : {}), }, - { path: service.id, override: true } + { path: join(servicePath), override: true } ); // What files need added to the service (specification files) @@ -170,7 +179,12 @@ export default async (_: any, options: Props) => { } }; -const processMessagesForOpenAPISpec = async (pathToSpec: string, document: OpenAPI.Document) => { +const processMessagesForOpenAPISpec = async ( + pathToSpec: string, + document: OpenAPI.Document, + servicePath: string, + options: Props +) => { const operations = await getOperationsByType(pathToSpec); const version = document.info.version; let receives = [], @@ -185,10 +199,13 @@ const processMessagesForOpenAPISpec = async (pathToSpec: string, document: OpenA console.log(chalk.blue(`Processing message: ${message.name} (v${message.version})`)); - const { addFileToMessage, writeMessage, getMessage, versionMessage } = getMessageTypeUtils( - process.env.PROJECT_DIR as string, - messageType - ); + const { + addFileToMessage, + writeMessage, + getMessage, + versionMessage, + collection: folder, + } = getMessageTypeUtils(process.env.PROJECT_DIR as string, messageType); // Check if the message already exists in the catalog const catalogedMessage = await getMessage(message.id, 'latest'); @@ -203,8 +220,13 @@ const processMessagesForOpenAPISpec = async (pathToSpec: string, document: OpenA } } + let messagePath = join(servicePath, folder, message.id); + if (options.writeFilesToRoot) { + messagePath = message.id; + } + // Write the message to the catalog - await writeMessage({ ...message, markdown: messageMarkdown }, { path: message.id, override: true }); + await writeMessage({ ...message, markdown: messageMarkdown }, { path: messagePath, override: true }); // If the message send or recieved by the service? if (messageAction === 'sends') { diff --git a/packages/generator-openapi/src/test/plugin.test.ts b/packages/generator-openapi/src/test/plugin.test.ts index 2176921..b8220a5 100644 --- a/packages/generator-openapi/src/test/plugin.test.ts +++ b/packages/generator-openapi/src/test/plugin.test.ts @@ -591,11 +591,7 @@ describe('OpenAPI EventCatalog Plugin', () => { const command = await getQuery('list-pets'); - const dir = await fs.readdir(join(catalogDir, 'queries')); - - console.log(dir); - - const file = await fs.readFile(join(catalogDir, 'queries', 'list-pets', 'index.md')); + const file = await fs.readFile(join(catalogDir, 'services', 'swagger-petstore', 'queries', 'list-pets', 'index.md')); expect(file).toBeDefined(); expect(command).toEqual( @@ -787,7 +783,9 @@ describe('OpenAPI EventCatalog Plugin', () => { // Can the schema be something else than JSON schema? expect(command.schemaPath).toEqual('request-body.json'); - const schema = await fs.readFile(join(catalogDir, 'commands', 'createPets', 'request-body.json')); + const schema = await fs.readFile( + join(catalogDir, 'services', 'swagger-petstore', 'commands', 'createPets', 'request-body.json') + ); expect(schema).toBeDefined(); }); @@ -807,9 +805,9 @@ describe('OpenAPI EventCatalog Plugin', () => { await plugin(config, { services: [{ path: join(openAPIExamples, 'petstore.yml'), id: 'swagger-petstore' }] }); - const command = await getCommand('createPets'); - - const schema = await fs.readFile(join(catalogDir, 'commands', 'createPets', 'response-default.json')); + const schema = await fs.readFile( + join(catalogDir, 'services', 'swagger-petstore', 'commands', 'createPets', 'response-default.json') + ); expect(schema).toBeDefined(); }); @@ -903,6 +901,37 @@ describe('OpenAPI EventCatalog Plugin', () => { }); }); + describe('writeFilesToRoot', () => { + it('when writeFilesToRoot is set to true, the files are written to the root of the catalog and not inside the service folder', async () => { + await plugin(config, { + services: [{ path: join(openAPIExamples, 'petstore.yml'), id: 'swagger-petstore' }], + writeFilesToRoot: true, + }); + + // Read events directory + const eventsDir = await fs.readdir(join(catalogDir, 'events')); + expect(eventsDir).toEqual(['petAdopted']); + + const eventFiles = await fs.readdir(join(catalogDir, 'events', 'petAdopted')); + expect(eventFiles).toEqual(['index.md', 'request-body.json', 'response-default.json']); + + const commandsDir = await fs.readdir(join(catalogDir, 'commands')); + expect(commandsDir).toEqual(['createPets']); + + const commandFiles = await fs.readdir(join(catalogDir, 'commands', 'createPets')); + expect(commandFiles).toEqual(['index.md', 'request-body.json', 'response-default.json']); + + const queriesDir = await fs.readdir(join(catalogDir, 'queries')); + expect(queriesDir).toEqual(['list-pets', 'petVaccinated', 'showPetById']); + + const queryFiles = await fs.readdir(join(catalogDir, 'queries', 'list-pets')); + expect(queryFiles).toEqual(['index.md', 'response-200.json', 'response-default.json']); + + const serviceFiles = await fs.readdir(join(catalogDir, 'services', 'swagger-petstore')); + expect(serviceFiles).toEqual(['index.md', 'petstore.yml']); + }); + }); + it('when the OpenAPI service is already defined in the EventCatalog and the versions match, the owners and repository are persisted', async () => { // Create a service with the same name and version as the OpenAPI file for testing const { writeService, getService } = utils(catalogDir); diff --git a/packages/generator-openapi/src/utils/catalog-shorthand.ts b/packages/generator-openapi/src/utils/catalog-shorthand.ts index 9419728..656e024 100644 --- a/packages/generator-openapi/src/utils/catalog-shorthand.ts +++ b/packages/generator-openapi/src/utils/catalog-shorthand.ts @@ -30,6 +30,7 @@ export const getMessageTypeUtils = (projectDirectory: string, messageType: strin rmMessageById: rmEventById, writeMessage: writeEvent, addFileToMessage: addFileToEvent, + collection: 'events', }, command: { versionMessage: versionCommand, @@ -37,6 +38,7 @@ export const getMessageTypeUtils = (projectDirectory: string, messageType: strin rmMessageById: rmCommandById, writeMessage: writeCommand, addFileToMessage: addFileToCommand, + collection: 'commands', }, query: { versionMessage: versionQuery, @@ -44,6 +46,7 @@ export const getMessageTypeUtils = (projectDirectory: string, messageType: strin rmMessageById: rmQueryById, writeMessage: writeQuery, addFileToMessage: addFileToQuery, + collection: 'queries', }, }; From 889b780654f518873b14204813ce152df81c640a Mon Sep 17 00:00:00 2001 From: David Boyne Date: Thu, 6 Feb 2025 16:55:41 +0000 Subject: [PATCH 2/3] feat(plugin): openapi plugin now groups by service --- .../generator-federation/src/test/plugin.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/generator-federation/src/test/plugin.test.ts b/packages/generator-federation/src/test/plugin.test.ts index 2349a74..b8e2f68 100644 --- a/packages/generator-federation/src/test/plugin.test.ts +++ b/packages/generator-federation/src/test/plugin.test.ts @@ -41,7 +41,7 @@ describe('generator-federation', () => { const services = await fs.readdir(path.join(catalogDir, 'services')); expect(services).toHaveLength(3); }, - { timeout: 10000 } + { timeout: 20000 } ); it( @@ -60,7 +60,7 @@ describe('generator-federation', () => { const services = await fs.readdir(path.join(catalogDir, 'services')); expect(services).toHaveLength(4); }, - { timeout: 10000 } + { timeout: 20000 } ); it('if no copy configuration is provided then it clones target directory and copies all resources (e.g events, services, domains, teams, users) into the catalog', async () => { @@ -98,7 +98,7 @@ describe('generator-federation', () => { const services = await fs.readdir(path.join(catalogDir, 'services')); expect(services).toHaveLength(2); }, - { timeout: 10000 } + { timeout: 20000 } ); }); @@ -130,7 +130,7 @@ describe('generator-federation', () => { const services = await fs.readdir(path.join(catalogDir, 'services')); expect(services).toHaveLength(3); }, - { timeout: 10000 } + { timeout: 20000 } ); it( @@ -159,7 +159,7 @@ describe('generator-federation', () => { const services = await fs.readdir(path.join(catalogDir, 'services')); expect(services).toHaveLength(4); }, - { timeout: 10000 } + { timeout: 20000 } ); }); @@ -189,7 +189,7 @@ describe('generator-federation', () => { }) ).rejects.toThrow(/Path already exists at/); }, - { timeout: 10000 } + { timeout: 20000 } ); it( @@ -216,7 +216,7 @@ describe('generator-federation', () => { }) ).rejects.toThrow(/Path already exists at/); }, - { timeout: 10000 } + { timeout: 20000 } ); }); @@ -251,7 +251,7 @@ describe('generator-federation', () => { }) ).rejects.toThrow(/EventCatalog already has services with the id InventoryService/); }, - { timeout: 10000 } + { timeout: 20000 } ); }); }); From c04dbe84220dbe3894604980f99e9249440c11b8 Mon Sep 17 00:00:00 2001 From: David Boyne Date: Thu, 6 Feb 2025 16:56:02 +0000 Subject: [PATCH 3/3] Create cold-turtles-flash.md --- .changeset/cold-turtles-flash.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cold-turtles-flash.md diff --git a/.changeset/cold-turtles-flash.md b/.changeset/cold-turtles-flash.md new file mode 100644 index 0000000..5cace86 --- /dev/null +++ b/.changeset/cold-turtles-flash.md @@ -0,0 +1,5 @@ +--- +"@eventcatalog/generator-openapi": major +--- + +feat(plugin): openapi plugin now groups by service