Skip to content

Conversation

@dcabib
Copy link

@dcabib dcabib commented Oct 1, 2025

GitHub Issue #1473 Comment - OpenAPI Compliance Discussion

Hey SAM CLI team! 👋

I've implemented sam generate openapi for issue #1473 - it's working and tested (94% coverage, 5,956 tests passing, verified on complex production templates). But I need architectural guidance on 5 OpenAPI compliance decisions before opening the PR.

What Works Today

Command successfully extracts OpenAPI from SAM templates via SAM Translator:

sam generate openapi -t template.yaml --openapi-version 3.0 -o api.yaml

Tested on production template: 35 API routes, 6 Lambda functions, Cognito authorization - all generated correctly with proper CORS and security configuration.

The 5 Compliance Gaps (Need Community Direction)

1. CloudFormation Intrinsic Functions

Current Output:

info:
  title: {Ref: AWS::StackName}
paths:
  /users:
    get:
      x-amazon-apigateway-integration:
        uri: {Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${UserFunction.Arn}/invocations}
security:
  - CognitoAuth:
      providerARNs: [{Ref: UserPoolArn}]

OpenAPI Standard: Plain resolved values

info:
  title: "My API"
paths:
  /users:
    get:
      # No CloudFormation syntax

Why It Happens: SAM Translator preserves template structure - doesn't resolve CloudFormation parameters or functions.

The Problem: CloudFormation refs make the spec less usable for swagger-codegen and other OpenAPI tooling.

Options:

  • A) Keep CloudFormation refs as-is (shows template structure, matches SAM deployment spec)
  • B) Resolve all refs using --parameter-overrides (cleaner output, but requires all parameters be provided)
  • C) Add --resolve-refs flag for user choice (default: keep refs)

My Recommendation: Option C - Default to raw output (accurate to SAM), add flag for resolved version if needed.


2. AWS-Specific Extensions

Current Output:

paths:
  /users/{userId}:
    get:
      x-amazon-apigateway-integration:
        httpMethod: POST
        type: aws_proxy
        uri: {Fn::Sub: arn:aws:apigateway:...}
      x-amazon-apigateway-auth:
        type: cognito_user_pools

x-amazon-apigateway-gateway-responses:
  DEFAULT_4XX:
    responseParameters:
      gatewayresponse.header.Access-Control-Allow-Origin: "'*'"

OpenAPI Standard: No vendor-specific extensions

Why It Happens: These ARE the actual API Gateway deployment configurations that SAM generates. They show exactly how the API will be deployed.

The Problem: Pure OpenAPI specs shouldn't have vendor extensions.

Options:

  • A) Keep AWS extensions (shows real deployment details, valuable for infrastructure docs)
  • B) Strip all x-amazon-apigateway-* fields (cleaner for generic tooling)
  • C) Add --strip-aws-extensions flag for choice

My Recommendation: Option A - Keep extensions. Reasons:

  1. They're accurate and valuable for understanding deployment
  2. Most OpenAPI tools (including swagger-codegen) ignore unknown extensions
  3. Removing them loses important configuration information
  4. If someone needs pure OpenAPI, they can post-process

3. Swagger 2.0 vs OpenAPI 3.0

Current Output: Swagger 2.0 format (what SAM Translator generates)

swagger: '2.0'
securityDefinitions:  # Swagger 2.0 location
  CognitoAuth: {...}

Why: SAM Translator hasn't migrated to OpenAPI 3.0 yet - still outputs Swagger 2.0.

Current Implementation: Added --openapi-version flag

  • --openapi-version 2.0: Raw SAM Translator output (Swagger 2.0)
  • --openapi-version 3.0: Auto-converts to OpenAPI 3.0 (moves securityDefinitions → components.securitySchemes)

Default: OpenAPI 3.0 (modern standard)

Question: Is defaulting to 3.0 correct, or should we default to 2.0 (matches SAM Translator output)?

My Recommendation: Keep default as 3.0 - Most tooling prefers OpenAPI 3.0. Users can specify 2.0 if needed.


