|
| 1 | +# Plan for Improving OpenAPI Generator |
| 2 | + |
| 3 | +## Goal: |
| 4 | + |
| 5 | +To evolve the `@metal-box/openapi-generator` into a robust, production-grade tool capable of generating `metal-box/fetch` clients that fully support the OpenAPI 3.x specification, as exemplified by `e-commerse.json` and `e-commerse2.json`. |
| 6 | + |
| 7 | +## Core Principles: |
| 8 | + |
| 9 | +- **Robustness:** Handle malformed input gracefully, provide clear error messages. |
| 10 | +- **Type Safety:** Maximize TypeScript type inference and correctness in generated code. |
| 11 | +- **Extensibility:** Design components to be easily extendable for future OpenAPI features or custom requirements. |
| 12 | +- **Maintainability:** Write clean, modular, and well-tested code. |
| 13 | +- **Completeness:** Aim to support all relevant OpenAPI features for client generation. |
| 14 | + |
| 15 | +## Phases of Improvement: |
| 16 | + |
| 17 | +### Phase 1: Foundational Improvements & Robustness |
| 18 | + |
| 19 | +1. **Enhanced Error Handling in `parser.ts`:** |
| 20 | + - Wrap `readFileSync` and `JSON.parse` in `try-catch` blocks. |
| 21 | + - Provide specific, actionable error messages for file not found, permission issues, and invalid JSON. |
| 22 | + - **Example from `e-commerse.json`:** If the JSON is malformed, the current tool crashes. |
| 23 | + |
| 24 | +2. **OpenAPI Spec Validation:** |
| 25 | + - Integrate an external OpenAPI validator library (e.g., `swagger-parser` for validation and dereferencing). |
| 26 | + - Validate the input OpenAPI document against the official OpenAPI schema before processing. |
| 27 | + - **Benefit:** Prevents downstream errors from invalid or incomplete specs. |
| 28 | + |
| 29 | +3. **Centralized OpenAPI Dereferencing:** |
| 30 | + - Use a library (e.g., `swagger-parser`'s `dereference` method) to resolve all `$ref` pointers in the OpenAPI document upfront. |
| 31 | + - **Benefit:** Simplifies subsequent processing by working with a fully resolved document, avoiding repeated `$ref` resolution logic. |
| 32 | + - **Example from `e-commerse.json`:** `#/components/schemas/Product` is referenced multiple times. Dereferencing would provide the full schema directly. |
| 33 | + |
| 34 | +4. **Improved Type Safety for `parsePaths` Output:** |
| 35 | + - Replace `Record<string, any>` with a more precise recursive type definition for the `parsedPaths` object. |
| 36 | + - **Benefit:** Enhances maintainability and enables better type checking for the router structure. |
| 37 | + |
| 38 | +### Phase 2: Comprehensive Schema Generation (`mapOpenApiTypeToZod`) |
| 39 | + |
| 40 | +This phase focuses on making `mapOpenApiTypeToZod` capable of generating accurate Zod schemas for all OpenAPI schema features. |
| 41 | + |
| 42 | +1. **Polymorphism (`allOf`, `anyOf`, `oneOf`, `discriminator`):** |
| 43 | + - **`allOf`:** Map to `z.intersection()` or merge properties. |
| 44 | + - **`anyOf`:** Map to `z.union()` (or `z.discriminatedUnion()` if `discriminator` is present). |
| 45 | + - **`oneOf`:** Map to `z.union()` with additional checks for exclusivity, or `z.discriminatedUnion()`. |
| 46 | + - **`discriminator`:** Implement logic to generate `z.discriminatedUnion()` for polymorphic schemas. |
| 47 | + - **Example from `e-commerse.json`:** `Product` schema uses `discriminator` with `ElectronicsProduct` and `ClothingProduct` via `allOf`. |
| 48 | + - **Example from `e-commerse2.json`:** `PaymentMethod` uses `oneOf` with `discriminator`. |
| 49 | + |
| 50 | +2. **Enums:** |
| 51 | + - Detect `enum` keyword in schema properties. |
| 52 | + - Generate `z.enum(['value1', 'value2'])`. |
| 53 | + - **Example from `e-commerse.json`:** `category` parameter, `Product.productType`, `Order.status`. |
| 54 | + |
| 55 | +3. **Nullability:** |
| 56 | + - Check for `nullable: true` (OpenAPI 3.0) or `type: ['string', 'null']` (OpenAPI 3.1). |
| 57 | + - Generate `.nullable()` for the corresponding Zod schema. |
| 58 | + - **Example from `e-commerse2.json`:** `User.profile` is optional, but properties within it might be nullable. |
| 59 | + |
| 60 | +4. **Constraints and Validation:** |
| 61 | + - **Numeric:** Map `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`, `multipleOf` to `z.number().min()`, `.max()`, `.multipleOf()`. |
| 62 | + - **String:** Map `minLength`, `maxLength`, `pattern` to `z.string().min()`, `.max()`, `.regex()`. |
| 63 | + - **Array:** Map `minItems`, `maxItems`, `uniqueItems` to `z.array().min()`, `.max()`, `.unique()`. |
| 64 | + - **Example from `e-commerse.json`:** `price` has `minimum: 0`. `page` and `limit` parameters have `minimum`/`maximum`. `priceRange` array has `minItems`/`maxItems`. |
| 65 | + - **Example from `e-commerse2.json`:** `User.username` has `pattern`. |
| 66 | + |
| 67 | +5. **Default Values:** |
| 68 | + - Detect `default` keyword. |
| 69 | + - Generate `.default()` for the Zod schema. |
| 70 | + - **Example from `e-commerse.json`:** `page` and `limit` parameters have `default` values. |
| 71 | + |
| 72 | +6. **`readOnly` / `writeOnly`:** |
| 73 | + - Consider how to reflect these in generated types (e.g., separate input/output types, or Zod refinements). |
| 74 | + - **Example from `e-commerse2.json`:** `User.id` is `readOnly`, `User.profile.joinDate` is `readOnly`, `Product.stock` is `writeOnly`. |
| 75 | + |
| 76 | +7. **`additionalProperties`:** |
| 77 | + - Handle `additionalProperties` (boolean or schema) for object types. |
| 78 | + - **Example from `e-commerse.json`:** `sort` parameter has `additionalProperties`. `ElectronicsProduct.specs` has `additionalProperties`. |
| 79 | + |
| 80 | +8. **Recursive Schemas:** |
| 81 | + - Implement a strategy to handle schemas that reference themselves (e.g., a tree structure). This often involves `z.lazy()` or a two-pass approach. |
| 82 | + |
| 83 | +9. **Improved `$ref` Resolution for Schemas:** |
| 84 | + - Ensure robust handling of `$ref`s within schemas, including local and external references (if supported by the dereferencing step). |
| 85 | + |
| 86 | +### Phase 3: Advanced Operation & Parameter Handling (`generateBuilder`) |
| 87 | + |
| 88 | +This phase focuses on making `generateBuilder` capable of generating `metal-box/fetch` configurations for all operation-level features. |
| 89 | + |
| 90 | +1. **Full Content Type Support for Request Bodies:** |
| 91 | + - Iterate through `operation.requestBody.content` to find the most appropriate media type. |
| 92 | + - Map `multipart/form-data` to `FormData` and `def_body()` that accepts `FormData`. |
| 93 | + - Map `application/xml` or `text/plain` to appropriate `def_body()` that accepts `string` or `Blob`. |
| 94 | + - **Example from `e-commerse.json`:** `products` POST supports `application/json`, `application/xml`, `multipart/form-data`. |
| 95 | + - **Example from `e-commerse2.json`:** `products` POST uses `multipart/form-data`. |
| 96 | + |
| 97 | +2. **Comprehensive Parameter Handling:** |
| 98 | + - **Query Parameters (`in: 'query'`):** |
| 99 | + - Generate `def_searchparams()` with a Zod schema derived from the parameter schemas. |
| 100 | + - Handle `style` (e.g., `form`, `pipeDelimited`, `deepObject`) and `explode` for complex query parameters (arrays, objects). |
| 101 | + - **Example from `e-commerse.json`:** `category`, `priceRange`, `page`, `limit`, `sort` parameters. |
| 102 | + - **Example from `e-commerse2.json`:** `searchQuery`, `tags`, `page` parameters. |
| 103 | + - **Header Parameters (`in: 'header'`):** |
| 104 | + - Generate `headers` object in the `query` call or use `def_request_handler` to set headers. |
| 105 | + - **Example from `e-commerse.json`:** `userId` parameter. |
| 106 | + - **Cookie Parameters (`in: 'cookie'`):** |
| 107 | + - Similar to header parameters, but for cookies. |
| 108 | + - **Path Parameters (`in: 'path'`):** |
| 109 | + - Ensure `metal-box/fetch`'s dynamic path handling (`$id`) is correctly integrated with the generated `path` object in the `query` call. |
| 110 | + - **Example from `e-commerse.json`:** `orderId` path parameter. |
| 111 | + |
| 112 | +3. **Multiple Responses (Success and Error):** |
| 113 | + - Iterate through `operation.responses` to generate `def_response` for all successful status codes (2xx). |
| 114 | + - Consider generating specific error response types for non-2xx responses (e.g., `400`, `404`, `default`). This might involve a union type for the response or separate error handling. |
| 115 | + - **Example from `e-commerse.json`:** `products` GET has `200`, `400`, `default` responses. |
| 116 | + |
| 117 | +4. **Request Body `$ref` Resolution:** |
| 118 | + - Ensure `generateBuilder` can resolve `$ref`s pointing to `components/requestBodies` (e.g., `OrderProcessingRequest` in `e-commerse2.json`). |
| 119 | + |
| 120 | +5. **Conditional `def_json()`:** |
| 121 | + - Only add `f.builder().def_json()` if `application/json` is the primary content type for either request or response. |
| 122 | + |
| 123 | +### Phase 4: Router & API Enhancements (`generateRouter`) |
| 124 | + |
| 125 | +1. **Server Variable Resolution:** |
| 126 | + - Implement logic to resolve server variables (e.g., `{environment}`, `{version}`). |
| 127 | + - Provide options for users to specify variable values (e.g., via CLI arguments or configuration). |
| 128 | + - Default to `default` values if available. |
| 129 | + - **Example from `e-commerse.json`:** `servers` array with `environment` and `version` variables. |
| 130 | + |
| 131 | +2. **Security Scheme Integration:** |
| 132 | + - Parse `components/securitySchemes` and `security` objects at global and operation levels. |
| 133 | + - Generate `metal-box/fetch` middleware or `def_request_handler` logic to add authentication headers (e.g., `Authorization` for API Key, Basic, Bearer, OAuth2). |
| 134 | + - **Example from `e-commerse.json`:** `apiKeyAuth`, `basicAuth`, `bearerAuth`, `oauth2` schemes. |
| 135 | + |
| 136 | +3. **Callbacks & Webhooks:** |
| 137 | + - **Callbacks:** Analyze `callbacks` objects within operations. Determine if client-side generation for these is feasible or necessary (often server-side). If so, generate corresponding client-side types/interfaces. |
| 138 | + - **Webhooks:** Analyze `webhooks` objects. Generate types for webhook payloads. |
| 139 | + - **Example from `e-commerse.json`:** `productCreatedNotification` callback, `orderStatusUpdate` webhook. |
| 140 | + - **Example from `e-commerse2.json`:** `onOrderProcessed` callback, `inventoryUpdate` webhook. |
| 141 | + |
| 142 | +4. **Configurability:** |
| 143 | + - Allow users to configure `prettier` options (e.g., read from `.prettierrc`). |
| 144 | + - Add CLI options for various generation behaviors (e.g., include/exclude specific features, custom base URL overrides). |
| 145 | + |
| 146 | +### Phase 5: Testing & Documentation |
| 147 | + |
| 148 | +1. **Comprehensive Unit & Integration Tests:** |
| 149 | + - Write dedicated tests for each new feature implemented, using small, focused OpenAPI snippets. |
| 150 | + - Expand integration tests using `e-commerse.json` and `e-commerse2.json` to cover the newly supported features. |
| 151 | + - Ensure generated code passes linting and type-checking. |
| 152 | + |
| 153 | +2. **Detailed Documentation:** |
| 154 | + - Update the `README.md` with instructions on how to use the generator, supported OpenAPI features, and configuration options. |
| 155 | + - Provide examples of generated code for various OpenAPI constructs. |
| 156 | + |
| 157 | +## Tools/Libraries to Consider: |
| 158 | + |
| 159 | +- **`swagger-parser`:** For robust OpenAPI parsing, validation, and dereferencing. |
| 160 | +- **`json-schema-to-typescript`:** As a reference or for inspiration on complex schema mapping, though direct Zod generation is preferred. |
| 161 | +- **`openapi-sampler`:** For generating example payloads from schemas (useful for testing). |
| 162 | + |
| 163 | +This plan outlines a significant undertaking, but following these steps will transform the current generator into a high-quality, production-ready tool. |
0 commit comments