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
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ test:
@./go.test.sh
.PHONY: test

build:
CGO_ENABLED=0 go build -ldflags="-s -w" -o _bin/ogen ./cmd/ogen
.PHONEY: build

build-ci:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o _bin/ci/ogen ./cmd/ogen
.PHONEY: build-ci

coverage:
@./go.coverage.sh
.PHONY: coverage
Expand Down
70 changes: 70 additions & 0 deletions _testdata/positive/type_extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
openapi: 3.0.3
info:
title: API
version: 0.1.0
paths:
/optional:
get:
operationId: default
parameters:
- name: foo
in: query
schema:
type: string
x-ogen-type: github.com/ogen-go/ogen/_testdata/types/foo/decimal.Decimal
default: "1.23"
- name: bar
in: query
schema:
type: string
x-ogen-type: github.com/ogen-go/ogen/_testdata/types/bar/decimal.Decimal
default: "1.23"
responses:
'200':
description: Test
content:
application/json:
schema:
type: object
properties:
foo:
type: string
x-ogen-type: github.com/ogen-go/ogen/_testdata/types/foo/decimal.Decimal
default: "1.23"
bar:
type: string
x-ogen-type: github.com/ogen-go/ogen/_testdata/types/bar/decimal.Decimal
default: "1.23"
/required:
get:
operationId: required
parameters:
- name: foo
in: query
required: true
schema:
type: string
x-ogen-type: github.com/ogen-go/ogen/_testdata/types/foo/decimal.Decimal
- name: bar
in: query
required: true
schema:
type: string
x-ogen-type: github.com/ogen-go/ogen/_testdata/types/bar/decimal.Decimal
responses:
'200':
description: Test
content:
application/json:
schema:
type: object
required:
- foo
- bar
properties:
foo:
type: string
x-ogen-type: github.com/ogen-go/ogen/_testdata/types/foo/decimal.Decimal
bar:
type: string
x-ogen-type: github.com/ogen-go/ogen/_testdata/types/bar/decimal.Decimal
3 changes: 3 additions & 0 deletions _testdata/types/bar/decimal/decimal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package decimal

type Decimal string
3 changes: 3 additions & 0 deletions _testdata/types/foo/decimal/decimal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package decimal

type Decimal string
10 changes: 10 additions & 0 deletions gen/_template/cfg.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ type clientConfig struct {
otelConfig
{{- end }}
Client ht.Client
// UseRawPath indicates whether to use raw path in request URL, without URL encoding. Default to false (i.e., use encoding).
UseRawPath bool
}

// ClientOption is client config option.
Expand Down Expand Up @@ -243,6 +245,14 @@ func WithClient(client ht.Client) ClientOption {
}
})
}

// WithUseRawPath specifies whether to use raw path in request URL, without URL encoding.
func WithUseRawPath(useRawPath bool) ClientOption {
return optionFunc[clientConfig](func(cfg *clientConfig) {
cfg.UseRawPath = useRawPath
})
}

{{- end }}

