Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
75f0225
fix(gen): resolve operation security from its scheme and the document
xseman Oct 25, 2026
1e7043d
fix(parser): keep the model name of an array model's $ref items
xseman Oct 25, 2026
1195394
feat(go-fetch): add a Go client generator in the shape of typescript-…
xseman Oct 25, 2026
08e7446
fix(parser): read OpenAPI 3.1 null, const and binary schemas as upstream
xseman Oct 25, 2026
e1bb0b6
feat(cli): take comma-separated pairs in -p, as upstream does
xseman Oct 25, 2026
056b7ab
feat(go-fetch): extra keys, other 2xx bodies and nullable results
xseman Oct 25, 2026
7447801
test(go-fetch): cover parameter styles, other bodies and model shapes
xseman Oct 25, 2026
b4c83ea
fix(parser): keep parameters that share a name once camelCased
xseman Oct 25, 2026
45a77de
fix(go-fetch): read naive date-times and enums of mixed or null values
xseman Oct 25, 2026
df160ff
fix(go-fetch): send a form that holds a file as multipart
xseman Oct 25, 2026
0b33a2c
fix(go-fetch): keep the type of an enum that lists null
xseman Oct 25, 2026
b8bca64
fix(parser): read a typeless Swagger 2.0 definition as any
xseman Oct 25, 2026
f461b43
fix(go-fetch): send a binary property of a JSON body as its text
xseman Oct 25, 2026
7ec1a2a
fix(go-fetch): keep null items and results, other 2xx inline bodies
xseman Oct 25, 2026
dbbd37f
fix(parser): load a Swagger 2.0 spec written in YAML
xseman Oct 25, 2026
4d1e048
feat(go-fetch): stream JSON Lines and server-sent events
xseman Oct 25, 2026
5b930d7
fix(go-fetch): send null for a required nullable body
xseman Oct 25, 2026
ae46c0e
fix(parser): make a union nullable when a member takes null
xseman Oct 25, 2026
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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ go.yaml.in/yaml/v3. No cgo.
| `internal/parser` | kin-openapi document → `codegen` structs: models, operations, parameters |
| `internal/codegen` | the `Codegen*` structs templates render; fields mirror the Java originals |
| `internal/gen` | the pipeline: load spec → parse → render models, apis, supporting files |
| `internal/generator` | `CodegenConfig` and the language configs, `typescript/` and `dart/` |
| `internal/generator` | `CodegenConfig` and the language configs, `typescript/`, `dart/`, `golang/` |
| `internal/template` | the Mustache engine: partials, lambdas, struct-to-map via json |
| `internal/config` | generator options and their config-file keys |
| `internal/update` | release check, verified download, self-update |
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help test cover cover-html lint fmt vet build install clean tidy update samples-dart-fetch regress all
.PHONY: help test cover cover-html lint fmt vet build install clean tidy update samples-dart-fetch samples-go-fetch regress all

GOFUMPT ?= go run mvdan.cc/gofumpt@v0.12.0
GOLANGCI ?= go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.0
Expand Down Expand Up @@ -72,6 +72,12 @@ samples-dart-fetch: build
@rm -rf samples/dart-fetch/petstore-expanded
$(OUTPUT_DIR)/openapi-generator generate -c samples/dart-fetch/config.yaml

## samples-go-fetch: Regenerate the go-fetch petstore sample
samples-go-fetch: build
@echo "Generating go-fetch petstore sample..."
@rm -rf samples/go-fetch/petstore-expanded
$(OUTPUT_DIR)/openapi-generator generate -c samples/go-fetch/config.yaml

