From 0f1d405d8fc3fa8b684495aa6d244962be88507b Mon Sep 17 00:00:00 2001 From: openhands Date: Sun, 15 Mar 2026 22:00:50 +0000 Subject: [PATCH 1/8] Add openapi-to-frontend plugin This plugin generates a full TypeScript client, React component library, and frontend app from an OpenAPI specification. Features: - Phase 1: Generate TypeScript client (types, API class, auth handlers) - Phase 2: Generate React components (Form, Detail, List per schema) - Phase 3: Generate React frontend app (routing, context, pages) - Phase 4: Generate comprehensive tests (unit, integration, e2e) - Phase 5: Generate GitHub Actions CI/CD workflows Additional capabilities: - Incremental updates when spec changes (surgical edits, not regeneration) - Support for API Key, Bearer Token, and OAuth2 authentication - E2E backend strategy detection (Docker, mock server, external URL) - Verification scripts for coverage and component completeness Co-authored-by: openhands --- marketplaces/default.json | 16 + plugins/openapi-to-frontend/README.md | 252 +++++ .../openapi-to-frontend/agents/spec-differ.md | 357 +++++++ .../commands/generate-all.md | 188 ++++ plugins/openapi-to-frontend/hooks/hooks.json | 6 + .../references/auth-patterns.md | 629 ++++++++++++ .../references/change-taxonomy.md | 577 +++++++++++ .../references/naming-conventions.md | 311 ++++++ .../scripts/lint-generated.sh | 105 ++ .../scripts/parse-openapi.py | 321 ++++++ .../scripts/verify-components.py | 291 ++++++ .../scripts/verify-coverage.py | 255 +++++ .../skills/generate-ci/SKILL.md | 494 +++++++++ .../skills/generate-client/SKILL.md | 482 +++++++++ .../skills/generate-components/SKILL.md | 740 ++++++++++++++ .../skills/generate-frontend/SKILL.md | 769 ++++++++++++++ .../skills/generate-tests/SKILL.md | 946 ++++++++++++++++++ .../skills/update-from-spec/SKILL.md | 456 +++++++++ 18 files changed, 7195 insertions(+) create mode 100644 plugins/openapi-to-frontend/README.md create mode 100644 plugins/openapi-to-frontend/agents/spec-differ.md create mode 100644 plugins/openapi-to-frontend/commands/generate-all.md create mode 100644 plugins/openapi-to-frontend/hooks/hooks.json create mode 100644 plugins/openapi-to-frontend/references/auth-patterns.md create mode 100644 plugins/openapi-to-frontend/references/change-taxonomy.md create mode 100644 plugins/openapi-to-frontend/references/naming-conventions.md create mode 100755 plugins/openapi-to-frontend/scripts/lint-generated.sh create mode 100755 plugins/openapi-to-frontend/scripts/parse-openapi.py create mode 100755 plugins/openapi-to-frontend/scripts/verify-components.py create mode 100755 plugins/openapi-to-frontend/scripts/verify-coverage.py create mode 100644 plugins/openapi-to-frontend/skills/generate-ci/SKILL.md create mode 100644 plugins/openapi-to-frontend/skills/generate-client/SKILL.md create mode 100644 plugins/openapi-to-frontend/skills/generate-components/SKILL.md create mode 100644 plugins/openapi-to-frontend/skills/generate-frontend/SKILL.md create mode 100644 plugins/openapi-to-frontend/skills/generate-tests/SKILL.md create mode 100644 plugins/openapi-to-frontend/skills/update-from-spec/SKILL.md diff --git a/marketplaces/default.json b/marketplaces/default.json index 60c03b7..28c7d75 100644 --- a/marketplaces/default.json +++ b/marketplaces/default.json @@ -483,6 +483,22 @@ "verification", "sample" ] + }, + { + "name": "openapi-to-frontend", + "source": "./plugins/openapi-to-frontend", + "description": "Generate a full TypeScript client, React component library, and frontend app from an OpenAPI spec. Includes tests and CI/CD workflows.", + "category": "development", + "keywords": [ + "openapi", + "swagger", + "typescript", + "react", + "codegen", + "frontend", + "api", + "client" + ] } ] } diff --git a/plugins/openapi-to-frontend/README.md b/plugins/openapi-to-frontend/README.md new file mode 100644 index 0000000..d43ef9c --- /dev/null +++ b/plugins/openapi-to-frontend/README.md @@ -0,0 +1,252 @@ +# openapi-to-frontend + +Generate a full TypeScript client, React component library, and frontend app from an OpenAPI spec. + +## Overview + +This plugin converts an OpenAPI specification into three layers of output: + +1. **TypeScript Client** — one class per schema, one method per API endpoint +2. **React Component Library** — one component per schema, wired to the TS client +3. **React Frontend App** — a purpose-built UI inferred from the API's description +4. **Comprehensive Tests** — unit, integration, and e2e tests +5. **GitHub Actions CI/CD** — build/test on PR, deploy to GitHub Pages, publish to npm + +The plugin also handles **incremental updates**: given a change to the OpenAPI spec, it produces targeted changes rather than regenerating everything. + +## Quick Start + +Provide an OpenAPI spec (JSON or YAML) and run phases sequentially: + +``` +/generate-all path/to/openapi.yaml +``` + +Or run individual phases: + +1. **Generate client**: Creates TypeScript types and API class +2. **Generate components**: Creates React components for each schema +3. **Generate frontend**: Creates a full application shell +4. **Generate tests**: Creates unit, integration, and e2e tests +5. **Generate CI**: Creates GitHub Actions workflows + +## Plugin Contents + +``` +plugins/openapi-to-frontend/ +├── README.md # This file +├── commands/ +│ └── generate-all.md # Slash command: run all phases +├── skills/ +│ ├── generate-client/ +│ │ └── SKILL.md # Phase 1: OpenAPI → TypeScript client +│ ├── generate-components/ +│ │ └── SKILL.md # Phase 2: TS client → React components +│ ├── generate-frontend/ +│ │ └── SKILL.md # Phase 3: Components → full app +│ ├── generate-tests/ +│ │ └── SKILL.md # Phase 4: Tests +│ ├── generate-ci/ +│ │ └── SKILL.md # Phase 5: GitHub Actions +│ └── update-from-spec/ +│ └── SKILL.md # Incremental updates +├── agents/ +│ └── spec-differ.md # Subagent: diffs two spec versions +├── hooks/ +│ └── hooks.json # (reserved for future use) +├── scripts/ +│ ├── parse-openapi.py # Extract schemas, endpoints, auth from spec +│ ├── lint-generated.sh # Run eslint + tsc on generated code +│ ├── verify-coverage.py # Cross-reference spec against client +│ └── verify-components.py # Cross-reference components against client +├── references/ +│ ├── auth-patterns.md # Bearer, API key, OAuth2 handling +│ ├── naming-conventions.md # Spec→TS→React naming rules +│ └── change-taxonomy.md # Enumerated change types + handling +└── .mcp.json # (reserved for future use) +``` + +## Features + +- **Full Stack Generation** — From spec to deployable frontend in one workflow +- **Type Safety** — TypeScript throughout, with proper generics and inference +- **Auth Support** — API Key, Bearer Token, and OAuth2 flows +- **Component Library** — Reusable Form, Detail, and List components per schema +- **Tailored UIs** — Frontend design inferred from API purpose (CRUD, analytics, workflow) +- **Incremental Updates** — Surgical edits when the spec changes +- **Test Coverage** — Unit, integration, and e2e tests with mock factories +- **CI/CD Ready** — GitHub Actions for build, test, deploy, and publish + +## Prerequisites + +- Node.js 18+ with npm or yarn +- TypeScript 5+ +- React 18+ +- OpenAPI 3.0+ specification (JSON or YAML) + +## Output Structure + +After running all phases: + +``` +your-project/ +├── client/ +│ ├── types.ts # TypeScript interfaces for all schemas +│ ├── api.ts # API class with async methods +│ ├── auth.ts # Auth configuration +│ └── index.ts # Barrel export +├── components/ +│ ├── / +│ │ ├── Form.tsx +│ │ ├── Detail.tsx +│ │ ├── List.tsx +│ │ └── index.ts +│ ├── shared/ +│ │ ├── LoadingSpinner.tsx +│ │ ├── ErrorDisplay.tsx +│ │ └── Pagination.tsx +│ └── index.ts +├── app/ +│ ├── App.tsx +│ ├── pages/ +│ ├── context/ +│ ├── hooks/ +│ └── utils/ +├── tests/ +│ ├── unit/ +│ ├── integration/ +│ ├── e2e/ +│ └── setup/ +└── .github/ + └── workflows/ + ├── ci.yml + ├── deploy.yml + └── publish.yml +``` + +## Workflow Phases + +### Phase 1: Generate Client + +Creates the TypeScript API client: + +- **types.ts** — Interface for every schema in `components/schemas` +- **api.ts** — Class with async methods for every endpoint +- **auth.ts** — Auth handlers based on `securitySchemes` + +See [skills/generate-client/SKILL.md](skills/generate-client/SKILL.md) + +### Phase 2: Generate Components + +Creates React components for each schema: + +- **Form** — Create/edit with validation +- **Detail** — Read-only view +- **List** — Table with pagination + +See [skills/generate-components/SKILL.md](skills/generate-components/SKILL.md) + +### Phase 3: Generate Frontend + +Creates the application shell: + +- Routing based on API resources +- Auth context and guards +- UI tailored to API purpose (CRUD, analytics, workflow, search) + +See [skills/generate-frontend/SKILL.md](skills/generate-frontend/SKILL.md) + +### Phase 4: Generate Tests + +Creates comprehensive test coverage: + +- **Unit** — Client methods, type guards, component rendering +- **Integration** — Form→Client flows, auth context +- **E2E** — Smoke tests, CRUD flows, auth flows + +See [skills/generate-tests/SKILL.md](skills/generate-tests/SKILL.md) + +### Phase 5: Generate CI + +Creates GitHub Actions workflows: + +- **ci.yml** — Build + test on push/PR +- **deploy.yml** — Deploy to GitHub Pages +- **publish.yml** — Publish packages to npm + +See [skills/generate-ci/SKILL.md](skills/generate-ci/SKILL.md) + +## Incremental Updates + +When the OpenAPI spec changes: + +1. The spec-differ agent compares old and new specs +2. Changes are classified (new schema, removed endpoint, etc.) +3. Surgical edits are applied to existing files + +See [skills/update-from-spec/SKILL.md](skills/update-from-spec/SKILL.md) + +## Auth Patterns + +The plugin supports three auth styles: + +| Style | Client Handling | Frontend UI | +|-------|-----------------|-------------| +| API Key | Attached as header/query per spec | Settings page for key input | +| Bearer Token | `Authorization: Bearer ` | Token input or login form | +| OAuth2 | Full flow with PKCE, auto-refresh | Login/callback/logout pages | + +See [references/auth-patterns.md](references/auth-patterns.md) + +## Naming Conventions + +| OpenAPI | TypeScript | React Component | +|---------|------------|-----------------| +| `UserProfile` schema | `interface UserProfile` | `UserProfileForm`, `UserProfileDetail` | +| `GET /users/{id}` | `getUser(id: string)` | Used in `UserDetail` | +| `POST /users` | `createUser(data)` | Used in `UserForm` | +| `snake_case` field | `camelCase` property | "Title Case" label | + +See [references/naming-conventions.md](references/naming-conventions.md) + +## Scripts + +### parse-openapi.py + +```bash +python scripts/parse-openapi.py openapi.yaml > spec-summary.json +``` + +Outputs structured JSON with schemas, endpoints, and auth schemes. + +### lint-generated.sh + +```bash +./scripts/lint-generated.sh +``` + +Runs eslint and tsc on generated code. Returns non-zero on errors. + +### verify-coverage.py + +```bash +python scripts/verify-coverage.py openapi.yaml client/ +``` + +Ensures every schema and endpoint has corresponding TypeScript code. + +### verify-components.py + +```bash +python scripts/verify-components.py client/ components/ +``` + +Ensures every type has corresponding React components. + +## Contributing + +See the main [extensions repository](https://github.com/OpenHands/extensions) for contribution guidelines. + +## License + +This plugin is part of the OpenHands extensions repository. See [LICENSE](../../LICENSE) for details. diff --git a/plugins/openapi-to-frontend/agents/spec-differ.md b/plugins/openapi-to-frontend/agents/spec-differ.md new file mode 100644 index 0000000..8f1e399 --- /dev/null +++ b/plugins/openapi-to-frontend/agents/spec-differ.md @@ -0,0 +1,357 @@ +# Spec Differ Agent + +A subagent that compares two OpenAPI specification versions and produces a structured diff. + +## Purpose + +This agent analyzes the differences between two OpenAPI specs and classifies each change into a type that can be acted upon by the update-from-spec skill. + +## Invocation + +``` +@spec-differ +``` + +## Output Format + +The agent produces a JSON structure: + +```json +{ + "summary": { + "schemas_added": 2, + "schemas_removed": 0, + "schemas_modified": 3, + "endpoints_added": 4, + "endpoints_removed": 1, + "endpoints_modified": 2, + "auth_changed": false + }, + "changes": [ + { + "type": "schema_added", + "path": "components/schemas/Order", + "details": { + "name": "Order", + "fields": [ + { "name": "id", "type": "string", "required": true }, + { "name": "userId", "type": "string", "required": true }, + { "name": "items", "type": "array", "items": "OrderItem", "required": true }, + { "name": "status", "type": "OrderStatus", "required": true }, + { "name": "createdAt", "type": "string", "format": "date-time", "required": true } + ] + } + }, + { + "type": "field_added", + "path": "components/schemas/User.properties.phoneNumber", + "details": { + "schema": "User", + "field": "phoneNumber", + "type": "string", + "required": false + } + }, + { + "type": "endpoint_added", + "path": "paths./orders.get", + "details": { + "method": "GET", + "path": "/orders", + "operationId": "listOrders", + "parameters": [ + { "name": "page", "in": "query", "type": "integer" }, + { "name": "status", "in": "query", "type": "OrderStatus" } + ], + "response": "OrderListResponse" + } + } + ], + "breaking_changes": [ + { + "type": "field_removed", + "path": "components/schemas/User.properties.legacyId", + "severity": "high", + "migration": "Ensure no client code references User.legacyId before removing" + } + ] +} +``` + +## Change Types + +### Schema Changes + +| Type | Description | +|------|-------------| +| `schema_added` | New schema in components/schemas | +| `schema_removed` | Schema deleted | +| `schema_renamed` | Schema name changed (detected by field similarity) | +| `field_added` | New property added to schema | +| `field_removed` | Property removed from schema | +| `field_type_changed` | Property type changed | +| `field_required_changed` | Required status changed | + +### Endpoint Changes + +| Type | Description | +|------|-------------| +| `endpoint_added` | New path+method combination | +| `endpoint_removed` | Path+method deleted | +| `endpoint_method_changed` | HTTP method changed (rare) | +| `param_added` | New parameter (path, query, header, body) | +| `param_removed` | Parameter removed | +| `param_type_changed` | Parameter type changed | +| `response_type_changed` | Response schema changed | +| `response_code_added` | New response code | +| `response_code_removed` | Response code removed | + +### Auth Changes + +| Type | Description | +|------|-------------| +| `auth_scheme_added` | New security scheme | +| `auth_scheme_removed` | Security scheme removed | +| `auth_scheme_modified` | Scheme configuration changed | +| `security_requirement_changed` | Endpoint auth requirements changed | + +### Metadata Changes + +| Type | Description | +|------|-------------| +| `info_changed` | title, version, description changed | +| `server_added` | New server URL | +| `server_removed` | Server URL removed | +| `tag_added` | New tag | +| `tag_removed` | Tag removed | + +## Diff Algorithm + +### Step 1: Normalize Specs + +1. Parse both specs (JSON or YAML) +2. Resolve all `$ref` references +3. Sort keys for consistent comparison +4. Handle nullable and oneOf/anyOf unions + +### Step 2: Compare Schemas + +For each schema in old spec: +- If not in new spec → `schema_removed` +- If in new spec → compare fields + +For each schema in new spec: +- If not in old spec → `schema_added` + +For each field: +- Compare type, format, required, enum values + +### Step 3: Compare Endpoints + +Build a key for each endpoint: `{method} {path}` + +For each endpoint in old spec: +- If not in new spec → `endpoint_removed` +- If in new spec → compare parameters and responses + +For each endpoint in new spec: +- If not in old spec → `endpoint_added` + +### Step 4: Compare Auth + +Compare `components/securitySchemes`: +- Added/removed schemes +- Changed scheme configurations + +Compare `security` requirements on paths + +### Step 5: Identify Breaking Changes + +Flag changes that may break existing clients: + +- `schema_removed` +- `field_removed` +- `endpoint_removed` +- `param_added` (required) +- `field_type_changed` +- `auth_scheme_removed` + +## Example Usage + +### Input: old-spec.yaml + +```yaml +openapi: 3.0.0 +info: + title: My API + version: 1.0.0 +components: + schemas: + User: + type: object + required: [id, email] + properties: + id: + type: string + email: + type: string + legacyId: + type: string +paths: + /users: + get: + operationId: listUsers + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' +``` + +### Input: new-spec.yaml + +```yaml +openapi: 3.0.0 +info: + title: My API + version: 1.1.0 +components: + schemas: + User: + type: object + required: [id, email] + properties: + id: + type: string + email: + type: string + phoneNumber: + type: string + Order: + type: object + required: [id, userId] + properties: + id: + type: string + userId: + type: string +paths: + /users: + get: + operationId: listUsers + parameters: + - name: search + in: query + schema: + type: string + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + /orders: + get: + operationId: listOrders + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Order' +``` + +### Output + +```json +{ + "summary": { + "schemas_added": 1, + "schemas_removed": 0, + "schemas_modified": 1, + "endpoints_added": 1, + "endpoints_removed": 0, + "endpoints_modified": 1, + "auth_changed": false + }, + "changes": [ + { + "type": "schema_added", + "path": "components/schemas/Order", + "details": { + "name": "Order", + "fields": [ + { "name": "id", "type": "string", "required": true }, + { "name": "userId", "type": "string", "required": true } + ] + } + }, + { + "type": "field_added", + "path": "components/schemas/User.properties.phoneNumber", + "details": { + "schema": "User", + "field": "phoneNumber", + "type": "string", + "required": false + } + }, + { + "type": "field_removed", + "path": "components/schemas/User.properties.legacyId", + "details": { + "schema": "User", + "field": "legacyId", + "type": "string" + } + }, + { + "type": "param_added", + "path": "paths./users.get.parameters.search", + "details": { + "endpoint": "GET /users", + "param": "search", + "in": "query", + "type": "string", + "required": false + } + }, + { + "type": "endpoint_added", + "path": "paths./orders.get", + "details": { + "method": "GET", + "path": "/orders", + "operationId": "listOrders", + "response": "Order[]" + } + } + ], + "breaking_changes": [ + { + "type": "field_removed", + "path": "components/schemas/User.properties.legacyId", + "severity": "medium", + "migration": "Remove references to User.legacyId in generated code" + } + ] +} +``` + +## Error Handling + +- If specs are invalid YAML/JSON, report parsing errors +- If specs are not valid OpenAPI 3.x, report validation errors +- If `$ref` cannot be resolved, report reference errors + +## See Also + +- [../skills/update-from-spec/SKILL.md](../skills/update-from-spec/SKILL.md) — Uses this agent's output +- [../references/change-taxonomy.md](../references/change-taxonomy.md) — Full change type definitions diff --git a/plugins/openapi-to-frontend/commands/generate-all.md b/plugins/openapi-to-frontend/commands/generate-all.md new file mode 100644 index 0000000..08e2666 --- /dev/null +++ b/plugins/openapi-to-frontend/commands/generate-all.md @@ -0,0 +1,188 @@ +# /generate-all + +Run all generation phases end-to-end from an OpenAPI specification. + +## Usage + +``` +/generate-all [options] +``` + +## Arguments + +| Argument | Description | +|----------|-------------| +| `spec-file` | Path to OpenAPI specification (JSON or YAML) | + +## Options + +| Option | Description | +|--------|-------------| +| `--output-dir ` | Output directory (default: current directory) | +| `--skip-tests` | Skip test generation | +| `--skip-ci` | Skip CI/CD workflow generation | +| `--client-only` | Generate only the TypeScript client | +| `--dry-run` | Show what would be generated without writing files | + +## Examples + +### Full generation + +``` +/generate-all ./openapi.yaml +``` + +This will: +1. Parse the OpenAPI spec +2. Generate TypeScript client in `client/` +3. Generate React components in `components/` +4. Generate React frontend in `app/` +5. Generate tests in `tests/` +6. Generate GitHub Actions in `.github/workflows/` + +### Client only + +``` +/generate-all ./api-spec.json --client-only +``` + +### Custom output directory + +``` +/generate-all ./openapi.yaml --output-dir ./packages/generated +``` + +### Skip CI + +``` +/generate-all ./openapi.yaml --skip-ci +``` + +## Workflow + +The command runs these phases in sequence: + +### Phase 1: Parse Spec + +```bash +python scripts/parse-openapi.py > .generated/spec-summary.json +``` + +Extracts schemas, endpoints, and auth schemes into a normalized format. + +### Phase 2: Generate Client + +Uses `skills/generate-client/SKILL.md`: + +- Creates `client/types.ts` with TypeScript interfaces +- Creates `client/api.ts` with API class and methods +- Creates `client/auth.ts` with auth handlers +- Creates `client/index.ts` barrel export + +### Phase 3: Generate Components + +Uses `skills/generate-components/SKILL.md`: + +- Creates `components//` directories +- Creates Form, Detail, List components per schema +- Creates `components/shared/` utilities +- Creates barrel exports + +### Phase 4: Generate Frontend + +Uses `skills/generate-frontend/SKILL.md`: + +- Infers app type from API structure +- Creates `app/App.tsx` with routing +- Creates `app/pages/` per resource +- Creates `app/context/` for API and auth +- Creates `app/hooks/` for data fetching + +### Phase 5: Generate Tests + +Uses `skills/generate-tests/SKILL.md`: + +- Creates `tests/setup/` with config and factories +- Creates `tests/unit/` for client and components +- Creates `tests/integration/` for flows +- Creates `tests/e2e/` for Playwright + +### Phase 6: Generate CI + +Uses `skills/generate-ci/SKILL.md`: + +- Creates `.github/workflows/ci.yml` +- Creates `.github/workflows/deploy.yml` +- Creates `.github/workflows/publish.yml` + +## Verification + +After generation, the command runs: + +```bash +# Lint check +./scripts/lint-generated.sh + +# Coverage verification +python scripts/verify-coverage.py client/ +python scripts/verify-components.py client/ components/ +``` + +## Output Structure + +``` +/ +├── client/ +│ ├── types.ts +│ ├── api.ts +│ ├── auth.ts +│ └── index.ts +├── components/ +│ ├── / +│ │ ├── Form.tsx +│ │ ├── Detail.tsx +│ │ ├── List.tsx +│ │ └── index.ts +│ ├── shared/ +│ └── index.ts +├── app/ +│ ├── App.tsx +│ ├── Layout.tsx +│ ├── main.tsx +│ ├── pages/ +│ ├── context/ +│ ├── hooks/ +│ └── utils/ +├── tests/ +│ ├── setup/ +│ ├── unit/ +│ ├── integration/ +│ └── e2e/ +└── .github/ + └── workflows/ + ├── ci.yml + ├── deploy.yml + └── publish.yml +``` + +## Interactive Mode + +When the API purpose is ambiguous, the command asks for confirmation: + +> "Based on the API, this appears to be a **user management system**. I'll generate: +> - Dashboard with user list +> - User detail and edit pages +> - Role management section +> - OAuth2 login flow +> +> Does this match your expectations? [Y/n]" + +## Incremental Updates + +For updating existing generated code when the spec changes, use: + +``` +/update-from-spec +``` + +See `skills/update-from-spec/SKILL.md` for details. diff --git a/plugins/openapi-to-frontend/hooks/hooks.json b/plugins/openapi-to-frontend/hooks/hooks.json new file mode 100644 index 0000000..89cfb38 --- /dev/null +++ b/plugins/openapi-to-frontend/hooks/hooks.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://openhands.dev/schemas/hooks.json", + "version": "1.0.0", + "description": "Lifecycle hooks for openapi-to-frontend plugin (reserved for future use)", + "hooks": {} +} diff --git a/plugins/openapi-to-frontend/references/auth-patterns.md b/plugins/openapi-to-frontend/references/auth-patterns.md new file mode 100644 index 0000000..fa5ede7 --- /dev/null +++ b/plugins/openapi-to-frontend/references/auth-patterns.md @@ -0,0 +1,629 @@ +# Auth Patterns Reference + +This document describes how to handle different authentication methods found in OpenAPI specs. + +## Detecting Auth Type + +Read `components/securitySchemes` in the OpenAPI spec: + +```yaml +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X-API-Key + BearerAuth: + type: http + scheme: bearer + OAuth2: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: https://auth.example.com/authorize + tokenUrl: https://auth.example.com/token + scopes: + read: Read access + write: Write access +``` + +## Pattern 1: API Key + +### Detection + +```yaml +securitySchemes: + ApiKeyAuth: + type: apiKey + in: header # or "query" + name: X-API-Key # header/query param name +``` + +### Client Implementation + +```typescript +export interface ApiKeyAuth { + type: 'apiKey'; + key: string; + location: 'header' | 'query'; + name: string; +} + +export function attachApiKey( + headers: Record, + url: URL, + auth: ApiKeyAuth +): void { + if (auth.location === 'header') { + headers[auth.name] = auth.key; + } else { + url.searchParams.set(auth.name, auth.key); + } +} +``` + +### Client Constructor + +```typescript +const client = new ApiClient({ + baseUrl: 'https://api.example.com', + auth: { + type: 'apiKey', + key: 'your-api-key', + location: 'header', + name: 'X-API-Key', + }, +}); +``` + +### Frontend UI + +- Settings page with API key input +- Store key in localStorage +- No login flow required + +```tsx +function ApiKeySettings() { + const [key, setKey] = useState(localStorage.getItem('api_key') || ''); + + const handleSave = () => { + localStorage.setItem('api_key', key); + // Reinitialize API client + }; + + return ( +
+ + setKey(e.target.value)} + /> + +
+ ); +} +``` + +--- + +## Pattern 2: Bearer Token + +### Detection + +```yaml +securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT # optional +``` + +### Client Implementation + +```typescript +export interface BearerAuth { + type: 'bearer'; + token: string; +} + +export function attachBearer( + headers: Record, + auth: BearerAuth +): void { + headers['Authorization'] = `Bearer ${auth.token}`; +} +``` + +### Client Constructor + +```typescript +const client = new ApiClient({ + baseUrl: 'https://api.example.com', + auth: { + type: 'bearer', + token: 'your-jwt-token', + }, +}); +``` + +### Frontend UI + +Two approaches based on how tokens are obtained: + +#### Option A: Token Input + +If tokens are externally issued (e.g., service accounts): + +```tsx +function TokenSettings() { + const [token, setToken] = useState(''); + + return ( +
+ +