Back to catalog

openapi-contract-editor

Guides editing and creation of OpenAPI 3.0 contracts in binora-contract submodule with modular YAML structure, Redocly validation, and Django/DRF consistency checks. Auto-activates when editing contracts, implementing endpoints, or modifying serializers.

  • Category: discovered
  • Tags: github, community
  • Version: 1.0.0
  • Author: walid
  • Downloads: 0
  • Updated: 2025-11-22

Download the latest version (v1.0.0) from this page.

SKILL.md

---
name: openapi-contract-editor
description: Guides editing and creation of OpenAPI 3.0 contracts in binora-contract submodule with modular YAML structure, Redocly validation, and Django/DRF consistency checks. Auto-activates when editing contracts, implementing endpoints, or modifying serializers.
activation:
  keywords:
    - openapi
    - contract
    - schema
    - api spec
    - swagger
    - redocly
    - yaml
    - api documentation
    - binora-contract
    - rest api
  triggers:
    - binora-contract/
    - npm run lint
    - npm run bundle
    - openapi.yaml
    - openapi-index.yaml
    - $ref
---

# OpenAPI Contract Editor

🎯 **CRITICAL SKILL**: Enforces error-free OpenAPI 3.0 contract editing with modular YAML structure, Redocly validation, and Django/DRF consistency for binora-contract submodule.

**Version**: 1.0.0

**Auto-activates on**: openapi, contract, schema, endpoint, api spec, contrato, especificación, binora-contract, swagger, rest api, drf endpoint, redocly, yaml, api documentation

---

## Mission

Guide **error-free OpenAPI 3.0 contract editing** with **modular YAML structure**, **Redocly validation**, **Django/DRF consistency**, and **best practices enforcement** for binora-contract submodule.

---

## Core Principles

### 1. Modular Structure with $ref

**Rule**: Never create monolithic YAML files. Always use separate files with $ref for paths, schemas, and responses.

**❌ WRONG**: Single 5000-line YAML with everything inline

**✅ CORRECT**: Separate files (workflows.yaml, openapi-index.yaml, components/responses.yaml) with $ref linking

**Auto-check**:
- [ ] Each module file < 1000 lines
- [ ] Shared responses use $ref to components/responses.yaml
- [ ] Schemas use $ref for nested objects
- [ ] paths/ references registered in openapi-index.yaml

---

### 2. Django/DRF Consistency

**Rule**: OpenAPI schemas MUST match Django serializers, ViewSet actions, and model fields exactly.

**❌ WRONG**: Schema uses `uuid` field, but serializer returns `id`

**✅ CORRECT**: Schema field `id` matches serializer Meta.fields exactly

**Auto-check**:
- [ ] operationId matches ViewSet action (e.g., `workflows_list` → `WorkflowViewSet.list()`)
- [ ] Schema field names match serializer fields
- [ ] Required fields match serializer required validation
- [ ] Enum values match Django model choices
- [ ] Parameter names match ViewSet filterset_fields

**Verification Commands**:
```bash
# Check serializer fields
grep -A 20 "class WorkflowListSerializer" apps/processes/serializers/workflow.py

# Check ViewSet actions
grep -A 5 "def list\|@action" apps/processes/views/workflow.py

# Check model choices
grep -A 10 "class.*Choices" apps/processes/models/workflow.py
```

---

### 3. Redocly Validation

**Rule**: Always validate contracts with Redocly CLI before committing. Zero linting errors allowed.

**❌ WRONG - Skip Validation**:
```bash
# Edit contract and commit directly ❌
git add binora-contract/contract/workflows.yaml
git commit -m "Add workflows contract"  # NO VALIDATION!
```

**✅ CORRECT - Validate First**:
```bash
# Navigate to contract directory
cd binora-contract

# Run Redocly lint
npm run lint  # MUST pass with 0 errors

# Bundle to verify $ref resolution
npm run bundle  # MUST generate openapi.yaml successfully

# Then commit
git add contract/workflows.yaml contract/openapi-index.yaml
git commit -m "feat: add workflows contract - validated with Redocly"
```