## regress: Diff generated output against a git ref (default HEAD): make regress REF=master
regress:
./scripts/regress.sh $(REF)
Expand Down
28 changes: 17 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ minimal dependencies.
- Fast generation times without JVM overhead
- Load specs from local files or remote URLs
- Command-line interface compatible with the original OpenAPI Generator
- Supports OpenAPI 3.x and Swagger 2.0 specifications (auto-converts 2.0 -> 3.x)
- Supports OpenAPI 3.x and Swagger 2.0 specifications, JSON or YAML
(auto-converts 2.0 -> 3.x); a 3.1 `null` member of `anyOf`/`oneOf`, a `type`
list with `null`, a `const` and a `contentMediaType` binary string read as
their 3.0 forms, as the upstream normalizer's defaults do; 3.2's `itemSchema`
of a streamed response is accepted in a 3.1 document, as FastAPI writes it

## Installation

Expand Down Expand Up @@ -47,6 +51,7 @@ go install github.com/xseman/openapi-generator/cmd/openapi-generator@latest

- [typescript-fetch](./templates/typescript-fetch/README.md)
- [dart-fetch](./samples/dart-fetch/README.md)
- [go-fetch](./templates/go-fetch/README.md)

### Server

Expand Down Expand Up @@ -75,16 +80,16 @@ openapi-generator validate -i openapi.yaml --recommend

### `generate`

| Option | Short | Description |
| --------------------------- | ----- | ------------------------------------------------ |
| `--input-spec` | `-i` | Location of the OpenAPI spec (file or URL) |
| `--generator-name` | `-g` | Generator to use: typescript-fetch, dart-fetch |
| `--output` | `-o` | Output directory |
| `--config` | `-c` | Configuration file (JSON/YAML) |
| `--template-dir` | `-t` | Custom template directory |
| `--additional-properties` | `-p` | Key=value pairs for generator options |
| `--skip-validate-spec` | | Skip OpenAPI spec validation |
| `--verbose` | `-v` | Enable verbose output |
| Option | Short | Description |
| ------------------------- | ----- | ---------------------------------------------------- |
| `--input-spec` | `-i` | Location of the OpenAPI spec (file or URL) |
| `--generator-name` | `-g` | typescript-fetch, dart-fetch, go-fetch |
| `--output` | `-o` | Output directory |
| `--config` | `-c` | Configuration file (JSON/YAML) |
| `--template-dir` | `-t` | Custom template directory |
| `--additional-properties` | `-p` | `key=value` generator options, `a=1,b=2` or repeated |
| `--skip-validate-spec` | | Skip OpenAPI spec validation |
| `--verbose` | `-v` | Enable verbose output |

### `validate`

Expand Down Expand Up @@ -163,6 +168,7 @@ sequenceDiagram

CLI->>Parser: Load Spec (YAML/JSON)
Parser-->>Parser: Convert Swagger 2.0 to OpenAPI 3.x
Parser-->>Parser: Normalize 3.1 null and binary schemas
Parser-->>CLI: Parsed Spec