4. Missing servers Section

Current Output: No servers array

OpenAPI 3.0 Requirement:

servers:
  - url: https://api.example.com/v1
    description: Production API

Why Missing: API Gateway assigns URL at deployment time. Pre-deployment, the actual URL doesn't exist yet.

The Problem:

  • OpenAPI 3.0 spec technically requires servers section
  • But we can't know the URL from just the SAM template
  • URL format: https://{apiId}.execute-api.{region}.amazonaws.com/{stage}
  • Where {apiId} is assigned by CloudFormation at deploy time

Options:

  • A) Omit entirely (current) - Spec is technically invalid per OpenAPI 3.0
  • B) Add placeholder: https://example.com/REPLACE_AFTER_DEPLOYMENT
  • C) Add template URL: https://{apiId}.execute-api.{region}.amazonaws.com/{stage} with note
  • D) Only include if user provides --api-url parameter

My Recommendation: Option C - Add template URL with CloudFormation-style variables. Shows the structure, users can substitute {apiId} and {stage} after deployment. Example:

servers:
  - url: "https://{apiId}.execute-api.{region}.amazonaws.com/{stage}"
    description: API Gateway endpoint (replace {apiId} and {stage} after deployment)
    variables:
      apiId: {description: "API Gateway REST API ID", default: "REPLACE_ME"}
      stage: {description: "Deployment stage", default: "dev"}

5. Missing Request/Response Schemas

Current Output:

paths:
  /users/{userId}:
    get:
      responses: {}  # Completely empty!
    post:
      responses: {}  # No request body schema either

OpenAPI Standard: Should define detailed schemas

paths:
  /users/{userId}:
    get:
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
            pattern: '^[0-9]+$'
      responses:
        200:
          description: User found
          content:
            application/json:
              schema:
                type: object
                properties:
                  userId: {type: string}
                  name: {type: string}
                  email: {type: string, format: email}
        404:
          description: User not found
    post:
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name: {type: string}

Why Missing: SAM templates define ROUTES but not DATA MODELS. Example SAM template:

Resources:
  UserFunction:
    Type: AWS::Serverless::Function
    Properties:
      Events:
        CreateUser:
          Type: Api
          Properties:
            Path: /users
            Method: post
            # NO RequestModel or ResponseModel defined!

SAM supports RequestModel/ResponseModel, but most templates don't use them.

The Real Problem: This is a SAM template limitation, not a command limitation. If schemas aren't in the template, we can't generate them.

Options:

  • A) Leave empty (current) - Accurate to template, documents limitation
  • B) Generate generic placeholder schemas (type: object)
  • C) Require users to add RequestModel/ResponseModel to SAM template if they want schemas
  • D) Add --infer-schemas flag to attempt inference from Lambda function type hints (complex, fragile)

My Recommendation: Option A - Leave empty and document. Reasons:

  1. Being accurate to template is better than making up schemas
  2. Users needing schemas should define models in SAM template
  3. swagger-codegen works fine without schemas (generates generic types)
  4. Option D (inference) is scope creep and error-prone

Summary of Recommendations

Gap Proposed Solution Reasoning
CloudFormation refs Keep, add --resolve-refs flag later if needed Accuracy first, convenience optional
AWS extensions Keep all extensions Valuable config info, ignored by tools anyway
Swagger vs OpenAPI Default to 3.0, flag for 2.0 ✅ Modern standard preferred
servers section Add template URL with variables Shows structure, user substitutes after deploy
Schemas Leave empty, document Can't generate what's not in template

The Trade-Off

Current approach: Generates "deployment-faithful" OpenAPI

  • Shows what SAM actually creates
  • Includes AWS config
  • Has CloudFormation template structure

Alternative: "tooling-friendly" OpenAPI

  • Resolved values
  • No AWS stuff
  • Pure standard compliance
  • Less accurate to actual deployment

Question: Which philosophy should SAM CLI adopt? The original issue says "use swagger-codegen" but doesn't specify which approach.

