ResolveSpec/pkg/openapi/README.md
2025-12-09 12:01:21 +02:00

9.0 KiB

OpenAPI Generator for ResolveSpec

This package provides automatic OpenAPI 3.0 specification generation for ResolveSpec, RestheadSpec, and FuncSpec API frameworks.

Features

  • Automatic Schema Generation: Generates OpenAPI schemas from Go struct models
  • Multiple Framework Support: Works with RestheadSpec, ResolveSpec, and FuncSpec
  • Dynamic Endpoint Discovery: Automatically discovers all registered models and generates paths
  • Query Parameter Access: Access spec via ?openapi on any endpoint or via /openapi
  • Comprehensive Documentation: Includes all request/response schemas, parameters, and security schemes

Quick Start

RestheadSpec Example

import (
    "github.com/bitechdev/ResolveSpec/pkg/openapi"
    "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
    "github.com/gorilla/mux"
)

func main() {
    // 1. Create handler
    handler := restheadspec.NewHandlerWithGORM(db)

    // 2. Register models
    handler.registry.RegisterModel("public.users", User{})
    handler.registry.RegisterModel("public.products", Product{})

    // 3. Configure OpenAPI generator
    handler.SetOpenAPIGenerator(func() (string, error) {
        generator := openapi.NewGenerator(openapi.GeneratorConfig{
            Title:       "My API",
            Description: "API documentation",
            Version:     "1.0.0",
            BaseURL:     "http://localhost:8080",
            Registry:    handler.registry.(*modelregistry.DefaultModelRegistry),
            IncludeRestheadSpec: true,
            IncludeResolveSpec:  false,
            IncludeFuncSpec:     false,
        })
        return generator.GenerateJSON()
    })

    // 4. Setup routes (automatically includes /openapi endpoint)
    router := mux.NewRouter()
    restheadspec.SetupMuxRoutes(router, handler, nil)

    // Start server
    http.ListenAndServe(":8080", router)
}

ResolveSpec Example

func main() {
    // 1. Create handler
    handler := resolvespec.NewHandlerWithGORM(db)

    // 2. Register models
    handler.RegisterModel("public", "users", User{})
    handler.RegisterModel("public", "products", Product{})

    // 3. Configure OpenAPI generator
    handler.SetOpenAPIGenerator(func() (string, error) {
        generator := openapi.NewGenerator(openapi.GeneratorConfig{
            Title:       "My API",
            Version:     "1.0.0",
            Registry:    handler.registry.(*modelregistry.DefaultModelRegistry),
            IncludeResolveSpec: true,
        })
        return generator.GenerateJSON()
    })

    // 4. Setup routes
    router := mux.NewRouter()
    resolvespec.SetupMuxRoutes(router, handler, nil)

    http.ListenAndServe(":8080", router)
}

Accessing the OpenAPI Specification

Once configured, the OpenAPI spec is available in two ways:

1. Global /openapi Endpoint

curl http://localhost:8080/openapi

Returns the complete OpenAPI specification for all registered models.

2. Query Parameter on Any Endpoint

# RestheadSpec
curl http://localhost:8080/public/users?openapi

# ResolveSpec
curl http://localhost:8080/resolve/public/users?openapi

Returns the same OpenAPI specification as /openapi.

Generated Endpoints

RestheadSpec

For each registered model (e.g., public.users), the following paths are generated:

  • GET /public/users - List records with header-based filtering
  • POST /public/users - Create a new record
  • GET /public/users/{id} - Get a single record
  • PUT /public/users/{id} - Update a record
  • PATCH /public/users/{id} - Partially update a record
  • DELETE /public/users/{id} - Delete a record
  • GET /public/users/metadata - Get table metadata
  • OPTIONS /public/users - CORS preflight

ResolveSpec

For each registered model (e.g., public.users), the following paths are generated:

  • POST /resolve/public/users - Execute operations (read, create, meta)
  • POST /resolve/public/users/{id} - Execute operations (update, delete)
  • GET /resolve/public/users - Get metadata
  • OPTIONS /resolve/public/users - CORS preflight

Schema Generation

The generator automatically extracts information from your Go struct tags:

type User struct {
    ID        int       `json:"id" gorm:"primaryKey" description:"User ID"`
    Name      string    `json:"name" gorm:"not null" description:"User's full name"`
    Email     string    `json:"email" gorm:"unique" description:"Email address"`
    CreatedAt time.Time `json:"created_at" description:"Creation timestamp"`
    Roles     []string  `json:"roles" description:"User roles"`
}