**Auto-check**:
- [ ] `npm run lint` passes with 0 errors
- [ ] `npm run bundle` generates openapi.yaml successfully
- [ ] No unresolved $ref references
- [ ] YAML syntax is valid
- [ ] OpenAPI 3.0.3 specification compliance

**Redocly Rules** (from redocly.yaml):
- ✅ `no-invalid-media-type-examples: error`
- ⚠️ `no-empty-servers: off` (allowed)
- ⚠️ `info-license: off` (not required)
- ⚠️ `no-path-trailing-slash: off` (trailing slashes allowed)

---

### 4. Complete Endpoint Documentation

**Rule**: Every endpoint MUST include operationId, summary, parameters, requestBody (if applicable), all responses (200, 400, 401, 403, 404, 500), tags, and security.

**❌ WRONG - Incomplete**:
```yaml
/workflows/:
  get:
    # Missing operationId, tags, security
    responses:
      '200':
        description: OK  # ❌ Too vague
```

**✅ CORRECT - Complete**:
```yaml
/workflows/:
  get:
    operationId: workflows_list  # ✅ Matches ViewSet action
    summary: List workflows with filters and pagination  # ✅ Descriptive
    parameters:
      - name: page
        in: query
        required: false
        description: A page number within the paginated result set
        schema:
          type: integer
      - name: group
        in: query
        required: false
        description: Filter by process type group
        schema:
          type: string
          enum: [mac, maintenance]
    tags:
      - workflows  # ✅ Organized
    security:
      - jwtAuth: []  # ✅ Authentication required
    responses:
      '200':
        description: Workflows list
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaginatedWorkflowList'
            examples:
              workflowsPage:
                summary: Sample workflows list page
                value:
                  total: 2
                  limit: 10
                  current_page: 1
                  pages: 1
                  results: [...]
      '400':
        $ref: './components/responses.yaml#/responses/BadRequest'
      '401':
        $ref: './components/responses.yaml#/responses/Unauthorized'
      '403':
        $ref: './components/responses.yaml#/responses/Forbidden'
      '500':
        $ref: './components/responses.yaml#/responses/InternalServerError'
```

**Auto-check**:
- [ ] operationId present and unique
- [ ] summary describes what endpoint does
- [ ] All query/path/body parameters documented
- [ ] Responses for 200, 400, 401, 403, 500 (404 if detail endpoint)
- [ ] tags assigned for organization
- [ ] security: jwtAuth specified
- [ ] examples provided for 200 responses

---

### 5. Schema Reusability

**Rule**: Define schemas once in components/schemas and reuse with $ref. Avoid inline schema definitions.

**❌ WRONG**: Inline schemas duplicated across endpoints

**✅ CORRECT**: Define once in components/schemas, reference with $ref everywhere, use allOf for extension

**Auto-check**:
- [ ] Schemas defined in components/schemas section
- [ ] Paths reference schemas with $ref
- [ ] Use allOf for schema extension/composition
- [ ] Nested objects use $ref (not inline)
- [ ] No duplicate schema definitions

---

## Anti-Patterns

### 1. Manual Tenant_id in OpenAPI Parameters

**❌ ANTI-PATTERN**:
```yaml
/workflows/:
  get:
    parameters:
      - name: tenant_id  # ❌ FORBIDDEN - Middleware handles this
        in: query
        schema:
          type: string
```

**✅ CORRECT**:
```yaml
/workflows/:
  get:
    parameters:
      # NO tenant_id - middleware adds automatically
      - name: group
        in: query
        schema:
          type: string
    security:
      - jwtAuth: []  # ✅ JWT contains tenant context
```

**Why it matters**: Multi-tenant middleware automatically filters by tenant. Manual tenant_id parameters violate transparent isolation principle and could allow cross-tenant data access.

---

### 2. Ignoring Redocly Lint Errors

**❌ ANTI-PATTERN**:
```bash
# See errors but ignore them
npm run lint
# validating /Users/oriol/.../openapi-index.yaml...
# [1] workflows.yaml:45:7 at #/paths/~1workflows~1/get/responses/200/content/application~1json/schema
# $ref must resolve to a valid schema

# Commit anyway ❌
git commit -m "Add workflows contract"
```