What I'm Asking

  1. Agree/disagree with the 5 recommendations above?
  2. Should we do this iteratively? (MVP now, enhancements based on feedback)
  3. Any other compliance concerns I missed?

Branch is ready: https://github.com/dcabib/aws-sam-cli/tree/feat/generate-openapi-command
(Blocked by AWS security hook on unrelated file, but code is committed locally)

Happy to adjust based on team direction! 🚀

Implements sam generate openapi command to extract OpenAPI/Swagger specs from SAM templates.

Features:
- Generates OpenAPI from SAM templates using SAM Translator
- Supports both Swagger 2.0 and OpenAPI 3.0 output (--openapi-version flag)
- YAML and JSON output formats
- Auto-detects single API or accepts --api-logical-id for multiple APIs
- Outputs to file or stdout
- Works with complex templates (tested: 35+ routes, 6 functions)

Implementation:
- Command: samcli/commands/generate/openapi/
- Library: samcli/lib/generate/openapi_generator.py
- Converter: samcli/lib/generate/openapi_converter.py (Swagger 2.0 → OpenAPI 3.0)
- Tests: 81 comprehensive unit tests + integration tests

Test Coverage: 94.01% (exceeds 94% threshold)
Tests Passing: 5,956 tests

Addresses: aws#1473
@dcabib dcabib requested a review from a team as a code owner October 1, 2025 23:14
@github-actions github-actions bot added pr/external stage/needs-triage Automatically applied to new issues and PRs, indicating they haven't been looked at. labels Oct 1, 2025
@dcabib
Copy link
Author

dcabib commented Oct 8, 2025

Which issue(s) does this change fix?

Fixes #1473

Why is this change necessary?

SAM CLI currently generates OpenAPI/Swagger documents automatically at deploy time, but there's no way to access this generated document during the build process. This creates challenges for development teams who want to:

  • Generate API client code using swagger-codegen or OpenAPI Generator
  • Create API documentation during CI/CD pipelines
  • Integrate with other OpenAPI-based tooling and workflows
  • Validate API contracts before deployment

Currently, teams must hand-manage OpenAPI documents separately instead of leveraging SAM's automatic generation capabilities, leading to:

  • Duplicated effort maintaining both SAM templates and OpenAPI specs
  • Potential inconsistencies between deployed APIs and documentation
  • Inability to use modern API development workflows that rely on OpenAPI specs

This feature has been requested by the community for over 6 years with strong support from multiple users.

How does it address the issue?

This PR implements a new sam generate openapi command that extracts OpenAPI specifications from SAM templates using the existing SAM Translator library. The implementation includes:

Command Structure

  • Adds new generate command group to support future artifact generation
  • Implements openapi subcommand with comprehensive options
  • Usage: sam generate openapi -t template.yaml --openapi-version 3.0 -o api.yaml

Key Features

  1. Automatic API Detection: Auto-detects single API resources in templates
  2. Multi-API Support: Supports --api-logical-id flag for templates with multiple APIs
  3. Format Options: Outputs YAML (default) or JSON via --format flag
  4. Version Support: Generates OpenAPI 3.0 (default) or Swagger 2.0 via --openapi-version flag
  5. CloudFormation Integration: Preserves CloudFormation intrinsic functions and parameter references
  6. AWS Extensions: Maintains x-amazon-apigateway-* extensions showing actual deployment configuration
  7. Flexible Output: Supports stdout or file output via --output-file flag

Architecture

  • Core generation logic in samcli/lib/generate/openapi_generator.py using SAM Translator
  • Swagger 2.0 to OpenAPI 3.0 conversion in samcli/lib/generate/openapi_converter.py
  • CLI command implementation following SAM CLI patterns with context managers
  • Comprehensive exception handling with user-friendly error messages

Implementation Philosophy

The command generates "deployment-faithful" OpenAPI documents that accurately reflect what SAM creates at deploy time:

  • Preserves CloudFormation syntax (can be resolved with --parameter-overrides)
  • Includes AWS-specific extensions (valuable for infrastructure documentation, ignored by most tools)
  • Defaults to modern OpenAPI 3.0 standard
  • Leaves schemas empty if not defined in template (accurate vs. synthetic)