This generates an OpenAPI schema with:

  • Property names from json tags
  • Required fields from gorm:"not null" and non-pointer types
  • Descriptions from description tags
  • Proper type mappings (int → integer, time.Time → string with format: date-time, etc.)

RestheadSpec Headers

The generator documents all RestheadSpec HTTP headers:

  • X-Filters - JSON array of filter conditions
  • X-Columns - Comma-separated columns to select
  • X-Sort - JSON array of sort specifications
  • X-Limit - Maximum records to return
  • X-Offset - Records to skip
  • X-Preload - Relations to eager load
  • X-Expand - Relations to expand (LEFT JOIN)
  • X-Distinct - Enable DISTINCT queries
  • X-Response-Format - Response format (detail, simple, syncfusion)
  • X-Clean-JSON - Remove null/empty fields
  • X-Custom-SQL-Where - Custom WHERE clause (AND)
  • X-Custom-SQL-Or - Custom WHERE clause (OR)

ResolveSpec Request Body

The generator documents the ResolveSpec request body structure:

{
  "operation": "read",
  "data": {},
  "id": 123,
  "options": {
    "limit": 10,
    "offset": 0,
    "filters": [
      {"column": "status", "operator": "eq", "value": "active"}
    ],
    "sort": [
      {"column": "created_at", "direction": "desc"}
    ]
  }
}

Security Schemes

The generator automatically includes common security schemes:

  • BearerAuth: JWT Bearer token authentication
  • SessionToken: Session token in Authorization header
  • CookieAuth: Cookie-based session authentication
  • HeaderAuth: Header-based user authentication (X-User-ID)

FuncSpec Custom Endpoints

For FuncSpec, you can manually register custom SQL endpoints:

funcSpecEndpoints := map[string]openapi.FuncSpecEndpoint{
    "/api/reports/sales": {
        Path:        "/api/reports/sales",
        Method:      "GET",
        Summary:     "Get sales report",
        Description: "Returns sales data for specified date range",
        SQLQuery:    "SELECT * FROM sales WHERE date BETWEEN [start_date] AND [end_date]",
        Parameters:  []string{"start_date", "end_date"},
    },
}

generator := openapi.NewGenerator(openapi.GeneratorConfig{
    // ... other config
    IncludeFuncSpec:   true,
    FuncSpecEndpoints: funcSpecEndpoints,
})

Combining Multiple Frameworks

You can generate a unified OpenAPI spec that includes multiple frameworks:

generator := openapi.NewGenerator(openapi.GeneratorConfig{
    Title:       "Unified API",
    Version:     "1.0.0",
    Registry:    sharedRegistry,
    IncludeRestheadSpec: true,
    IncludeResolveSpec:  true,
    IncludeFuncSpec:     true,
    FuncSpecEndpoints:   funcSpecEndpoints,
})

This will generate a complete spec with all endpoints from all frameworks.

Advanced Customization

You can customize the generated spec further:

handler.SetOpenAPIGenerator(func() (string, error) {
    generator := openapi.NewGenerator(config)

    // Generate initial spec
    spec, err := generator.Generate()
    if err != nil {
        return "", err
    }

    // Add contact information
    spec.Info.Contact = &openapi.Contact{
        Name:  "API Support",
        Email: "support@example.com",
        URL:   "https://example.com/support",
    }

    // Add additional servers
    spec.Servers = append(spec.Servers, openapi.Server{
        URL:         "https://staging.example.com",
        Description: "Staging Server",
    })

    // Convert back to JSON
    data, _ := json.MarshalIndent(spec, "", "  ")
    return string(data), nil
})

Using with Swagger UI

You can serve the generated OpenAPI spec with Swagger UI:

  1. Get the spec from /openapi
  2. Load it in Swagger UI at https://petstore.swagger.io/
  3. Or self-host Swagger UI and point it to your /openapi endpoint

Example with self-hosted Swagger UI:

// Serve Swagger UI static files
router.PathPrefix("/swagger/").Handler(
    http.StripPrefix("/swagger/", http.FileServer(http.Dir("./swagger-ui"))),
)

// Configure Swagger UI to use /openapi

Testing

You can test the OpenAPI endpoint:

# Get the full spec
curl http://localhost:8080/openapi | jq

# Validate with openapi-generator
openapi-generator validate -i http://localhost:8080/openapi

# Generate client SDKs
openapi-generator generate -i http://localhost:8080/openapi -g typescript-fetch -o ./client

Complete Example

See example.go in this package for complete, runnable examples including:

  • Basic RestheadSpec setup
  • Basic ResolveSpec setup
  • Combining both frameworks
  • Adding FuncSpec endpoints
  • Advanced customization

License

Part of the ResolveSpec project.