{{- if $.AnyServerEnabled }}
Expand Down
9 changes: 9 additions & 0 deletions gen/_template/client.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ func (c *{{ if $op.WebhookInfo }}Webhook{{ end }}Client) send{{ $op.Name }}(ctx
{{- end }}

{{ if $otel }}stage = "EncodeRequest"{{ end }}
if c.cfg.UseRawPath {
u.Opaque = fmt.Sprintf("//%s%s", u.Host, u.Path)
}
r, err := ht.NewRequest(ctx, {{ $op.Spec.HTTPMethod | upper | quote }}, u)
if err != nil {
return res, errors.Wrap(err, "create request")
Expand Down Expand Up @@ -421,6 +424,12 @@ func (c *{{ if $op.WebhookInfo }}Webhook{{ end }}Client) send{{ $op.Name }}(ctx
if err != nil {
return res, errors.Wrap(err, "do request")
}

if resp.Header.Get("Content-Type") == "" {
resp.Header.Set("Content-Type", "application/json")
resp.Body = io.NopCloser(bytes.NewBufferString("{}")) // Ensure body is not nil
}

defer resp.Body.Close()

{{ if $cfg.RequestOptionsEnabled -}}
Expand Down
3 changes: 3 additions & 0 deletions gen/_template/defaults/set.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
{{ $t := $.Type }}{{ $j := $t.JSON }}{{- $val := print_go $.Default.Value }}
{{- if $j.TimeFormat }}
val, _ := json.DecodeTimeFormat(jx.DecodeStr({{ quote $val }}), {{ $j.TimeFormat }})
{{- else if $t.IsExternal }}
var val {{ $t.Go }}
_ = json.Unmarshal([]byte({{ $val }}), &val)
{{- else if $j.Format }}
val, _ := json.Decode{{ $j.Format }}(jx.DecodeStr({{ quote $val }}))
{{- else if $j.IsBase64 }}
Expand Down
3 changes: 3 additions & 0 deletions gen/_template/header.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ import (
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/otel/trace"
"go.uber.org/multierr"
{{ range $alias, $pkg := $.Imports -}}
{{ $alias }} "{{ $pkg }}"
{{ end }}

"github.com/ogen-go/ogen/conv"
ht "github.com/ogen-go/ogen/http"
Expand Down
10 changes: 10 additions & 0 deletions gen/_template/json/decode.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ if err := d.Arr(func(d *jx.Decoder) error {
if err != nil {
return err
}
{{- else if $t.IsExternal }}
b, err := d.Raw()
if err != nil {
return err
}
var v {{ $t.Go }}
if err := json.Unmarshal(b, &v); err != nil {
return err
}
{{ $.Var }} = v
{{- else if $j.Format }}
v, err := json.Decode{{ $j.Format }}(d)
{{ $.Var }} = v
Expand Down
4 changes: 4 additions & 0 deletions gen/_template/json/encode.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ if {{ $.Var }}.Set {
{{- else if $j.TimeFormat }}
{{- template "json/enc_field" $ }}
json.EncodeTimeFormat(e, {{ $.Var }}, {{ $j.TimeFormat }})
{{- else if $t.IsExternal -}}
{{- template "json/enc_field" $ }}
b, _ := json.Marshal({{ $.Var }})
e.Raw(b)
{{- else if $j.Format -}}
{{- template "json/enc_field" $ }}
json.Encode{{ $j.Format }}(e, {{ $.Var }})
Expand Down
13 changes: 13 additions & 0 deletions gen/_template/json/encoders_generic.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ func (o {{ $.ReadOnlyReceiver }}) Encode(e *jx.Encoder{{ if $g.Format }}, format
format(e, o.Value)
{{- else if $g.JSON.TimeFormat }}
json.EncodeTimeFormat(e, o.Value, {{ $g.JSON.TimeFormat }})
{{- else if $g.IsExternal }}
b, _ := json.Marshal(o.Value)
e.Raw(b)
{{- else if $g.JSON.Format }}
json.Encode{{ $g.JSON.Format }}(e, o.Value)
{{- else if $g.JSON.Fn }}
Expand Down Expand Up @@ -76,6 +79,16 @@ func (o *{{ $.Name }}) Decode(d *jx.Decoder{{ if $g.Format }}, format func(*jx.D
return err
}
o.Value = v
{{- else if $g.IsExternal }}
b, err := d.Raw()
if err != nil {
return err
}
var v {{ $g.Go }}
if err := json.Unmarshal(b, &v); err != nil {
return err
}
o.Value = v
{{- else if $g.JSON.Format }}
v, err := json.Decode{{ $g.JSON.Format }}(d)
if err != nil {
Expand Down
12 changes: 6 additions & 6 deletions gen/_template/schema/sum.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@
{{- define "schema/sum" }}
// {{ $.Name }} represents sum type.
type {{ $.Name }} struct {
Type {{ $.Name }}Type // switch on this field
Type {{ $.Name }}TypeEnum // switch on this field
{{- range $s := $.SumOf }}
{{ $s.Name }} {{ $s.Go }}
{{- end }}
}

// {{ $.Name }}Type is oneOf type of {{ $.Name }}.
type {{ $.Name }}Type string
// {{ $.Name }}TypeEnum is oneOf type of {{ $.Name }}.
type {{ $.Name }}TypeEnum string

// Possible values for {{ $.Name }}Type.
// Possible values for {{ $.Name }}TypeEnum.
const (
{{- range $s := $.SumOf }}
{{- $m := $.SumSpec.PickMappingEntryFor $s }}
{{- if $m }}
{{- /* try to use mapping key if the entry for $s is defined */}}
{{ $s.Name }}{{ $.Name }} {{ $.Name }}Type = {{ quote $m.Key }}
{{ $s.Name }}{{ $.Name }} {{ $.Name }}TypeEnum = {{ quote $m.Key }}
{{- else }}
{{- /* otherwise fallback to SumOf Type name */}}
{{ $s.Name }}{{ $.Name }} {{ $.Name }}Type = {{ quote $s.Go }}
{{ $s.Name }}{{ $.Name }} {{ $.Name }}TypeEnum = {{ quote $s.Go }}
{{- end }}
{{- end }}
)
Expand Down
3 changes: 3 additions & 0 deletions gen/_template/uri/decode.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

{{ if $t.JSON.TimeFormat -}}
c, err := time.Parse({{ $t.JSON.TimeFormat }}, val)
{{- else if $t.IsExternal -}}
var c {{ $t.Go }}
err = json.Unmarshal([]byte(val), &c)
{{- else -}}
c, err := conv.{{ $t.FromString }}(val)
{{- end }}
Expand Down
6 changes: 6 additions & 0 deletions gen/_template/uri/encode.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
{{- if $t.IsPrimitive }}
{{ if $t.JSON.TimeFormat -}}
return e.EncodeValue({{ $var }}.Format({{ $t.JSON.TimeFormat }}))
{{- else if $t.IsExternal -}}
b, err := json.Marshal({{ $var }})
if err != nil {
return err
}
return e.EncodeValue(string(b))
{{- else -}}
return e.EncodeValue(conv.{{ $t.ToString }}({{ $var }}))
{{- end }}
Expand Down
2 changes: 2 additions & 0 deletions gen/gen_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func (g *Generator) generateSchema(
gen.log = g.log.Named("schemagen")
gen.fail = g.fail
gen.depthLimit = g.parseOpts.SchemaDepthLimit
gen.imports = g.imports

t, err := gen.generate(name, schema, optional)
if err != nil {
Expand Down Expand Up @@ -185,6 +186,7 @@ func GenerateSchema(schema *jsonschema.Schema, fs FileSystem, opts GenerateSchem
if err := w.Generate("jsonschema", opts.FileName, TemplateConfig{
Package: opts.PkgName,
Types: ctx.local.types,
Imports: gen.imports,
}); err != nil {
return errors.Wrap(err, "write")
}
Expand Down
2 changes: 2 additions & 0 deletions gen/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type Generator struct {
errType *ir.Response
webhookRouter WebhookRouter
router Router
imports map[string]string

log *zap.Logger
}
Expand Down Expand Up @@ -103,6 +104,7 @@ func NewGenerator(spec *ogen.Spec, opts Options) (*Generator, error) {
errType: nil,
webhookRouter: WebhookRouter{},
router: Router{},
imports: map[string]string{},
log: opts.Logger,
}

Expand Down
27 changes: 26 additions & 1 deletion gen/ir/constructors.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package ir

import "github.com/ogen-go/ogen/jsonschema"
import (
"fmt"
"path"
"strings"

"github.com/ogen-go/ogen/jsonschema"
)

func Primitive(typ PrimitiveType, schema *jsonschema.Schema) *Type {
return &Type{
Expand Down Expand Up @@ -73,3 +79,22 @@ func Stream(name string, schema *jsonschema.Schema) *Type {
Schema: schema,
}
}

func External(schema *jsonschema.Schema) (*Type, error) {
i := strings.LastIndex(schema.XOgenType, ".")
if i < 0 || i == len(schema.XOgenType)-1 {
return nil, fmt.Errorf("'%s' is not a valid Go type (expected 'pkg/path.Type')", schema.XOgenType)
}
pkgPath := schema.XOgenType[:i]
typeName := schema.XOgenType[i+1:]

return &Type{
Kind: KindPrimitive,
Primitive: PrimitiveType(path.Base(pkgPath) + "." + typeName),
Schema: schema,
External: ExternalType{
PackagePath: pkgPath,
GoName: typeName,
},
}, nil
}
8 changes: 8 additions & 0 deletions gen/ir/external.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ir

// ExternalType represents an external type.
type ExternalType struct {
PackagePath string
GoName string
ImportAlias string
}
2 changes: 1 addition & 1 deletion gen/ir/faker.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (t *Type) FakeValue() string {
case Null:
return "struct{}{}"
default:
panic(fmt.Sprintf("unexpected PrimitiveType: %d", p))
panic("unexpected PrimitiveType: " + p)
}
}

Expand Down
Loading