What side effects does this change have?

Positive Side Effects

  • Enables modern API-first development workflows with SAM
  • Reduces maintenance burden by avoiding duplicate OpenAPI specs
  • Improves API documentation accuracy by generating directly from source templates
  • Enables integration with ecosystem tools (swagger-codegen, OpenAPI Generator, etc.)

Potential Considerations

  1. CloudFormation References: Generated OpenAPI contains CloudFormation intrinsic functions (e.g., {Ref: AWS::StackName}). This is intentional for accuracy but may require post-processing for some tooling. Can be resolved with --parameter-overrides.

  2. AWS Extensions: Output includes x-amazon-apigateway-* extensions. These are ignored by most OpenAPI tools but provide valuable deployment configuration details.

  3. Missing Schemas: If SAM templates don't define RequestModel/ResponseModel, the generated OpenAPI will have empty response objects. This is accurate to the template but may require users to enhance their SAM templates for complete documentation.

  4. No Breaking Changes: This is a purely additive feature with no changes to existing SAM CLI functionality.

Mandatory Checklist

  • Add input/output type hints to new functions/methods: All new functions have complete type hints (str, Optional, Dict, etc.)

  • Write design document if needed: Not required - straightforward feature addition following existing SAM CLI patterns. Leverages existing SAM Translator library.

  • Write/update unit tests:

    • Complete unit test coverage for all modules
    • Tests for command group, OpenAPI command, context, exceptions
    • Tests for OpenAPI generator and converter logic
    • Coverage: 94.05% (meets 94% requirement)
  • Write/update integration tests:

    • Integration tests for command execution
    • Tests with sample SAM templates
    • Tests for multiple scenarios (single API, multiple APIs, error cases)
    • Located in tests/integration/generate/openapi/
  • Write/update functional tests if needed: Integration tests serve as functional tests for this feature

  • make pr passes:

    • ✅ All 5,957 tests pass
    • ✅ Coverage requirement met (94.05%)
    • ✅ Black formatting applied
    • ✅ Type hints added
  • make update-reproducible-reqs if dependencies were changed: No new dependencies added - uses existing SAM Translator and Click libraries

  • Write documentation:

    • Comprehensive docstrings for all modules, classes, and functions
    • Command help text and descriptions
    • Usage examples in command help
    • Additional documentation can be added to official docs upon approval

Test Results

Required test coverage of 94% reached. Total coverage: 94.05%
====================== 5957 passed, 21 skipped in 52.31s =======================

Testing on Production Templates

Successfully tested on complex production template with:

  • 35 API routes
  • 6 Lambda functions
  • Cognito authorization
  • CORS configuration
  • All generated correctly with proper security and integration settings

Open Questions for Maintainers

While the implementation is complete and tested, I'd appreciate guidance on these architectural decisions before finalizing:

  1. CloudFormation References: Current approach preserves refs for accuracy. Should we add --resolve-refs flag in future PR?
  2. Default OpenAPI Version: Currently defaults to 3.0 (modern standard). Should we default to 2.0 (matches SAM Translator output)?
  3. servers Section: Currently omitted (URL unknown pre-deployment). Should we add template URL with variables?

Happy to adjust based on team preferences. The current implementation follows a "deployment-faithful" philosophy but can be enhanced iteratively based on user feedback.


Example Usage

# Basic usage - output to stdout
sam generate openapi -t template.yaml

# Save to file with specific format
sam generate openapi -t template.yaml -o api.yaml --format yaml

# Generate Swagger 2.0 instead of OpenAPI 3.0
sam generate openapi -t template.yaml --openapi-version 2.0

# Specify API for templates with multiple APIs
sam generate openapi -t template.yaml --api-logical-id MyApi

# JSON output to stdout
sam generate openapi -t template.yaml --format json

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr/external stage/needs-triage Automatically applied to new issues and PRs, indicating they haven't been looked at.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant