Skip to content

Commit b658fcf

Browse files
authored
Support unions with multiple mappings pointing to a single underlying type (oapi-codegen#2071)
1 parent fca3f0f commit b658fcf

File tree

7 files changed

+263
-18
lines changed

7 files changed

+263
-18
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package: issue1530
2+
generate:
3+
models: true
4+
output: issue1530.gen.go
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package issue1530
2+
3+
//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml issue1530.yaml

internal/test/issues/issue-1530/issue1530.gen.go

Lines changed: 126 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
paths:
2+
/config:
3+
post:
4+
summary: Save configuration
5+
requestBody:
6+
content:
7+
application/json:
8+
schema:
9+
$ref: "#/components/schemas/ConfigSaveReq"
10+
responses:
11+
"200":
12+
description: Configuration saved successfully
13+
components:
14+
schemas:
15+
ConfigHttp:
16+
type: object
17+
properties:
18+
config_type:
19+
type: string
20+
host:
21+
type: string
22+
port:
23+
type: integer
24+
user:
25+
type: string
26+
password:
27+
type: string
28+
required:
29+
- config_type
30+
- host
31+
- port
32+
ConfigSaveReq:
33+
oneOf:
34+
- $ref: "#/components/schemas/ConfigHttp"
35+
- $ref: "#/components/schemas/ConfigSsh"
36+
discriminator:
37+
propertyName: config_type
38+
mapping:
39+
ssh_server: "#/components/schemas/ConfigSsh"
40+
apache_server: "#/components/schemas/ConfigHttp"
41+
web_server: "#/components/schemas/ConfigHttp"
42+
another_server: "#/components/schemas/ConfigHttp"
43+
ConfigSsh:
44+
type: object
45+
properties:
46+
config_type:
47+
type: string
48+
host:
49+
type: string
50+
port:
51+
type: integer
52+
user:
53+
type: string
54+
private_key:
55+
type: string
56+
required:
57+
- config_type
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package issue1530_test
2+
3+
import (
4+
"testing"
5+
6+
issue1530 "github.com/oapi-codegen/oapi-codegen/v2/internal/test/issues/issue-1530"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestIssue1530(t *testing.T) {
11+
httpConfigTypes := []string{
12+
"another_server",
13+
"apache_server",
14+
"web_server",
15+
}
16+
17+
for _, configType := range httpConfigTypes {
18+
t.Run("http-"+configType, func(t *testing.T) {
19+
saveReq := issue1530.ConfigSaveReq{}
20+
err := saveReq.FromConfigHttp(issue1530.ConfigHttp{
21+
ConfigType: configType,
22+
Host: "example.com",
23+
})
24+
require.NoError(t, err)
25+
26+
cfg, err := saveReq.AsConfigHttp()
27+
require.NoError(t, err)
28+
require.Equal(t, configType, cfg.ConfigType)
29+
30+
cfgByDiscriminator, err := saveReq.ValueByDiscriminator()
31+
require.NoError(t, err)
32+
require.Equal(t, cfg, cfgByDiscriminator)
33+
})
34+
}
35+
36+
t.Run("ssh", func(t *testing.T) {
37+
saveReq := issue1530.ConfigSaveReq{}
38+
err := saveReq.FromConfigSsh(issue1530.ConfigSsh{
39+
ConfigType: "ssh_server",
40+
})
41+
require.NoError(t, err)
42+
43+
cfg, err := saveReq.AsConfigSsh()
44+
require.NoError(t, err)
45+
require.Equal(t, "ssh_server", cfg.ConfigType)
46+
47+
cfgByDiscriminator, err := saveReq.ValueByDiscriminator()
48+
require.NoError(t, err)
49+
require.Equal(t, cfg, cfgByDiscriminator)
50+
})
51+
}

pkg/codegen/schema.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,6 @@ func generateUnion(outSchema *Schema, elements openapi3.SchemaRefs, discriminato
938938
if v == element.Ref {
939939
outSchema.Discriminator.Mapping[k] = elementSchema.GoType
940940
mapped = true
941-
break
942941
}
943942
}
944943
// Implicit mapping.
@@ -949,7 +948,7 @@ func generateUnion(outSchema *Schema, elements openapi3.SchemaRefs, discriminato
949948
outSchema.UnionElements = append(outSchema.UnionElements, UnionElement(elementSchema.GoType))
950949
}
951950

952-
if (outSchema.Discriminator != nil) && len(outSchema.Discriminator.Mapping) != len(elements) {
951+
if (outSchema.Discriminator != nil) && len(outSchema.Discriminator.Mapping) < len(elements) {
953952
return errors.New("discriminator: not all schemas were mapped")
954953
}
955954

pkg/codegen/templates/union.tmpl

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{{$typeName := .TypeName -}}
33
{{$discriminator := .Schema.Discriminator}}
44
{{$properties := .Schema.Properties -}}
5+
{{$numberOfUnionTypes := len .Schema.UnionElements -}}
56
{{range .Schema.UnionElements}}
67
{{$element := . -}}
78
// As{{ .Method }} returns the union data inside the {{$typeName}} as a {{.}}
@@ -14,16 +15,18 @@
1415
// From{{ .Method }} overwrites any union data inside the {{$typeName}} as the provided {{.}}
1516
func (t *{{$typeName}}) From{{ .Method }} (v {{.}}) error {
1617
{{if $discriminator -}}
17-
{{range $value, $type := $discriminator.Mapping -}}
18-
{{if eq $type $element -}}
19-
{{$hasProperty := false -}}
20-
{{range $properties -}}
21-
{{if eq .GoFieldName $discriminator.PropertyName -}}
22-
t.{{$discriminator.PropertyName}} = "{{$value}}"
23-
{{$hasProperty = true -}}
18+
{{if eq $numberOfUnionTypes (len $discriminator.Mapping) -}}
19+
{{range $value, $type := $discriminator.Mapping -}}
20+
{{if eq $type $element -}}
21+
{{$hasProperty := false -}}
22+
{{range $properties -}}
23+
{{if eq .GoFieldName $discriminator.PropertyName -}}
24+
t.{{$discriminator.PropertyName}} = "{{$value}}"
25+
{{$hasProperty = true -}}
26+
{{end -}}
2427
{{end -}}
28+
{{if not $hasProperty}}v.{{$discriminator.PropertyName}} = "{{$value}}"{{end}}
2529
{{end -}}
26-
{{if not $hasProperty}}v.{{$discriminator.PropertyName}} = "{{$value}}"{{end}}
2730
{{end -}}
2831
{{end -}}
2932
{{end -}}
@@ -35,16 +38,18 @@
3538
// Merge{{ .Method }} performs a merge with any union data inside the {{$typeName}}, using the provided {{.}}
3639
func (t *{{$typeName}}) Merge{{ .Method }} (v {{.}}) error {
3740
{{if $discriminator -}}
38-
{{range $value, $type := $discriminator.Mapping -}}
39-
{{if eq $type $element -}}
40-
{{$hasProperty := false -}}
41-
{{range $properties -}}
42-
{{if eq .GoFieldName $discriminator.PropertyName -}}
43-
t.{{$discriminator.PropertyName}} = "{{$value}}"
44-
{{$hasProperty = true -}}
41+
{{if eq $numberOfUnionTypes (len $discriminator.Mapping) -}}
42+
{{range $value, $type := $discriminator.Mapping -}}
43+
{{if eq $type $element -}}
44+
{{$hasProperty := false -}}
45+
{{range $properties -}}
46+
{{if eq .GoFieldName $discriminator.PropertyName -}}
47+
t.{{$discriminator.PropertyName}} = "{{$value}}"
48+
{{$hasProperty = true -}}
49+
{{end -}}
4550
{{end -}}
51+
{{if not $hasProperty}}v.{{$discriminator.PropertyName}} = "{{$value}}"{{end}}
4652
{{end -}}
47-
{{if not $hasProperty}}v.{{$discriminator.PropertyName}} = "{{$value}}"{{end}}
4853
{{end -}}
4954
{{end -}}
5055
{{end -}}

0 commit comments

Comments
 (0)