CLI->>Config: Merge CLI + File Config
Expand Down
3 changes: 3 additions & 0 deletions cmd/openapi-generator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ var listCmd = &cobra.Command{
fmt.Println("CLIENT generators:")
fmt.Println(" - typescript-fetch")
fmt.Println(" - dart-fetch")
fmt.Println(" - go-fetch")
fmt.Println()
},
}
Expand All @@ -123,6 +124,8 @@ var configHelpCmd = &cobra.Command{
gen.PrintTypeScriptFetchConfigHelp()
case "dart-fetch":
gen.PrintDartFetchConfigHelp()
case "go-fetch":
gen.PrintGoFetchConfigHelp()
default:
fmt.Printf("Unknown generator: %s\n", args[0])
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.26.0
require (
github.com/cbroglie/mustache v1.4.2
github.com/getkin/kin-openapi v0.152.0
github.com/oasdiff/yaml v0.1.1
github.com/spf13/cobra v1.10.2
go.yaml.in/yaml/v3 v3.0.4
)
Expand All @@ -14,7 +15,6 @@ require (
github.com/go-openapi/swag/jsonname v0.25.5 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/oasdiff/yaml v0.1.1 // indirect
github.com/oasdiff/yaml3 v0.0.14 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 // indirect
github.com/spf13/pflag v1.0.9 // indirect
Expand Down
14 changes: 8 additions & 6 deletions internal/codegen/parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,14 @@ type CodegenParameter struct {
IsCollectionFormatMulti bool `json:"isCollectionFormatMulti"`

// Nested
ComposedModels []string `json:"composedModels"` // member models of a union/intersection type, for imports
Items *CodegenProperty `json:"items"`
AdditionalProperties *CodegenProperty `json:"additionalProperties"`
Vars []*CodegenProperty `json:"vars"`
RequiredVars []*CodegenProperty `json:"requiredVars"`
MostInnerItems *CodegenProperty `json:"mostInnerItems"`
ComposedModels []string `json:"composedModels"` // member models of a union/intersection type, for imports
Items *CodegenProperty `json:"items"`
AdditionalProperties *CodegenProperty `json:"additionalProperties"`
Vars []*CodegenProperty `json:"vars"`
RequiredVars []*CodegenProperty `json:"requiredVars"`
MostInnerItems *CodegenProperty `json:"mostInnerItems"`
ComposedSchemas *CodegenComposedSchemas `json:"composedSchemas"`
Ref string `json:"ref"` // the $ref the parameter's schema is, as the spec writes it

// Documentation
Description string `json:"description"`
Expand Down
3 changes: 3 additions & 0 deletions internal/codegen/property.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ type CodegenProperty struct {
IsInnerEnum bool `json:"isInnerEnum"` // Inline enum
IsEnumRef bool `json:"isEnumRef"` // Reference to enum model

// Ref is the $ref the property is typed from, as the spec writes it.
Ref string `json:"ref"`

// Enum
Enum []string `json:"enum"`
EnumName string `json:"enumName"`
Expand Down
18 changes: 18 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,24 @@ func NewDartFetchConfig() *DartFetchConfig {
}
}

// GoFetchConfig holds configuration specific to the go-fetch generator.
//
// The go-fetch generator produces a Go client that imports only the
// standard library, in a single package.
type GoFetchConfig struct {
// PackageName is the Go package name of the generated files.
PackageName string `json:"packageName,omitempty"`
// WithGoMod writes a go.mod, making the output a module of its own.
WithGoMod bool `json:"withGoMod,omitempty"`
// ModuleName is the module path in go.mod (default: PackageName).
ModuleName string `json:"moduleName,omitempty"`
}

// NewGoFetchConfig creates a new GoFetchConfig with default values.
func NewGoFetchConfig() *GoFetchConfig {
return &GoFetchConfig{PackageName: "openapi"}
}

// FileNamingType represents the file naming convention.
type FileNamingType string

Expand Down
28 changes: 23 additions & 5 deletions internal/gen/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/xseman/openapi-generator/internal/config"
"github.com/xseman/openapi-generator/internal/generator"
"github.com/xseman/openapi-generator/internal/generator/dart"
"github.com/xseman/openapi-generator/internal/generator/golang"
"github.com/xseman/openapi-generator/internal/generator/typescript"
"github.com/xseman/openapi-generator/internal/template"
)
Expand All @@ -28,8 +29,8 @@ type Options struct {
InputSpec string
// OutputDir is the directory generated files are written to.
OutputDir string
// GeneratorName selects the target generator: "typescript-fetch" or
// "dart-fetch".
// GeneratorName selects the target generator: "typescript-fetch",
// "dart-fetch" or "go-fetch".
GeneratorName string
// TemplateDir, if set, overrides the embedded templates with templates
// loaded from this filesystem directory.
Expand Down Expand Up @@ -68,8 +69,10 @@ func Generate(opts Options) error {
return errors.New("generator-name is required (use -g flag or generatorName in config file)")
}

if opts.GeneratorName != "typescript-fetch" && opts.GeneratorName != "dart-fetch" {
return fmt.Errorf("unsupported generator: %s (supported: 'typescript-fetch', 'dart-fetch')", opts.GeneratorName)
switch opts.GeneratorName {
case "typescript-fetch", "dart-fetch", "go-fetch":
default:
return fmt.Errorf("unsupported generator: %s (supported: 'typescript-fetch', 'dart-fetch', 'go-fetch')", opts.GeneratorName)
}

additionalProps := parseAdditionalProperties(opts.AdditionalProperties)
Expand All @@ -82,14 +85,15 @@ func Generate(opts Options) error {
AdditionalProperties: additionalProps,
}

// Construct the generator (TypeScript or Dart) and keep both the
// Construct the generator (TypeScript, Dart or Go) and keep both the
// abstract CodegenConfig (for parser/template plumbing) and the
// concrete generator (for language-specific helpers like index-file
// generation).
var (
cg generator.CodegenConfig
tsGen *typescript.FetchGenerator
dartGen *dart.FetchGenerator
goGen *golang.FetchGenerator
)

switch opts.GeneratorName {
Expand All @@ -110,6 +114,15 @@ func Generate(opts Options) error {
dartGen.SetConfig(cfg)
dartGen.DartConfig = dartConfig
cg = dartGen

case "go-fetch":
goConfig := config.NewGoFetchConfig()
applyGoFetchAdditionalProperties(goConfig, additionalProps)

goGen = golang.NewFetchGenerator()
goGen.SetConfig(cfg)
goGen.GoConfig = goConfig
cg = goGen
}

if err := cg.ProcessOpts(); err != nil {
Expand All @@ -127,6 +140,10 @@ func Generate(opts Options) error {
case dartGen != nil:
apiPackage = dartGen.ApiPackage
modelPackage = dartGen.ModelPackage

case goGen != nil:
apiPackage = goGen.ApiPackage
modelPackage = goGen.ModelPackage
}

spec, err := assembleSpec(cg, opts)
Expand Down Expand Up @@ -156,6 +173,7 @@ func Generate(opts Options) error {
cg: cg,
tsGen: tsGen,
dartGen: dartGen,
goGen: goGen,
engine: engine,
outputDir: opts.OutputDir,
apiPackage: apiPackage,
Expand Down
53 changes: 53 additions & 0 deletions internal/gen/gofetch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package gen

import (
"fmt"

"github.com/xseman/openapi-generator/internal/config"
)

// applyGoFetchAdditionalProperties maps CLI / config additional-property
// values onto a GoFetchConfig.
func applyGoFetchAdditionalProperties(gc *config.GoFetchConfig, props map[string]any) {
if v, ok := props["packageName"].(string); ok {
gc.PackageName = v
}

if v, ok := props["withGoMod"].(bool); ok {
gc.WithGoMod = v
}

if v, ok := props["moduleName"].(string); ok {
gc.ModuleName = v
}
}

// goDeclareParams marks the operations whose Params type this API file
// declares: an operation under several tags is rendered into each tag's
// file, and its Params type must be declared in only one, the first.
func goDeclareParams(opMaps []map[string]any, declared map[string]bool) {
for _, op := range opMaps {
ext, _ := op["vendorExtensions"].(map[string]any)
name, _ := ext["x-go-params"].(string)

op["goDeclareParams"] = !declared[name]
declared[name] = true
}
}

// PrintGoFetchConfigHelp prints the go-fetch additional config options to
// stdout (used by the "config-help" CLI command).
func PrintGoFetchConfigHelp() {
fmt.Println("CONFIG OPTIONS for go-fetch:")
fmt.Println()
fmt.Println(" packageName")
fmt.Println(" Go package name of the generated files. (Default: openapi)")
fmt.Println()
fmt.Println(" withGoMod")
fmt.Println(" Write a go.mod, making the output a module of its own.")
fmt.Println(" (Default: false)")
fmt.Println()
fmt.Println(" moduleName")
fmt.Println(" Module path in go.mod. (Default: packageName)")
fmt.Println()
}
Loading
Loading