**✅ CORRECT**:
```bash
npm run lint
# Fix ALL errors before committing
# [1] workflows.yaml:45:7 - Fixed $ref path

npm run lint  # 0 errors ✅
npm run bundle  # Success ✅
git commit -m "feat: add workflows contract - Redocly validated"
```

**Why it matters**: Unresolved $ref breaks API documentation generation (Swagger UI, Redoc) and CI/CD pipelines.

---

### 3. Missing HTTP Status Codes

**❌ ANTI-PATTERN**:
```yaml
/workflows/:
  post:
    responses:
      '201':
        description: Created  # ❌ Missing 400, 401, 403, 500
```

**✅ CORRECT**:
```yaml
/workflows/:
  post:
    responses:
      '201':
        description: Workflow created successfully
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowDetail'
      '400':  # ✅ Validation errors
        $ref: './components/responses.yaml#/responses/BadRequest'
      '401':  # ✅ Unauthenticated
        $ref: './components/responses.yaml#/responses/Unauthorized'
      '403':  # ✅ Unauthorized
        $ref: './components/responses.yaml#/responses/Forbidden'
      '500':  # ✅ Server error
        $ref: './components/responses.yaml#/responses/InternalServerError'
```

**Why it matters**: Frontend developers need to know all possible status codes to handle errors correctly. Missing status codes cause unhandled exceptions.

---

### 4. Inconsistent Field Naming

**❌ ANTI-PATTERN**:
```yaml
# OpenAPI uses snake_case
WorkflowListItem:
  properties:
    workflow_name:  # ❌ Serializer uses 'name'
      type: string

# Serializer uses different name
class WorkflowListSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ['id', 'name']  # ❌ Mismatch!
```

**✅ CORRECT**:
```yaml
# OpenAPI matches serializer exactly
WorkflowListItem:
  properties:
    name:  # ✅ Matches serializer
      type: string
      maxLength: 200

# Serializer
class WorkflowListSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ['id', 'name']  # ✅ Match!
```

**Why it matters**: Field name mismatches break frontend integration and cause validation errors.

---

### 5. No Examples in 200 Responses

**❌ ANTI-PATTERN**:
```yaml
/workflows/:
  get:
    responses:
      '200':
        description: Workflows list
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaginatedWorkflowList'
            # ❌ No example - developers don't know what data looks like
```

**✅ CORRECT**:
```yaml
/workflows/:
  get:
    responses:
      '200':
        description: Workflows list
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaginatedWorkflowList'
            examples:  # ✅ Real example data
              workflowsPage:
                summary: Sample workflows list page
                value:
                  total: 2
                  limit: 10
                  current_page: 1
                  pages: 1
                  next: null
                  previous: null
                  results:
                    - id: "3fa85f64-5717-4562-b3fc-2c963f66afa6"
                      url: "https://api.example.org/workflows/3fa85f64-5717-4562-b3fc-2c963f66afa6/"
                      name: "Asset Registration Standard Workflow"
                      is_published: true
                      is_predefined: true
```

**Why it matters**: Examples help developers understand response structure and speed up frontend integration.

---

## Validation Checklist

### Critical (MUST Fix Before Commit)

- [ ] **Redocly Lint**: `cd binora-contract && npm run lint` passes with 0 errors
- [ ] **Redocly Bundle**: `npm run bundle` generates openapi.yaml successfully
- [ ] **YAML Syntax**: All YAML files have valid syntax (no tabs, correct indentation)
- [ ] **$ref Resolution**: All $ref paths resolve correctly (no broken references)
- [ ] **Index Registration**: New paths registered in openapi-index.yaml with correct JSONPointer encoding (~1 for /)
- [ ] **No Tenant_id**: No manual tenant_id parameters in any endpoint
- [ ] **operationId Unique**: All operationId values are unique across the contract

### High Priority (Should Fix)

- [ ] **Django Consistency**: Field names match serializer fields exactly
- [ ] **Model Choices Match**: Enum values match Django model choices
- [ ] **Parameters Match Filters**: Query parameters match ViewSet filterset_fields
- [ ] **Required Fields**: Required schema fields match serializer required validation
- [ ] **HTTP Status Codes**: All endpoints have 200, 400, 401, 403, 500 (404 for detail endpoints)
- [ ] **Security Scheme**: All endpoints have `security: - jwtAuth: []`
- [ ] **Tags Assigned**: All endpoints have tags for organization

### Medium Priority (Nice to Have)

- [ ] **Examples Provided**: 200 responses include realistic examples
- [ ] **Descriptions Complete**: Summary and description fields are descriptive (not just "OK")
- [ ] **Schema Reuse**: Schemas use $ref instead of inline definitions
- [ ] **allOf for Extension**: Use allOf to extend base schemas (e.g., Detail extends ListItem)
- [ ] **maxLength Constraints**: String fields have maxLength matching model constraints
- [ ] **Format Specified**: Use format (uuid, date-time, email, uri) where applicable

---

## References

| File | Purpose | Lines |
|------|---------|-------|
| `binora-contract/contract/openapi-index.yaml` | Central index with all path references | 85 |
| `binora-contract/contract/assets.yaml` | Reference example (complete endpoint documentation) | 600 |
| `binora-contract/contract/hierarchy.yaml` | Reference example (nested schemas) | 800 |
| `binora-contract/contract/components/responses.yaml` | Reusable error responses | 70 |
| `binora-contract/package.json` | npm scripts (lint, bundle) | 10 |
| `binora-contract/redocly.yaml` | Redocly configuration and rules | 14 |
| `WORKFLOW_CONTRACT_IMPLEMENTATION_PLAN.md` | Complete workflow example (workflows contract) | 1652 |
| `apps/processes/serializers/workflow.py` | Serializer reference (field names, required fields) | Variable |
| `apps/processes/views/workflow.py` | ViewSet reference (operationId, actions, filters) | Variable |
| `apps/processes/models/workflow.py` | Model reference (choices, constraints) | Variable |

---

## Activation Criteria

**Keywords**: OpenAPI, contract, schema, endpoint, API spec, contrato, especificación, binora-contract, swagger, REST API, DRF endpoint, redocly, YAML, API documentation, OpenAPI 3.0

**Context triggers**:
- User mentions editing or creating OpenAPI contract
- User edits YAML files in binora-contract/ directory
- User implements new ViewSet or @action decorator in Django/DRF
- User modifies serializers and needs to update contract
- User asks about API documentation or contract validation
- User runs npm run lint or npm run bundle in binora-contract/

**Explicit invocation**: "Use openapi-contract-editor skill to guide me through updating the workflows contract"

---

## Workflow Integration

### Scenario 1: Adding New Endpoint

**User**: "I need to add a new endpoint POST /workflows/clone/"

**Skill Response**:
1. **Check Existing Implementation**:
   ```bash
   # Check if ViewSet action exists
   grep -A 10 "def clone\|@action.*clone" apps/processes/views/workflow.py
   ```

2. **Create OpenAPI Path**:
   - Open `binora-contract/contract/workflows.yaml`
   - Add path definition with complete documentation (operationId, summary, parameters, requestBody, responses, tags, security)

3. **Define Schemas**:
   - Create WorkflowCloneInput schema in components/schemas
   - Ensure response schema matches WorkflowDetail

4. **Register in Index**:
   - Add to `binora-contract/contract/openapi-index.yaml`:
     ```yaml
     /workflows/clone/:
       $ref: './workflows.yaml#/paths/~1workflows~1clone~1'
     ```

5. **Validate**:
   ```bash
   cd binora-contract
   npm run lint  # MUST pass
   npm run bundle  # MUST succeed
   ```

6. **Verify Consistency**:
   - Check WorkflowCloneInputSerializer fields match schema
   - Check operationId `workflow_clone` matches ViewSet action

---

### Scenario 2: Modifying Request/Response Schema

**User**: "I added a new field `priority` to Workflow model"

**Skill Response**:
1. **Check Serializer**:
   ```bash
   grep -A 20 "class WorkflowListSerializer\|class WorkflowDetailSerializer" apps/processes/serializers/workflow.py
   ```

2. **Update Schema**:
   - Open `binora-contract/contract/workflows.yaml`
   - Add `priority` field to WorkflowListItem and/or WorkflowDetail schema
   - Specify type, description, constraints (enum, minimum, maximum)

3. **Update Examples**:
   - Add `priority` to examples in 200 responses

4. **Validate**:
   ```bash
   cd binora-contract
   npm run lint
   npm run bundle
   ```

5. **Verify Model Constraints**:
   ```bash
   grep -A 5 "priority" apps/processes/models/workflow.py
   ```
   - Match field type, choices, max_length, default value

---

### Scenario 3: Adding Query Parameters

**User**: "I added filterset_fields = ['is_published', 'is_predefined'] to WorkflowViewSet"

**Skill Response**:
1. **Check ViewSet Filters**:
   ```bash
   grep -A 5 "filterset_fields\|filter_backends" apps/processes/views/workflow.py
   ```

2. **Add Parameters**:
   - Open `binora-contract/contract/workflows.yaml`
   - Add query parameters to GET /workflows/:
     ```yaml
     parameters:
       - name: is_published
         in: query
         required: false
         description: Filter by publication status
         schema:
           type: boolean
       - name: is_predefined
         in: query
         required: false
         description: Filter by predefined status
         schema:
           type: boolean
     ```

3. **Validate**:
   ```bash
   cd binora-contract
   npm run lint
   ```

---

### Scenario 4: Updating HTTP Status Codes

**User**: "I added validation that returns 409 Conflict if workflow name already exists"

**Skill Response**:
1. **Add 409 Response**:
   - Open `binora-contract/contract/workflows.yaml`
   - Add to POST /workflows/ responses:
     ```yaml
     '409':
       $ref: './components/responses.yaml#/responses/Conflict'
     ```

2. **Verify Conflict Response Exists**:
   - Check `binora-contract/contract/components/responses.yaml`
   - If missing, add Conflict response definition

3. **Update Examples** (Optional):
   - Add example for 409 response showing error format

4. **Validate**:
   ```bash
   cd binora-contract
   npm run lint
   ```

---

## Tools Integration

### Context7 MCP for Pattern Search

**Use Context7 to find OpenAPI patterns**:

```bash
# Find pagination schema patterns
@context7 openapi pagination schema examples

# Find $ref usage patterns
@context7 openapi $ref components schemas

# Find authentication patterns
@context7 openapi security jwt authentication

# Find parameter definitions
@context7 openapi query parameters filters
```

### Validation Commands

```bash
# Navigate to contract directory
cd binora-contract

# Redocly lint (validate syntax + OpenAPI spec)
npm run lint

# Redocly bundle (generate consolidated openapi.yaml)
npm run bundle

# Check generated bundle
ls -lh openapi.yaml

# Optionally: Open in Swagger UI for visual testing
# (requires local Swagger UI setup or use editor.swagger.io)
```

### Django Consistency Checks

```bash
# Check serializer fields
grep -A 20 "class.*Serializer" apps/processes/serializers/workflow.py

# Check ViewSet actions and filters
grep -A 10 "class.*ViewSet\|def.*\|@action" apps/processes/views/workflow.py

# Check model choices
grep -A 10 "class.*Choices" apps/processes/models/workflow.py

# Check model constraints
grep -A 5 "max_length=\|choices=\|default=" apps/processes/models/workflow.py
```

---

## Quick Tips

1. **Always start with Redocly validation**: Run `npm run lint` before and after editing
2. **Use $ref everywhere**: Avoid inline schemas, reuse with $ref
3. **Match serializer fields exactly**: Check serializer Meta.fields before defining schema
4. **Include all HTTP status codes**: 200, 400, 401, 403, 500 (+ 404 for detail endpoints)
5. **Provide realistic examples**: Help frontend developers understand response structure
6. **Validate Django consistency**: operationId matches ViewSet actions, enum matches model choices