Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions configuration-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
"server-urls": {
"type": "boolean",
"description": "Generate types for the `Server` definitions' URLs, instead of needing to provide your own values"
},
"strict-operation-list": {
"type": "boolean",
"description": "Generate a var StrictOperationIDs []string containing the normalized operation name passed to every StrictMiddlewareFunc call. Makes it straightforward to write generic tests that assert a behaviour holds for every strict operation."
}
}
},
Expand Down
5 changes: 5 additions & 0 deletions internal/test/operation-list/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# yaml-language-server: $schema=../../../configuration-schema.json
package: operationlist
generate:
strict-operation-list: true
output: operation_list.gen.go
3 changes: 3 additions & 0 deletions internal/test/operation-list/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package operationlist

//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml spec.yaml
15 changes: 15 additions & 0 deletions internal/test/operation-list/operation_list.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions internal/test/operation-list/operation_list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package operationlist

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestStrictOperationIDs(t *testing.T) {
expected := []string{"ListUsers", "CreateUser", "GetUser", "DeleteUser"}
assert.ElementsMatch(t, expected, StrictOperationIDs)
}
45 changes: 45 additions & 0 deletions internal/test/operation-list/spec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
openapi: "3.0.1"
info:
version: 1.0.0
title: Operation List Test
paths:
/users:
get:
summary: List users
operationId: listUsers
responses:
'200':
description: OK
post:
summary: Create user
operationId: createUser
responses:
'201':
description: Created
/users-alias:
$ref: "#/paths/~1users"
/users/{id}:
get:
summary: Get user
operationId: getUser
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
delete:
summary: Delete user
operationId: deleteUser
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'204':
description: No Content
34 changes: 34 additions & 0 deletions pkg/codegen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,14 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
}
}

var operationListOut string
if opts.Generate.StrictOperationList {
operationListOut, err = GenerateStrictOperationList(t, ops)
if err != nil {
return "", fmt.Errorf("error generating operation list: %w", err)
}
}

var buf bytes.Buffer
w := bufio.NewWriter(&buf)

Expand Down Expand Up @@ -522,6 +530,13 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
}
}

if opts.Generate.StrictOperationList {
_, err = w.WriteString(operationListOut)
if err != nil {
return "", fmt.Errorf("error writing operation list: %w", err)
}
}

err = w.Flush()
if err != nil {
return "", fmt.Errorf("error flushing output buffer: %w", err)
Expand Down Expand Up @@ -664,6 +679,25 @@ func GenerateConstants(t *template.Template, ops []OperationDefinition) (string,
return GenerateTemplates([]string{"constants.tmpl"}, t, constants)
}

// StrictOperationList is the data model passed to the operation-list.tmpl template.
type StrictOperationList struct {
OperationIDs []string
}

// GenerateStrictOperationList generates a slice of the normalized operation IDs
// passed to StrictMiddlewareFunc at runtime. Each value is the Go-identifier form
// of the operation (e.g. "ListUsers"), matching what the strict handler emits verbatim.
func GenerateStrictOperationList(t *template.Template, ops []OperationDefinition) (string, error) {
ids := make([]string, 0, len(ops))
for _, op := range ops {
if op.IsAlias {
continue
}
ids = append(ids, op.OperationId)
}
return GenerateTemplates([]string{"operation-list.tmpl"}, t, StrictOperationList{OperationIDs: ids})
}
Comment thread
AnthonySuper marked this conversation as resolved.

// GenerateTypesForSchemas generates type definitions for any custom types defined in the
// components/schemas section of the Swagger spec.
func GenerateTypesForSchemas(t *template.Template, schemas map[string]*openapi3.SchemaRef, excludeSchemas []string) ([]TypeDefinition, error) {
Expand Down
2 changes: 2 additions & 0 deletions pkg/codegen/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ type GenerateOptions struct {
EmbeddedSpec bool `yaml:"embedded-spec,omitempty"`
// ServerURLs generates types for the `Server` definitions' URLs, instead of needing to provide your own values
ServerURLs bool `yaml:"server-urls,omitempty"`
// StrictOperationList generates a slice of all strict operation IDs in the spec, useful for writing generic strict middleware tests
StrictOperationList bool `yaml:"strict-operation-list,omitempty"`
}

// RouterImports returns the framework-specific and strict middleware imports
Expand Down
9 changes: 9 additions & 0 deletions pkg/codegen/templates/operation-list.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// StrictOperationIDs lists the operation name string passed as the second
// argument to every StrictMiddlewareFunc call. Each entry is the normalized
// Go identifier for the operation (e.g. "ListUsers"), matching exactly what
// the strict handler passes when it invokes the middleware chain.
var StrictOperationIDs = []string{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Global name can collide

StrictOperationIDs is emitted as a fixed top-level package symbol, but it is not checked against generated type names. If a spec already has a component schema named StrictOperationIDs, code generation can emit both type StrictOperationIDs ... and var StrictOperationIDs = []string{...} in the same package. Go rejects that as a redeclaration, so enabling this option can make an otherwise valid spec fail to compile.

{{- range .OperationIDs}}
"{{.}}",
{{- end}}
}