From 50022a77e66aba348d5f0c72a42c20a10c044f53 Mon Sep 17 00:00:00 2001 From: jonaslagoni Date: Thu, 23 Apr 2026 16:07:36 +0200 Subject: [PATCH 1/2] add eventcatalog example --- examples/README.md | 9 + examples/eventcatalog-asyncapi/.gitignore | 9 + examples/eventcatalog-asyncapi/README.md | 72 +++ .../eventcatalog-asyncapi/codegen.config.mjs | 50 ++ .../eventcatalog/domains/user-domain/index.md | 41 ++ .../eventcatalog/eventcatalog.config.js | 28 ++ .../eventcatalog/events/UserSignedUp/index.md | 48 ++ .../eventcatalog/package.json | 19 + .../services/user-service/asyncapi.yaml | 170 +++++++ .../services/user-service/index.md | 61 +++ examples/eventcatalog-asyncapi/package.json | 18 + examples/eventcatalog-native/.gitignore | 9 + examples/eventcatalog-native/README.md | 93 ++++ .../eventcatalog-native/codegen.config.mjs | 35 ++ .../eventcatalog/domains/ecommerce/index.md | 15 + .../eventcatalog/eventcatalog.config.js | 10 + .../eventcatalog/events/OrderCreated/index.md | 11 + .../events/OrderCreated/schema.json | 34 ++ .../eventcatalog/events/OrderShipped/index.md | 11 + .../events/OrderShipped/schema.json | 23 + .../eventcatalog/events/StockUpdated/index.md | 11 + .../events/StockUpdated/schema.json | 25 + .../eventcatalog/package.json | 13 + .../services/inventory-service/index.md | 16 + .../services/order-service/index.md | 16 + examples/eventcatalog-native/package.json | 14 + examples/eventcatalog-openapi/.gitignore | 9 + examples/eventcatalog-openapi/README.md | 70 +++ .../eventcatalog-openapi/codegen.config.mjs | 45 ++ .../domains/ecommerce-domain/index.md | 42 ++ .../eventcatalog/eventcatalog.config.js | 28 ++ .../eventcatalog/package.json | 19 + .../services/petstore-api/index.md | 76 +++ .../services/petstore-api/openapi.json | 467 ++++++++++++++++++ examples/eventcatalog-openapi/package.json | 16 + 35 files changed, 1633 insertions(+) create mode 100644 examples/eventcatalog-asyncapi/.gitignore create mode 100644 examples/eventcatalog-asyncapi/README.md create mode 100644 examples/eventcatalog-asyncapi/codegen.config.mjs create mode 100644 examples/eventcatalog-asyncapi/eventcatalog/domains/user-domain/index.md create mode 100644 examples/eventcatalog-asyncapi/eventcatalog/eventcatalog.config.js create mode 100644 examples/eventcatalog-asyncapi/eventcatalog/events/UserSignedUp/index.md create mode 100644 examples/eventcatalog-asyncapi/eventcatalog/package.json create mode 100644 examples/eventcatalog-asyncapi/eventcatalog/services/user-service/asyncapi.yaml create mode 100644 examples/eventcatalog-asyncapi/eventcatalog/services/user-service/index.md create mode 100644 examples/eventcatalog-asyncapi/package.json create mode 100644 examples/eventcatalog-native/.gitignore create mode 100644 examples/eventcatalog-native/README.md create mode 100644 examples/eventcatalog-native/codegen.config.mjs create mode 100644 examples/eventcatalog-native/eventcatalog/domains/ecommerce/index.md create mode 100644 examples/eventcatalog-native/eventcatalog/eventcatalog.config.js create mode 100644 examples/eventcatalog-native/eventcatalog/events/OrderCreated/index.md create mode 100644 examples/eventcatalog-native/eventcatalog/events/OrderCreated/schema.json create mode 100644 examples/eventcatalog-native/eventcatalog/events/OrderShipped/index.md create mode 100644 examples/eventcatalog-native/eventcatalog/events/OrderShipped/schema.json create mode 100644 examples/eventcatalog-native/eventcatalog/events/StockUpdated/index.md create mode 100644 examples/eventcatalog-native/eventcatalog/events/StockUpdated/schema.json create mode 100644 examples/eventcatalog-native/eventcatalog/package.json create mode 100644 examples/eventcatalog-native/eventcatalog/services/inventory-service/index.md create mode 100644 examples/eventcatalog-native/eventcatalog/services/order-service/index.md create mode 100644 examples/eventcatalog-native/package.json create mode 100644 examples/eventcatalog-openapi/.gitignore create mode 100644 examples/eventcatalog-openapi/README.md create mode 100644 examples/eventcatalog-openapi/codegen.config.mjs create mode 100644 examples/eventcatalog-openapi/eventcatalog/domains/ecommerce-domain/index.md create mode 100644 examples/eventcatalog-openapi/eventcatalog/eventcatalog.config.js create mode 100644 examples/eventcatalog-openapi/eventcatalog/package.json create mode 100644 examples/eventcatalog-openapi/eventcatalog/services/petstore-api/index.md create mode 100644 examples/eventcatalog-openapi/eventcatalog/services/petstore-api/openapi.json create mode 100644 examples/eventcatalog-openapi/package.json diff --git a/examples/README.md b/examples/README.md index 14f15714..bc2e15ce 100644 --- a/examples/README.md +++ b/examples/README.md @@ -28,6 +28,15 @@ An example demonstrating how to generate complete, type-safe client SDKs for NAT ### [E-commerce AsyncAPI Types](./ecommerce-asyncapi-types/) A comprehensive example showing how to generate TypeScript types from AsyncAPI specifications for an e-commerce messaging system. +### [EventCatalog + AsyncAPI](./eventcatalog-asyncapi/) +An example demonstrating how to generate TypeScript code from AsyncAPI specifications stored in an [EventCatalog](https://eventcatalog.dev/). Shows how to use The Codegen Project with EventCatalog's service documentation structure. + +### [EventCatalog + OpenAPI](./eventcatalog-openapi/) +An example demonstrating how to generate a TypeScript HTTP client from OpenAPI specifications stored in an [EventCatalog](https://eventcatalog.dev/). Shows how to use The Codegen Project with EventCatalog's API documentation structure. + +### [EventCatalog Native](./eventcatalog-native/) +An example showing the native EventCatalog structure with markdown documentation and JSON Schema files, without AsyncAPI or OpenAPI specifications. Demonstrates generating models directly from EventCatalog event schemas. + ## Getting Started 1. Choose an example that matches your use case diff --git a/examples/eventcatalog-asyncapi/.gitignore b/examples/eventcatalog-asyncapi/.gitignore new file mode 100644 index 00000000..69aa5535 --- /dev/null +++ b/examples/eventcatalog-asyncapi/.gitignore @@ -0,0 +1,9 @@ +# Generated code +src/ + +# Dependencies +node_modules/ +eventcatalog/node_modules/ + +# Build artifacts +.eventcatalog/ diff --git a/examples/eventcatalog-asyncapi/README.md b/examples/eventcatalog-asyncapi/README.md new file mode 100644 index 00000000..01418323 --- /dev/null +++ b/examples/eventcatalog-asyncapi/README.md @@ -0,0 +1,72 @@ +# EventCatalog + AsyncAPI Example + +This example demonstrates the **proposed** EventCatalog integration where a service has an AsyncAPI specification attached. + +> **Note**: This is a showcase of the proposed `inputType: 'eventcatalog'` configuration. This feature does not exist yet. + +## Configuration + +```javascript +// codegen.config.mjs +export default { + inputType: 'eventcatalog', + inputPath: './eventcatalog', + service: 'user-service', // Service with asyncapiPath + language: 'typescript', + generators: [ + { preset: 'payloads', outputPath: './src/payloads' }, + { preset: 'channels', outputPath: './src/channels', protocols: ['nats'] }, + { preset: 'client', outputPath: './src/client', protocols: ['nats'] }, + ], +}; +``` + +## How It Works + +1. Read EventCatalog at `./eventcatalog` +2. Find `user-service` in `services/` +3. Detect `asyncapiPath: asyncapi.yaml` in service metadata +4. Load and parse the AsyncAPI spec +5. Generate code using AsyncAPI processing + +## Service Metadata + +The service's `index.md` declares its AsyncAPI spec: + +```yaml +--- +id: user-service +name: User Service +version: 1.0.0 +specifications: + asyncapiPath: asyncapi.yaml # <-- Auto-detected +--- +``` + +## Project Structure + +``` +eventcatalog-asyncapi/ +├── codegen.config.mjs +├── eventcatalog/ +│ ├── eventcatalog.config.js +│ ├── domains/user-domain/index.md +│ ├── services/user-service/ +│ │ ├── index.md # Has asyncapiPath in metadata +│ │ └── asyncapi.yaml # AsyncAPI 3.0 spec +│ └── events/UserSignedUp/index.md +└── src/ # Generated code +``` + +## Auto-Detection Logic + +| Service Metadata | Processing | +|------------------|------------| +| `asyncapiPath: ...` | AsyncAPI processing | +| `openapiPath: ...` | OpenAPI processing | +| Neither (just `sends`/`receives`) | Native JSON Schema | + +## Related Examples + +- [eventcatalog-openapi](../eventcatalog-openapi/) - Service with OpenAPI spec +- [eventcatalog-native](../eventcatalog-native/) - Service with native JSON Schema events diff --git a/examples/eventcatalog-asyncapi/codegen.config.mjs b/examples/eventcatalog-asyncapi/codegen.config.mjs new file mode 100644 index 00000000..38f84aae --- /dev/null +++ b/examples/eventcatalog-asyncapi/codegen.config.mjs @@ -0,0 +1,50 @@ +/** + * Codegen configuration for EventCatalog with AsyncAPI service. + * + * NOTE: This is a PROPOSED configuration format. The 'eventcatalog' input type + * does not exist yet - this example demonstrates what the integration COULD + * look like. + * + * The tool would detect that user-service has `asyncapiPath: asyncapi.yaml` + * in its metadata and automatically use AsyncAPI processing. + * + * @type {import("@the-codegen-project/cli").TheCodegenConfiguration} + */ +export default { + inputType: 'eventcatalog', + inputPath: './eventcatalog', + language: 'typescript', + + // Select which service to generate code for + service: 'user-service', + + generators: [ + { + preset: 'payloads', + outputPath: './src/payloads', + serializationType: 'json', + }, + { + preset: 'parameters', + outputPath: './src/parameters', + }, + { + preset: 'headers', + outputPath: './src/headers', + }, + { + preset: 'types', + outputPath: './src', + }, + { + preset: 'channels', + outputPath: './src/channels', + protocols: ['nats'], + }, + { + preset: 'client', + outputPath: './src/client', + protocols: ['nats'], + }, + ], +}; diff --git a/examples/eventcatalog-asyncapi/eventcatalog/domains/user-domain/index.md b/examples/eventcatalog-asyncapi/eventcatalog/domains/user-domain/index.md new file mode 100644 index 00000000..89fa92bc --- /dev/null +++ b/examples/eventcatalog-asyncapi/eventcatalog/domains/user-domain/index.md @@ -0,0 +1,41 @@ +--- +id: user-domain +name: User Domain +version: 1.0.0 +summary: Domain responsible for all user-related functionality +owners: + - platform-team +services: + - id: user-service + version: 1.0.0 +badges: + - content: Core Domain + backgroundColor: blue + textColor: white +--- + +## Overview + +The User Domain encompasses all functionality related to user management, including: + +- User registration and authentication +- Profile management +- User preferences +- Account lifecycle + +## Bounded Context + +This domain owns the concept of a "User" and is the source of truth for user identity and profile information. + +## Services + +| Service | Description | +|---------|-------------| +| User Service | Core service for user operations | + +## Events + +| Event | Published By | Description | +|-------|--------------|-------------| +| UserSignedUp | User Service | New user registration | +| UserProfileUpdated | User Service | Profile changes | diff --git a/examples/eventcatalog-asyncapi/eventcatalog/eventcatalog.config.js b/examples/eventcatalog-asyncapi/eventcatalog/eventcatalog.config.js new file mode 100644 index 00000000..c0c0f52f --- /dev/null +++ b/examples/eventcatalog-asyncapi/eventcatalog/eventcatalog.config.js @@ -0,0 +1,28 @@ +/** @type {import('@eventcatalog/core/bin/eventcatalog.config').Config} */ +export default { + title: 'User Domain Catalog', + tagline: 'Event-driven architecture documentation for the user domain', + organizationName: 'Acme Corp', + homepageLink: 'https://eventcatalog.dev/', + landingPage: '', + editUrl: 'https://github.com/acme-corp/eventcatalog/edit/main', + trailingSlash: false, + base: '/', + logo: { + alt: 'EventCatalog Logo', + src: '/logo.png', + }, + docs: { + sidebar: { + showPageHeadings: true, + }, + }, + generators: [ + // AsyncAPI generator can auto-populate the catalog from spec files + // ['@eventcatalog/generator-asyncapi', { + // services: [ + // { path: './services/user-service/asyncapi.yaml' } + // ] + // }] + ], +}; diff --git a/examples/eventcatalog-asyncapi/eventcatalog/events/UserSignedUp/index.md b/examples/eventcatalog-asyncapi/eventcatalog/events/UserSignedUp/index.md new file mode 100644 index 00000000..ab3fa4f6 --- /dev/null +++ b/examples/eventcatalog-asyncapi/eventcatalog/events/UserSignedUp/index.md @@ -0,0 +1,48 @@ +--- +id: UserSignedUp +name: User Signed Up +version: 1.0.0 +summary: Event published when a new user successfully registers +badges: + - content: Core Event + backgroundColor: green + textColor: white +owners: + - user-team +schemaPath: schema.json +--- + +## Overview + +The `UserSignedUp` event is published by the User Service whenever a new user successfully completes the registration process. This event is a key integration point for downstream services. + +## Consumers + +| Service | Purpose | +|---------|---------| +| Notification Service | Send welcome email | +| Analytics Service | Track signup metrics | +| Onboarding Service | Initialize user onboarding flow | + +## Payload Example + +```json +{ + "userId": "550e8400-e29b-41d4-a716-446655440000", + "email": "john.doe@example.com", + "displayName": "John Doe", + "signupTimestamp": "2024-01-15T10:30:00Z", + "signupSource": "web", + "metadata": { + "referralCode": "FRIEND123" + } +} +``` + +## Headers + +| Header | Type | Description | +|--------|------|-------------| +| correlationId | UUID | Request tracing ID | +| timestamp | ISO 8601 | Event timestamp | +| version | String | Schema version | diff --git a/examples/eventcatalog-asyncapi/eventcatalog/package.json b/examples/eventcatalog-asyncapi/eventcatalog/package.json new file mode 100644 index 00000000..1adbe422 --- /dev/null +++ b/examples/eventcatalog-asyncapi/eventcatalog/package.json @@ -0,0 +1,19 @@ +{ + "name": "user-domain-eventcatalog", + "version": "1.0.0", + "description": "EventCatalog for user domain services", + "private": true, + "type": "module", + "scripts": { + "dev": "eventcatalog dev", + "build": "eventcatalog build", + "preview": "eventcatalog preview", + "generate": "eventcatalog generate" + }, + "dependencies": { + "@eventcatalog/core": "^2.0.0" + }, + "devDependencies": { + "@eventcatalog/sdk": "^1.0.0" + } +} diff --git a/examples/eventcatalog-asyncapi/eventcatalog/services/user-service/asyncapi.yaml b/examples/eventcatalog-asyncapi/eventcatalog/services/user-service/asyncapi.yaml new file mode 100644 index 00000000..6e6cccdf --- /dev/null +++ b/examples/eventcatalog-asyncapi/eventcatalog/services/user-service/asyncapi.yaml @@ -0,0 +1,170 @@ +asyncapi: 3.0.0 +info: + title: User Service + version: 1.0.0 + description: | + The User Service handles user registration, authentication, and profile management. + It publishes events to notify other services about user-related activities. + contact: + name: User Team + email: user-team@acme.com + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + +servers: + production: + host: nats.acme.com:4222 + protocol: nats + description: Production NATS server + development: + host: localhost:4222 + protocol: nats + description: Local development NATS server + +channels: + userSignedup: + address: user/signedup/{region} + description: Channel for user signup events + parameters: + region: + description: The region where the user signed up + enum: + - us-east + - us-west + - eu-west + - ap-south + messages: + UserSignedUp: + $ref: '#/components/messages/UserSignedUp' + + userProfileUpdated: + address: user/profile/updated + description: Channel for user profile update events + messages: + UserProfileUpdated: + $ref: '#/components/messages/UserProfileUpdated' + +operations: + publishUserSignedup: + action: send + summary: Publish user signup event + description: | + Publishes an event when a new user successfully completes registration. + This event is consumed by notification, analytics, and onboarding services. + channel: + $ref: '#/channels/userSignedup' + messages: + - $ref: '#/channels/userSignedup/messages/UserSignedUp' + + publishUserProfileUpdated: + action: send + summary: Publish user profile updated event + description: Publishes an event when a user updates their profile information. + channel: + $ref: '#/channels/userProfileUpdated' + messages: + - $ref: '#/channels/userProfileUpdated/messages/UserProfileUpdated' + +components: + messages: + UserSignedUp: + name: UserSignedUp + title: User Signed Up Event + summary: Event published when a new user registers + contentType: application/json + payload: + $ref: '#/components/schemas/UserSignedUpPayload' + headers: + $ref: '#/components/schemas/EventHeaders' + + UserProfileUpdated: + name: UserProfileUpdated + title: User Profile Updated Event + summary: Event published when a user updates their profile + contentType: application/json + payload: + $ref: '#/components/schemas/UserProfileUpdatedPayload' + headers: + $ref: '#/components/schemas/EventHeaders' + + schemas: + UserSignedUpPayload: + type: object + description: Payload for user signup events + required: + - userId + - email + - signupTimestamp + properties: + userId: + type: string + format: uuid + description: Unique identifier for the user + example: '550e8400-e29b-41d4-a716-446655440000' + email: + type: string + format: email + description: User's email address + example: 'john.doe@example.com' + displayName: + type: string + description: User's display name + example: 'John Doe' + signupTimestamp: + type: string + format: date-time + description: Timestamp when the user signed up + example: '2024-01-15T10:30:00Z' + signupSource: + type: string + enum: + - web + - mobile + - api + description: Source of the signup + example: 'web' + metadata: + type: object + additionalProperties: true + description: Additional metadata about the signup + + UserProfileUpdatedPayload: + type: object + description: Payload for user profile update events + required: + - userId + - updatedFields + - updateTimestamp + properties: + userId: + type: string + format: uuid + description: Unique identifier for the user + updatedFields: + type: array + items: + type: string + description: List of fields that were updated + example: ['displayName', 'avatar'] + updateTimestamp: + type: string + format: date-time + description: Timestamp when the profile was updated + + EventHeaders: + type: object + description: Common headers for all events + properties: + correlationId: + type: string + format: uuid + description: Correlation ID for request tracing + timestamp: + type: string + format: date-time + description: Event timestamp + version: + type: string + description: Event schema version + example: '1.0.0' diff --git a/examples/eventcatalog-asyncapi/eventcatalog/services/user-service/index.md b/examples/eventcatalog-asyncapi/eventcatalog/services/user-service/index.md new file mode 100644 index 00000000..b541e169 --- /dev/null +++ b/examples/eventcatalog-asyncapi/eventcatalog/services/user-service/index.md @@ -0,0 +1,61 @@ +--- +id: user-service +name: User Service +version: 1.0.0 +summary: Handles user registration, authentication, and profile management +owners: + - user-team +badges: + - content: AsyncAPI + backgroundColor: purple + textColor: white + - content: Production + backgroundColor: green + textColor: white +sends: + - id: UserSignedUp + version: 1.0.0 +receives: [] +repository: + language: TypeScript + url: https://github.com/acme-corp/user-service +specifications: + asyncapiPath: asyncapi.yaml +--- + +## Overview + +The User Service is responsible for all user-related operations in the platform. It publishes events when users perform actions like signing up, updating their profile, or changing their preferences. + +## Architecture + +```mermaid +graph LR + A[Client] --> B[User Service] + B --> C[NATS] + C --> D[Notification Service] + C --> E[Analytics Service] +``` + +## Events Published + +| Event | Description | +|-------|-------------| +| UserSignedUp | Published when a new user registers | + +## Getting Started + +```bash +# Install dependencies +npm install + +# Start the service +npm run start +``` + +## Configuration + +| Variable | Description | Default | +|----------|-------------|---------| +| `NATS_URL` | NATS server URL | `nats://localhost:4222` | +| `DATABASE_URL` | PostgreSQL connection string | - | diff --git a/examples/eventcatalog-asyncapi/package.json b/examples/eventcatalog-asyncapi/package.json new file mode 100644 index 00000000..0c691a96 --- /dev/null +++ b/examples/eventcatalog-asyncapi/package.json @@ -0,0 +1,18 @@ +{ + "name": "eventcatalog-asyncapi-example", + "version": "1.0.0", + "description": "Example: Generate TypeScript code from EventCatalog AsyncAPI specs", + "type": "module", + "scripts": { + "generate": "codegen generate", + "generate:watch": "codegen generate --watch", + "catalog:dev": "cd eventcatalog && npm run dev", + "catalog:build": "cd eventcatalog && npm run build" + }, + "devDependencies": { + "@the-codegen-project/cli": "latest" + }, + "dependencies": { + "nats": "^2.18.0" + } +} diff --git a/examples/eventcatalog-native/.gitignore b/examples/eventcatalog-native/.gitignore new file mode 100644 index 00000000..69aa5535 --- /dev/null +++ b/examples/eventcatalog-native/.gitignore @@ -0,0 +1,9 @@ +# Generated code +src/ + +# Dependencies +node_modules/ +eventcatalog/node_modules/ + +# Build artifacts +.eventcatalog/ diff --git a/examples/eventcatalog-native/README.md b/examples/eventcatalog-native/README.md new file mode 100644 index 00000000..b7c0625b --- /dev/null +++ b/examples/eventcatalog-native/README.md @@ -0,0 +1,93 @@ +# EventCatalog Native Example + +This example demonstrates the **proposed** EventCatalog integration where a service uses native EventCatalog events with JSON Schema (no AsyncAPI or OpenAPI specs). + +> **Note**: This is a showcase of the proposed `inputType: 'eventcatalog'` configuration. This feature does not exist yet. + +## Configuration + +```javascript +// codegen.config.mjs +export default { + inputType: 'eventcatalog', + inputPath: './eventcatalog', + service: 'order-service', // Service with sends/receives only + language: 'typescript', + generators: [ + { preset: 'payloads', outputPath: './src/payloads' }, + { preset: 'channels', outputPath: './src/channels', protocols: ['nats'] }, + { preset: 'client', outputPath: './src/client', protocols: ['nats'] }, + ], +}; +``` + +## How It Works + +1. Read EventCatalog at `./eventcatalog` +2. Find `order-service` in `services/` +3. No `asyncapiPath` or `openapiPath` → use native processing +4. Read `sends: [OrderCreated, OrderShipped]` from service metadata +5. Load `schema.json` from each event +6. Generate code from JSON Schemas + +## Service Metadata + +The service's `index.md` declares which events it sends/receives: + +```yaml +--- +id: order-service +name: Order Service +version: 1.0.0 +sends: + - id: OrderCreated + version: 1.0.0 + - id: OrderShipped + version: 1.0.0 +receives: [] +# No asyncapiPath or openapiPath → native processing +--- +``` + +## Project Structure + +``` +eventcatalog-native/ +├── codegen.config.mjs +├── eventcatalog/ +│ ├── eventcatalog.config.js +│ ├── domains/ecommerce/index.md +│ ├── services/ +│ │ ├── order-service/index.md # sends: [OrderCreated, OrderShipped] +│ │ └── inventory-service/index.md # sends: [StockUpdated] +│ └── events/ +│ ├── OrderCreated/ +│ │ ├── index.md +│ │ └── schema.json # JSON Schema for payload +│ ├── OrderShipped/ +│ │ ├── index.md +│ │ └── schema.json +│ └── StockUpdated/ +│ ├── index.md +│ └── schema.json +└── src/ # Generated code +``` + +## Auto-Detection Logic + +| Service Metadata | Processing | +|------------------|------------| +| `asyncapiPath: ...` | AsyncAPI processing | +| `openapiPath: ...` | OpenAPI processing | +| Neither (just `sends`/`receives`) | **Native JSON Schema** | + +## Benefits of Native + +- **No spec duplication** - EventCatalog is the source of truth +- **Simpler setup** - Just markdown + JSON Schema +- **Service-centric** - Events discovered from service relationships + +## Related Examples + +- [eventcatalog-asyncapi](../eventcatalog-asyncapi/) - Service with AsyncAPI spec +- [eventcatalog-openapi](../eventcatalog-openapi/) - Service with OpenAPI spec diff --git a/examples/eventcatalog-native/codegen.config.mjs b/examples/eventcatalog-native/codegen.config.mjs new file mode 100644 index 00000000..3388de4b --- /dev/null +++ b/examples/eventcatalog-native/codegen.config.mjs @@ -0,0 +1,35 @@ +/** + * Example codegen configuration for native EventCatalog integration. + * + * NOTE: This is a PROPOSED configuration format. The 'eventcatalog' input type + * does not exist yet - this example demonstrates what the integration COULD + * look like. + * + * @type {import("@the-codegen-project/cli").TheCodegenConfiguration} + */ +export default { + inputType: 'eventcatalog', + inputPath: './eventcatalog', + language: 'typescript', + + // Select which service to generate code for + service: 'order-service', + + generators: [ + { + preset: 'payloads', + outputPath: './src/payloads', + serializationType: 'json', + }, + { + preset: 'channels', + outputPath: './src/channels', + protocols: ['nats'], + }, + { + preset: 'client', + outputPath: './src/client', + protocols: ['nats'], + }, + ], +}; diff --git a/examples/eventcatalog-native/eventcatalog/domains/ecommerce/index.md b/examples/eventcatalog-native/eventcatalog/domains/ecommerce/index.md new file mode 100644 index 00000000..9367e6e8 --- /dev/null +++ b/examples/eventcatalog-native/eventcatalog/domains/ecommerce/index.md @@ -0,0 +1,15 @@ +--- +id: ecommerce +name: E-Commerce Domain +version: 1.0.0 +summary: Core e-commerce functionality +owners: + - platform-team +services: + - id: order-service + version: 1.0.0 + - id: inventory-service + version: 1.0.0 +--- + +The E-Commerce Domain handles orders and inventory management. diff --git a/examples/eventcatalog-native/eventcatalog/eventcatalog.config.js b/examples/eventcatalog-native/eventcatalog/eventcatalog.config.js new file mode 100644 index 00000000..15df2dc3 --- /dev/null +++ b/examples/eventcatalog-native/eventcatalog/eventcatalog.config.js @@ -0,0 +1,10 @@ +/** @type {import('@eventcatalog/core/bin/eventcatalog.config').Config} */ +export default { + title: 'E-Commerce Platform', + tagline: 'Native EventCatalog documentation', + organizationName: 'Acme Corp', + homepageLink: 'https://eventcatalog.dev/', + editUrl: 'https://github.com/acme/eventcatalog/edit/main', + trailingSlash: false, + base: '/', +}; diff --git a/examples/eventcatalog-native/eventcatalog/events/OrderCreated/index.md b/examples/eventcatalog-native/eventcatalog/events/OrderCreated/index.md new file mode 100644 index 00000000..32a5abf0 --- /dev/null +++ b/examples/eventcatalog-native/eventcatalog/events/OrderCreated/index.md @@ -0,0 +1,11 @@ +--- +id: OrderCreated +name: Order Created +version: 1.0.0 +summary: Published when a new order is placed +owners: + - orders-team +schemaPath: schema.json +--- + +Event published when a customer places a new order. diff --git a/examples/eventcatalog-native/eventcatalog/events/OrderCreated/schema.json b/examples/eventcatalog-native/eventcatalog/events/OrderCreated/schema.json new file mode 100644 index 00000000..61f5e7a6 --- /dev/null +++ b/examples/eventcatalog-native/eventcatalog/events/OrderCreated/schema.json @@ -0,0 +1,34 @@ +{ + "$id": "OrderCreated", + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": ["orderId", "customerId", "items", "totalAmount"], + "properties": { + "orderId": { + "type": "string", + "format": "uuid" + }, + "customerId": { + "type": "string", + "format": "uuid" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "productId": { "type": "string" }, + "quantity": { "type": "integer" }, + "price": { "type": "number" } + } + } + }, + "totalAmount": { + "type": "number" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } +} diff --git a/examples/eventcatalog-native/eventcatalog/events/OrderShipped/index.md b/examples/eventcatalog-native/eventcatalog/events/OrderShipped/index.md new file mode 100644 index 00000000..91558109 --- /dev/null +++ b/examples/eventcatalog-native/eventcatalog/events/OrderShipped/index.md @@ -0,0 +1,11 @@ +--- +id: OrderShipped +name: Order Shipped +version: 1.0.0 +summary: Published when an order is shipped +owners: + - orders-team +schemaPath: schema.json +--- + +Event published when an order has been shipped to the customer. diff --git a/examples/eventcatalog-native/eventcatalog/events/OrderShipped/schema.json b/examples/eventcatalog-native/eventcatalog/events/OrderShipped/schema.json new file mode 100644 index 00000000..1980d6f7 --- /dev/null +++ b/examples/eventcatalog-native/eventcatalog/events/OrderShipped/schema.json @@ -0,0 +1,23 @@ +{ + "$id": "OrderShipped", + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": ["orderId", "trackingNumber", "carrier"], + "properties": { + "orderId": { + "type": "string", + "format": "uuid" + }, + "trackingNumber": { + "type": "string" + }, + "carrier": { + "type": "string", + "enum": ["ups", "fedex", "dhl", "usps"] + }, + "shippedAt": { + "type": "string", + "format": "date-time" + } + } +} diff --git a/examples/eventcatalog-native/eventcatalog/events/StockUpdated/index.md b/examples/eventcatalog-native/eventcatalog/events/StockUpdated/index.md new file mode 100644 index 00000000..fba6cba2 --- /dev/null +++ b/examples/eventcatalog-native/eventcatalog/events/StockUpdated/index.md @@ -0,0 +1,11 @@ +--- +id: StockUpdated +name: Stock Updated +version: 1.0.0 +summary: Published when inventory stock level changes +owners: + - inventory-team +schemaPath: schema.json +--- + +Event published when product stock levels are updated. diff --git a/examples/eventcatalog-native/eventcatalog/events/StockUpdated/schema.json b/examples/eventcatalog-native/eventcatalog/events/StockUpdated/schema.json new file mode 100644 index 00000000..e677056b --- /dev/null +++ b/examples/eventcatalog-native/eventcatalog/events/StockUpdated/schema.json @@ -0,0 +1,25 @@ +{ + "$id": "StockUpdated", + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": ["productId", "previousQuantity", "newQuantity"], + "properties": { + "productId": { + "type": "string" + }, + "previousQuantity": { + "type": "integer" + }, + "newQuantity": { + "type": "integer" + }, + "reason": { + "type": "string", + "enum": ["sale", "restock", "adjustment", "return"] + }, + "updatedAt": { + "type": "string", + "format": "date-time" + } + } +} diff --git a/examples/eventcatalog-native/eventcatalog/package.json b/examples/eventcatalog-native/eventcatalog/package.json new file mode 100644 index 00000000..ea2d9a07 --- /dev/null +++ b/examples/eventcatalog-native/eventcatalog/package.json @@ -0,0 +1,13 @@ +{ + "name": "ecommerce-eventcatalog", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "eventcatalog dev", + "build": "eventcatalog build" + }, + "dependencies": { + "@eventcatalog/core": "^2.0.0" + } +} diff --git a/examples/eventcatalog-native/eventcatalog/services/inventory-service/index.md b/examples/eventcatalog-native/eventcatalog/services/inventory-service/index.md new file mode 100644 index 00000000..597c24bd --- /dev/null +++ b/examples/eventcatalog-native/eventcatalog/services/inventory-service/index.md @@ -0,0 +1,16 @@ +--- +id: inventory-service +name: Inventory Service +version: 1.0.0 +summary: Tracks product stock levels +owners: + - inventory-team +sends: + - id: StockUpdated + version: 1.0.0 +receives: + - id: OrderCreated + version: 1.0.0 +--- + +The Inventory Service manages product stock and alerts. diff --git a/examples/eventcatalog-native/eventcatalog/services/order-service/index.md b/examples/eventcatalog-native/eventcatalog/services/order-service/index.md new file mode 100644 index 00000000..a881234d --- /dev/null +++ b/examples/eventcatalog-native/eventcatalog/services/order-service/index.md @@ -0,0 +1,16 @@ +--- +id: order-service +name: Order Service +version: 1.0.0 +summary: Manages order lifecycle +owners: + - orders-team +sends: + - id: OrderCreated + version: 1.0.0 + - id: OrderShipped + version: 1.0.0 +receives: [] +--- + +The Order Service handles order creation and fulfillment. diff --git a/examples/eventcatalog-native/package.json b/examples/eventcatalog-native/package.json new file mode 100644 index 00000000..7995193a --- /dev/null +++ b/examples/eventcatalog-native/package.json @@ -0,0 +1,14 @@ +{ + "name": "eventcatalog-native-example", + "version": "1.0.0", + "description": "Example: Native EventCatalog structure with JSON Schema events", + "type": "module", + "scripts": { + "generate": "codegen generate", + "catalog:dev": "cd eventcatalog && npm run dev" + }, + "devDependencies": { + "@eventcatalog/sdk": "^1.0.0", + "@the-codegen-project/cli": "latest" + } +} diff --git a/examples/eventcatalog-openapi/.gitignore b/examples/eventcatalog-openapi/.gitignore new file mode 100644 index 00000000..69aa5535 --- /dev/null +++ b/examples/eventcatalog-openapi/.gitignore @@ -0,0 +1,9 @@ +# Generated code +src/ + +# Dependencies +node_modules/ +eventcatalog/node_modules/ + +# Build artifacts +.eventcatalog/ diff --git a/examples/eventcatalog-openapi/README.md b/examples/eventcatalog-openapi/README.md new file mode 100644 index 00000000..4065da71 --- /dev/null +++ b/examples/eventcatalog-openapi/README.md @@ -0,0 +1,70 @@ +# EventCatalog + OpenAPI Example + +This example demonstrates the **proposed** EventCatalog integration where a service has an OpenAPI specification attached. + +> **Note**: This is a showcase of the proposed `inputType: 'eventcatalog'` configuration. This feature does not exist yet. + +## Configuration + +```javascript +// codegen.config.mjs +export default { + inputType: 'eventcatalog', + inputPath: './eventcatalog', + service: 'petstore-api', // Service with openapiPath + language: 'typescript', + generators: [ + { preset: 'payloads', outputPath: './src/payloads' }, + { preset: 'channels', outputPath: './src/channels', protocols: ['http_client'] }, + ], +}; +``` + +## How It Works + +1. Read EventCatalog at `./eventcatalog` +2. Find `petstore-api` in `services/` +3. Detect `openapiPath: openapi.json` in service metadata +4. Load and parse the OpenAPI spec +5. Generate code using OpenAPI processing + +## Service Metadata + +The service's `index.md` declares its OpenAPI spec: + +```yaml +--- +id: petstore-api +name: Petstore API +version: 1.0.0 +specifications: + openapiPath: openapi.json # <-- Auto-detected +--- +``` + +## Project Structure + +``` +eventcatalog-openapi/ +├── codegen.config.mjs +├── eventcatalog/ +│ ├── eventcatalog.config.js +│ ├── domains/ecommerce-domain/index.md +│ └── services/petstore-api/ +│ ├── index.md # Has openapiPath in metadata +│ └── openapi.json # OpenAPI 3.0 spec +└── src/ # Generated code +``` + +## Auto-Detection Logic + +| Service Metadata | Processing | +|------------------|------------| +| `asyncapiPath: ...` | AsyncAPI processing | +| `openapiPath: ...` | OpenAPI processing | +| Neither (just `sends`/`receives`) | Native JSON Schema | + +## Related Examples + +- [eventcatalog-asyncapi](../eventcatalog-asyncapi/) - Service with AsyncAPI spec +- [eventcatalog-native](../eventcatalog-native/) - Service with native JSON Schema events diff --git a/examples/eventcatalog-openapi/codegen.config.mjs b/examples/eventcatalog-openapi/codegen.config.mjs new file mode 100644 index 00000000..6b125f23 --- /dev/null +++ b/examples/eventcatalog-openapi/codegen.config.mjs @@ -0,0 +1,45 @@ +/** + * Codegen configuration for EventCatalog with OpenAPI service. + * + * NOTE: This is a PROPOSED configuration format. The 'eventcatalog' input type + * does not exist yet - this example demonstrates what the integration COULD + * look like. + * + * The tool would detect that petstore-api has `openapiPath: openapi.json` + * in its metadata and automatically use OpenAPI processing. + * + * @type {import("@the-codegen-project/cli").TheCodegenConfiguration} + */ +export default { + inputType: 'eventcatalog', + inputPath: './eventcatalog', + language: 'typescript', + + // Select which service to generate code for + service: 'petstore-api', + + generators: [ + { + preset: 'payloads', + outputPath: './src/payloads', + serializationType: 'json', + }, + { + preset: 'parameters', + outputPath: './src/parameters', + }, + { + preset: 'headers', + outputPath: './src/headers', + }, + { + preset: 'types', + outputPath: './src', + }, + { + preset: 'channels', + outputPath: './src/channels', + protocols: ['http_client'], + }, + ], +}; diff --git a/examples/eventcatalog-openapi/eventcatalog/domains/ecommerce-domain/index.md b/examples/eventcatalog-openapi/eventcatalog/domains/ecommerce-domain/index.md new file mode 100644 index 00000000..7dfbff9c --- /dev/null +++ b/examples/eventcatalog-openapi/eventcatalog/domains/ecommerce-domain/index.md @@ -0,0 +1,42 @@ +--- +id: ecommerce-domain +name: E-Commerce Domain +version: 1.0.0 +summary: Domain encompassing all e-commerce functionality +owners: + - platform-team +services: + - id: petstore-api + version: 1.0.0 +badges: + - content: Core Domain + backgroundColor: blue + textColor: white +--- + +## Overview + +The E-Commerce Domain encompasses all functionality related to the online pet store, including: + +- Pet inventory management +- Order processing +- Customer management +- Payment processing + +## Bounded Context + +This domain owns the concepts of Pet, Order, and Store operations. It is the source of truth for product catalog and order lifecycle. + +## Services + +| Service | Type | Description | +|---------|------|-------------| +| Petstore API | REST | Main API for pet and order management | + +## Integration Points + +| System | Direction | Description | +|--------|-----------|-------------| +| Payment Gateway | Outbound | Process payments | +| Shipping Service | Outbound | Fulfill orders | +| Inventory System | Bidirectional | Sync stock levels | diff --git a/examples/eventcatalog-openapi/eventcatalog/eventcatalog.config.js b/examples/eventcatalog-openapi/eventcatalog/eventcatalog.config.js new file mode 100644 index 00000000..896cb7cc --- /dev/null +++ b/examples/eventcatalog-openapi/eventcatalog/eventcatalog.config.js @@ -0,0 +1,28 @@ +/** @type {import('@eventcatalog/core/bin/eventcatalog.config').Config} */ +export default { + title: 'E-Commerce API Catalog', + tagline: 'API documentation for the e-commerce platform', + organizationName: 'Petstore Inc', + homepageLink: 'https://eventcatalog.dev/', + landingPage: '', + editUrl: 'https://github.com/petstore/eventcatalog/edit/main', + trailingSlash: false, + base: '/', + logo: { + alt: 'EventCatalog Logo', + src: '/logo.png', + }, + docs: { + sidebar: { + showPageHeadings: true, + }, + }, + generators: [ + // OpenAPI generator can auto-populate the catalog from spec files + // ['@eventcatalog/generator-openapi', { + // services: [ + // { path: './services/petstore-api/openapi.json' } + // ] + // }] + ], +}; diff --git a/examples/eventcatalog-openapi/eventcatalog/package.json b/examples/eventcatalog-openapi/eventcatalog/package.json new file mode 100644 index 00000000..94c45a27 --- /dev/null +++ b/examples/eventcatalog-openapi/eventcatalog/package.json @@ -0,0 +1,19 @@ +{ + "name": "ecommerce-api-eventcatalog", + "version": "1.0.0", + "description": "EventCatalog for e-commerce API services", + "private": true, + "type": "module", + "scripts": { + "dev": "eventcatalog dev", + "build": "eventcatalog build", + "preview": "eventcatalog preview", + "generate": "eventcatalog generate" + }, + "dependencies": { + "@eventcatalog/core": "^2.0.0" + }, + "devDependencies": { + "@eventcatalog/sdk": "^1.0.0" + } +} diff --git a/examples/eventcatalog-openapi/eventcatalog/services/petstore-api/index.md b/examples/eventcatalog-openapi/eventcatalog/services/petstore-api/index.md new file mode 100644 index 00000000..3a328886 --- /dev/null +++ b/examples/eventcatalog-openapi/eventcatalog/services/petstore-api/index.md @@ -0,0 +1,76 @@ +--- +id: petstore-api +name: Petstore API +version: 1.0.0 +summary: REST API for managing pets in the pet store +owners: + - api-team +badges: + - content: OpenAPI 3.0 + backgroundColor: green + textColor: white + - content: Production + backgroundColor: blue + textColor: white +sends: [] +receives: [] +repository: + language: TypeScript + url: https://github.com/petstore/api +specifications: + openapiPath: openapi.json +--- + +## Overview + +The Petstore API provides RESTful endpoints for managing pets, orders, and users in the pet store application. It's the main interface for all client applications. + +## Architecture + +```mermaid +graph LR + A[Web App] --> B[Petstore API] + C[Mobile App] --> B + B --> D[PostgreSQL] + B --> E[Redis Cache] +``` + +## Endpoints + +| Method | Path | Description | +|--------|------|-------------| +| POST | /pet | Add a new pet | +| PUT | /pet | Update an existing pet | +| GET | /pet/findByStatus/{status}/{categoryId} | Find pets by status and category | + +## Authentication + +The API supports two authentication methods: + +1. **OAuth2** - For user-facing applications +2. **API Key** - For service-to-service communication + +## Getting Started + +```bash +# Using the generated client +import { PetstoreClient } from './src/client'; + +const client = new PetstoreClient({ + baseUrl: 'https://api.petstore.com', + apiKey: 'your-api-key', +}); + +const pets = await client.findPetsByStatusAndCategory({ + status: 'available', + categoryId: 1, +}); +``` + +## Rate Limits + +| Tier | Requests/min | Requests/day | +|------|--------------|--------------| +| Free | 60 | 1,000 | +| Pro | 600 | 50,000 | +| Enterprise | Unlimited | Unlimited | diff --git a/examples/eventcatalog-openapi/eventcatalog/services/petstore-api/openapi.json b/examples/eventcatalog-openapi/eventcatalog/services/petstore-api/openapi.json new file mode 100644 index 00000000..071393c5 --- /dev/null +++ b/examples/eventcatalog-openapi/eventcatalog/services/petstore-api/openapi.json @@ -0,0 +1,467 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Petstore API", + "version": "1.0.0", + "description": "REST API for managing pets in the pet store. This API allows you to add, update, and search for pets.", + "contact": { + "name": "API Team", + "email": "api-team@petstore.com" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "https://api.petstore.com/v1", + "description": "Production server" + }, + { + "url": "http://localhost:3000/v1", + "description": "Local development server" + } + ], + "tags": [ + { + "name": "pet", + "description": "Pet management operations" + }, + { + "name": "store", + "description": "Store order operations" + } + ], + "paths": { + "/pet": { + "post": { + "tags": ["pet"], + "summary": "Add a new pet to the store", + "description": "Creates a new pet in the store inventory", + "operationId": "addPet", + "requestBody": { + "description": "Pet object to be added", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "responses": { + "201": { + "description": "Pet created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid input", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + }, + "put": { + "tags": ["pet"], + "summary": "Update an existing pet", + "description": "Updates a pet in the store by ID", + "operationId": "updatePet", + "requestBody": { + "description": "Pet object with updated information", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "responses": { + "200": { + "description": "Pet updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/pet/{petId}": { + "get": { + "tags": ["pet"], + "summary": "Find pet by ID", + "description": "Returns a single pet by its ID", + "operationId": "getPetById", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of the pet to retrieve", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + }, + "delete": { + "tags": ["pet"], + "summary": "Delete a pet", + "description": "Deletes a pet from the store", + "operationId": "deletePet", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of the pet to delete", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "204": { + "description": "Pet deleted successfully" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": ["pet"], + "summary": "Find pets by status", + "description": "Returns pets filtered by their status", + "operationId": "findPetsByStatus", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status value to filter by", + "required": true, + "schema": { + "type": "string", + "enum": ["available", "pending", "sold"] + } + }, + { + "name": "limit", + "in": "query", + "description": "Maximum number of pets to return", + "required": false, + "schema": { + "type": "integer", + "default": 20, + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": ["store"], + "summary": "Place an order for a pet", + "description": "Creates a new order in the store", + "operationId": "placeOrder", + "requestBody": { + "description": "Order to be placed", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "responses": { + "201": { + "description": "Order placed successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "400": { + "description": "Invalid order" + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order/{orderId}": { + "get": { + "tags": ["store"], + "summary": "Find order by ID", + "description": "Returns an order by its ID", + "operationId": "getOrderById", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order to retrieve", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "404": { + "description": "Order not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "components": { + "schemas": { + "Pet": { + "type": "object", + "required": ["name", "status"], + "properties": { + "id": { + "type": "integer", + "format": "int64", + "description": "Unique identifier for the pet" + }, + "name": { + "type": "string", + "description": "Name of the pet", + "example": "Buddy" + }, + "category": { + "$ref": "#/components/schemas/Category" + }, + "photoUrls": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + }, + "description": "URLs to pet photos" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Tag" + }, + "description": "Tags associated with the pet" + }, + "status": { + "type": "string", + "enum": ["available", "pending", "sold"], + "description": "Pet availability status" + } + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "example": "Dogs" + } + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "example": "friendly" + } + } + }, + "Order": { + "type": "object", + "required": ["petId", "quantity"], + "properties": { + "id": { + "type": "integer", + "format": "int64", + "description": "Unique identifier for the order" + }, + "petId": { + "type": "integer", + "format": "int64", + "description": "ID of the pet being ordered" + }, + "quantity": { + "type": "integer", + "format": "int32", + "minimum": 1, + "description": "Number of pets ordered" + }, + "shipDate": { + "type": "string", + "format": "date-time", + "description": "Expected shipping date" + }, + "status": { + "type": "string", + "enum": ["placed", "approved", "delivered"], + "description": "Order status" + }, + "complete": { + "type": "boolean", + "default": false, + "description": "Whether the order is complete" + } + } + }, + "Error": { + "type": "object", + "required": ["code", "message"], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + }, + "securitySchemes": { + "api_key": { + "type": "apiKey", + "name": "X-API-Key", + "in": "header" + }, + "oauth2": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://petstore.com/oauth/authorize", + "scopes": { + "read:pets": "Read your pets", + "write:pets": "Modify pets in your account" + } + } + } + } + } + } +} diff --git a/examples/eventcatalog-openapi/package.json b/examples/eventcatalog-openapi/package.json new file mode 100644 index 00000000..e2f9420e --- /dev/null +++ b/examples/eventcatalog-openapi/package.json @@ -0,0 +1,16 @@ +{ + "name": "eventcatalog-openapi-example", + "version": "1.0.0", + "description": "Example: Generate TypeScript HTTP client from EventCatalog OpenAPI specs", + "type": "module", + "scripts": { + "generate": "codegen generate", + "generate:watch": "codegen generate --watch", + "catalog:dev": "cd eventcatalog && npm run dev", + "catalog:build": "cd eventcatalog && npm run build" + }, + "devDependencies": { + "@the-codegen-project/cli": "latest" + }, + "dependencies": {} +} From 5c7b47c8dccd751494b28f13e5c1afed9ea326f5 Mon Sep 17 00:00:00 2001 From: jonaslagoni Date: Sun, 26 Apr 2026 22:08:41 +0200 Subject: [PATCH 2/2] feat(inputs): add eventcatalog input type Adds inputType: 'eventcatalog' so codegen can read an EventCatalog directory directly. The loader picks one service via a new required 'service' field, follows its frontmatter, and routes generation through the existing AsyncAPI or OpenAPI pipeline. Native services (no spec file, only sends/receives) get a synthesized AsyncAPI 3.0 document so every existing preset works unchanged. - New loader stack at src/codegen/inputs/eventcatalog/ - Both-specs services require explicit specType disambiguation - Browser entry point rejects 'eventcatalog' (filesystem-bound) - 25 new unit tests + runtime spec + new docs page Closes #360 --- docs/README.md | 2 + docs/configurations.md | 9 + docs/contributing.md | 1 + docs/getting-started/README.md | 2 +- docs/inputs/eventcatalog.md | 146 ++++++++++ docs/migrations/v0.md | 1 + docs/telemetry.md | 2 +- docs/usage.md | 13 +- .../eventcatalog-asyncapi/codegen.config.mjs | 11 +- .../eventcatalog-native/codegen.config.mjs | 8 +- .../eventcatalog-openapi/codegen.config.mjs | 11 +- schemas/configuration-schema-0-with-docs.json | 56 ++++ schemas/configuration-schema-0.json | 56 ++++ src/browser/generate.ts | 8 +- src/codegen/configurations.ts | 15 ++ src/codegen/errors.ts | 4 +- .../inputs/eventcatalog/eventLoader.ts | 176 ++++++++++++ src/codegen/inputs/eventcatalog/index.ts | 10 + src/codegen/inputs/eventcatalog/parser.ts | 104 +++++++ .../inputs/eventcatalog/serviceLoader.ts | 162 +++++++++++ src/codegen/inputs/eventcatalog/types.ts | 29 ++ src/codegen/inputs/index.ts | 1 + src/codegen/renderer.ts | 10 +- src/codegen/types.ts | 43 ++- src/commands/init.ts | 70 ++++- src/telemetry/events.ts | 4 +- test/codegen/configurations.spec.ts | 67 +++++ .../services/user-service/asyncapi.yaml | 27 ++ .../services/user-service/index.md | 14 + .../services/order-service/asyncapi.yaml | 19 ++ .../services/order-service/index.md | 13 + .../services/order-service/openapi.json | 19 ++ .../events/MissingSchemaEvent/index.md | 8 + .../services/broken-service/index.md | 11 + .../services/broken-service/index.md | 3 + .../eventcatalog/services/.gitkeep | 0 .../eventcatalog/events/OrderCreated/index.md | 9 + .../events/OrderCreated/schema.json | 11 + .../eventcatalog/events/OrderShipped/index.md | 9 + .../events/OrderShipped/schema.json | 10 + .../services/order-service/index.md | 14 + .../services/petstore-api/index.md | 12 + .../services/petstore-api/openapi.json | 39 +++ .../inputs/eventcatalog/eventLoader.spec.ts | 93 +++++++ .../inputs/eventcatalog/parser.spec.ts | 89 ++++++ .../inputs/eventcatalog/serviceLoader.spec.ts | 85 ++++++ .../typescript/codegen-eventcatalog.mjs | 24 ++ .../services/test-service/asyncapi.yaml | 29 ++ .../services/test-service/index.md | 12 + test/runtime/typescript/package.json | 6 +- .../src/eventcatalog/channels/index.ts | 3 + .../src/eventcatalog/channels/nats.ts | 233 ++++++++++++++++ .../src/eventcatalog/client/NatsClient.ts | 254 ++++++++++++++++++ .../eventcatalog/payloads/EventCatalogPing.ts | 74 +++++ .../test/inputs/eventcatalog.spec.ts | 43 +++ 55 files changed, 2136 insertions(+), 48 deletions(-) create mode 100644 docs/inputs/eventcatalog.md create mode 100644 src/codegen/inputs/eventcatalog/eventLoader.ts create mode 100644 src/codegen/inputs/eventcatalog/index.ts create mode 100644 src/codegen/inputs/eventcatalog/parser.ts create mode 100644 src/codegen/inputs/eventcatalog/serviceLoader.ts create mode 100644 src/codegen/inputs/eventcatalog/types.ts create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/asyncapi-service/eventcatalog/services/user-service/asyncapi.yaml create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/asyncapi-service/eventcatalog/services/user-service/index.md create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/both-specs-service/eventcatalog/services/order-service/asyncapi.yaml create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/both-specs-service/eventcatalog/services/order-service/index.md create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/both-specs-service/eventcatalog/services/order-service/openapi.json create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/invalid/missing-event-schema/eventcatalog/events/MissingSchemaEvent/index.md create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/invalid/missing-event-schema/eventcatalog/services/broken-service/index.md create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/invalid/missing-frontmatter/eventcatalog/services/broken-service/index.md create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/invalid/missing-service/eventcatalog/services/.gitkeep create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/native-service/eventcatalog/events/OrderCreated/index.md create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/native-service/eventcatalog/events/OrderCreated/schema.json create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/native-service/eventcatalog/events/OrderShipped/index.md create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/native-service/eventcatalog/events/OrderShipped/schema.json create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/native-service/eventcatalog/services/order-service/index.md create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/openapi-service/eventcatalog/services/petstore-api/index.md create mode 100644 test/codegen/inputs/eventcatalog/__fixtures__/openapi-service/eventcatalog/services/petstore-api/openapi.json create mode 100644 test/codegen/inputs/eventcatalog/eventLoader.spec.ts create mode 100644 test/codegen/inputs/eventcatalog/parser.spec.ts create mode 100644 test/codegen/inputs/eventcatalog/serviceLoader.spec.ts create mode 100644 test/runtime/typescript/codegen-eventcatalog.mjs create mode 100644 test/runtime/typescript/eventcatalog/services/test-service/asyncapi.yaml create mode 100644 test/runtime/typescript/eventcatalog/services/test-service/index.md create mode 100644 test/runtime/typescript/src/eventcatalog/channels/index.ts create mode 100644 test/runtime/typescript/src/eventcatalog/channels/nats.ts create mode 100644 test/runtime/typescript/src/eventcatalog/client/NatsClient.ts create mode 100644 test/runtime/typescript/src/eventcatalog/payloads/EventCatalogPing.ts create mode 100644 test/runtime/typescript/test/inputs/eventcatalog.spec.ts diff --git a/docs/README.md b/docs/README.md index 1a0e595e..9950332d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -47,6 +47,7 @@ Each input has its own limitations, corner cases, and features; thus, each has s - [AsyncAPI](./inputs/asyncapi.md) - [OpenAPI](./inputs/openapi.md) - [JSON Schema](./inputs/jsonschema.md) +- [EventCatalog](./inputs/eventcatalog.md) ### Protocols Each protocol has its own limitations, corner cases, and features; thus, each has separate documentation. @@ -80,3 +81,4 @@ Connect AI assistants like Claude Code, Cursor, and Windsurf to The Codegen Proj + diff --git a/docs/configurations.md b/docs/configurations.md index 2e2d4ba1..d04b0c5c 100644 --- a/docs/configurations.md +++ b/docs/configurations.md @@ -58,6 +58,15 @@ If no explicit configuration file is sat, it will be looked for in the following - codegen.mjs - codegen.cjs +## Input Types + +The `inputType` field selects which input pipeline drives generation. Supported values: + +- `'asyncapi'` — AsyncAPI 2.x / 3.x documents. See [AsyncAPI input](./inputs/asyncapi.md). +- `'openapi'` — Swagger 2.0 / OpenAPI 3.0 / 3.1 documents. See [OpenAPI input](./inputs/openapi.md). +- `'jsonschema'` — Standalone JSON Schema documents. See [JSON Schema input](./inputs/jsonschema.md). +- `'eventcatalog'` — An [EventCatalog](https://eventcatalog.dev) directory; the loader picks one service via the required `service` field and forwards generation through the AsyncAPI or OpenAPI pipeline. See [EventCatalog input](./inputs/eventcatalog.md). + ## TypeScript Configuration When generating TypeScript code, you can configure global options that apply to all generators. These options can be overridden per-generator if needed. diff --git a/docs/contributing.md b/docs/contributing.md index b3778110..b1829d41 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -196,5 +196,6 @@ Prefix that follows specification is not enough though. Remember that the title + diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index ed898735..7508bdff 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -192,6 +192,6 @@ Discover how The Codegen Project supports various messaging protocols like NATS, ### Explore Further - **[Generator Documentation](../generators/README.md)** - Detailed documentation for each generator type - **[Protocol Documentation](../protocols/)** - Complete protocol reference and implementation details -- **[Input Types](../inputs/)** - Learn about AsyncAPI, OpenAPI, and JSON Schema support +- **[Input Types](../inputs/)** - Learn about AsyncAPI, OpenAPI, JSON Schema, and EventCatalog support - **[Examples](../../examples/)** - Real-world examples and integration patterns diff --git a/docs/inputs/eventcatalog.md b/docs/inputs/eventcatalog.md new file mode 100644 index 00000000..8492b065 --- /dev/null +++ b/docs/inputs/eventcatalog.md @@ -0,0 +1,146 @@ +--- +sidebar_position: 99 +--- + +# EventCatalog + +[EventCatalog](https://eventcatalog.dev) is a documentation tool that organizes events, services, and domains in a single browsable catalog. The Codegen Project can read an EventCatalog directory directly and generate code for a chosen service — no need to maintain a separate AsyncAPI/OpenAPI file alongside the catalog. + +The loader is a translation layer: it picks the requested service, follows its `specifications` block (or its `sends`/`receives` events for native mode), and routes the run through the existing AsyncAPI or OpenAPI pipeline. As a result, every preset that works for those input types works here too. + +## Supported Generators + +| **Presets** | EventCatalog | +|---|---| +| [`payloads`](../generators/payloads.md) | ✅ | +| [`parameters`](../generators/parameters.md) | ✅ | +| [`headers`](../generators/headers.md) | ✅ | +| [`types`](../generators/types.md) | ✅ | +| [`channels`](../generators/channels.md) | ✅ | +| [`client`](../generators/client.md) | ✅ * | +| [`custom`](../generators/custom.md) | ✅ | +| [`models`](../generators/models.md) | ✅ | + +\* The `client` preset is currently only generated when the resolved spec is AsyncAPI; OpenAPI services do not produce a client wrapper. + +## Configuration + +| Field | Required | Description | +|---|---|---| +| `inputType` | yes | Must be `'eventcatalog'`. | +| `inputPath` | yes | Path (or remote URL) to the EventCatalog root — the directory that contains `services/`. | +| `service` | yes | The `id` of the service inside `services//index.md` to generate from. | +| `specType` | no | Set to `'asyncapi'` or `'openapi'` to disambiguate when a service declares both `asyncapiPath` and `openapiPath`. | +| `auth` | no | Bearer / apiKey / custom auth used when the service's spec path is a remote URL. See [configurations guide](../configurations.md#remote-url-inputs). | + +```js +export default { + inputType: 'eventcatalog', + inputPath: './eventcatalog', + service: 'user-service', + language: 'typescript', + generators: [ + { preset: 'payloads', outputPath: './src/payloads' }, + { preset: 'channels', outputPath: './src/channels', protocols: ['nats'] }, + { preset: 'client', outputPath: './src/client', protocols: ['nats'] } + ] +}; +``` + +## The three modes + +The loader picks one of three modes based on the selected service's `index.md` frontmatter. + +### 1. AsyncAPI service + +The service declares an `asyncapiPath` under `specifications`. The loader reads that file and runs the AsyncAPI pipeline. + +```yaml +--- +id: user-service +name: User Service +specifications: + asyncapiPath: asyncapi.yaml +--- +``` + +### 2. OpenAPI service + +The service declares an `openapiPath` under `specifications`. The loader reads that file and runs the OpenAPI pipeline. + +```yaml +--- +id: petstore-api +name: Petstore API +specifications: + openapiPath: openapi.json +--- +``` + +### 3. Native service (no `specifications`) + +The service has no spec file but does list `sends` / `receives` events. The loader walks each referenced event under `events//`, reads its `schema.json` (or whatever `schemaPath` is set in the event's frontmatter), and **synthesizes an AsyncAPI 3.0 document** from the result. That synthesized document then drives every downstream generator, so presets like `channels` and `client` work seamlessly without you authoring an AsyncAPI file. + +```yaml +--- +id: order-service +name: Order Service +sends: + - id: OrderCreated + version: 1.0.0 +receives: + - id: OrderShipped + version: 1.0.0 +--- +``` + +#### Native-mode synthesis rules + +For each `sends[i]` event the synthesized document contains: +- a channel keyed by the event id, with a single message whose payload is the event's schema content +- an operation `send` with `action: 'send'` referring to that channel + +Each `receives[i]` event produces the same channel/message pair but with `action: 'receive'`. The synthesized `info` block uses: +- `info.title` = `service.name` (falls back to `service.id`) +- `info.version` = `service.version` (falls back to `'1.0.0'`) +- `info.description` = `service.summary` when present + +If you need richer mappings — multi-message channels, parameters, status-code variants, etc. — author a real AsyncAPI document and reference it via `specifications.asyncapiPath` instead. + +## Both-specs services + +If a service declares **both** `asyncapiPath` and `openapiPath`, the loader needs to know which one to follow. Set `specType` on the configuration: + +```js +export default { + inputType: 'eventcatalog', + inputPath: './eventcatalog', + service: 'order-service', + specType: 'openapi', + language: 'typescript', + generators: [/* … */] +}; +``` + +If `specType` is missing the loader throws a descriptive error so the run fails early with a clear next step. Generating from both specs in a single run is **not supported** in this version (see the limitations section). The recommended workaround is to keep two separate config files — one with `specType: 'asyncapi'`, one with `specType: 'openapi'` — and run them independently. + +## Remote URLs in `asyncapiPath` / `openapiPath` + +If your service frontmatter points to a remote URL instead of a local file, the loader fetches it the same way the underlying input pipeline does. The `auth` field on your codegen configuration is passed through unchanged. See the [remote URL inputs section](../configurations.md#remote-url-inputs) of the configurations guide for the full set of auth options and security considerations. + +## Examples + +The repository ships three end-to-end examples under `examples/`: + +- `examples/eventcatalog-asyncapi/` — a service backed by a real AsyncAPI 3.0 document +- `examples/eventcatalog-openapi/` — a service backed by an OpenAPI 3.0 document +- `examples/eventcatalog-native/` — a service with no spec file, demonstrating the native-mode synthesis + +Each example contains an `eventcatalog/` directory plus a `codegen.config.mjs` that mirrors the snippets above. Running `npx codegen generate` inside any of the three produces the generated TypeScript directly. + +## Limitations + +- **Dual-spec generation in one run is not supported.** Use two configs and run them sequentially. +- **`domains/` and `flows/` are ignored.** Only the selected service plus the events it directly references are read. +- **Cross-service event references aren't resolved.** Events referenced via `receives` are loaded from the catalog's `events//` directory, not from the producing service. +- **Browser mode is not supported.** EventCatalog is filesystem-bound; the browser bundle returns a clear error when invoked with `specFormat: 'eventcatalog'`. Use AsyncAPI / OpenAPI / JSON Schema directly in the browser. diff --git a/docs/migrations/v0.md b/docs/migrations/v0.md index c24f34c0..b2e75ac8 100644 --- a/docs/migrations/v0.md +++ b/docs/migrations/v0.md @@ -168,3 +168,4 @@ interface GeneratedFile { 4. Optionally leverage the new `content` property for in-memory processing + diff --git a/docs/telemetry.md b/docs/telemetry.md index 0550dee6..c5f19372 100644 --- a/docs/telemetry.md +++ b/docs/telemetry.md @@ -245,7 +245,7 @@ Example configuration: { event: 'generator_used', generator_type: 'payloads', - input_type: 'asyncapi', // Can be: asyncapi, openapi, jsonschema + input_type: 'asyncapi', // Can be: asyncapi, openapi, jsonschema, eventcatalog input_source: 'remote_url', // Not the actual URL! language: 'typescript', options: '{"includeValidation":true,"serializationType":"json"}', diff --git a/docs/usage.md b/docs/usage.md index a35947ba..3c55cbf8 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -9,7 +9,7 @@ $ npm install -g @the-codegen-project/cli $ codegen COMMAND running command... $ codegen (--version) -@the-codegen-project/cli/0.71.0 linux-x64 node-v22.22.2 +@the-codegen-project/cli/0.71.0 win32-x64 node-v22.14.0 $ codegen --help [COMMAND] USAGE $ codegen COMMAND @@ -140,9 +140,10 @@ Initialize The Codegen Project in your project ``` USAGE $ codegen init [--json] [--no-color] [--debug | | [--silent | -v | -q]] [--help] [--input-file ] - [--config-name ] [--input-type asyncapi|openapi] [--output-directory ] [--config-type - esm|json|yaml|ts] [--languages typescript] [--no-tty] [--include-payloads] [--include-headers] [--include-client] - [--include-parameters] [--include-channels] [--gitignore-generated] + [--config-name ] [--input-type asyncapi|openapi|jsonschema|eventcatalog] [--service ] + [--output-directory ] [--config-type esm|json|yaml|ts] [--languages typescript] [--no-tty] + [--include-payloads] [--include-headers] [--include-client] [--include-parameters] [--include-channels] + [--gitignore-generated] FLAGS -q, --quiet Only show errors and warnings @@ -163,7 +164,7 @@ FLAGS --include-payloads Include payloads generation, available for TypeScript --input-file= File path for the code generation input such as AsyncAPI document --input-type=