Skip to content

Commit

Permalink
#16: Simplify openapi client responses.
Browse files Browse the repository at this point in the history
  • Loading branch information
Tzrlk committed Aug 15, 2024
1 parent 7b5eabf commit da30852
Show file tree
Hide file tree
Showing 15 changed files with 1,315 additions and 818 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ dist/
*.dll
*.exe

# Keep windows files with windows line endings
*.winfile eol=crlf

# Mac
.DS_Store

Expand Down
76 changes: 76 additions & 0 deletions internal/idmc/templates/additional-properties.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// <editor-fold desc="additional-properties" defaultstate="collapsed"> /////////

{{range .Types}}{{$addType := .Schema.AdditionalPropertiesType.TypeDecl}}

// Getter for additional properties for {{.TypeName}}. Returns the specified
// element and whether it was found
func (a {{.TypeName}}) Get(fieldName string) (value {{$addType}}, found bool) {
if a.AdditionalProperties != nil {
value, found = a.AdditionalProperties[fieldName]
}
return
}

// Setter for additional properties for {{.TypeName}}
func (a *{{.TypeName}}) Set(fieldName string, value {{$addType}}) {
if a.AdditionalProperties == nil {
a.AdditionalProperties = make(map[string]{{$addType}})
}
a.AdditionalProperties[fieldName] = value
}

{{if eq 0 (len .Schema.UnionElements) -}}
// Override default JSON handling for {{.TypeName}} to handle AdditionalProperties
func (a *{{.TypeName}}) UnmarshalJSON(b []byte) error {
object := make(map[string]json.RawMessage)
err := json.Unmarshal(b, &object)
if err != nil {
return err
}
{{range .Schema.Properties}}
if raw, found := object["{{.JsonFieldName}}"]; found {
err = json.Unmarshal(raw, &a.{{.GoFieldName}})
if err != nil {
return fmt.Errorf("error reading '{{.JsonFieldName}}': %w", err)
}
delete(object, "{{.JsonFieldName}}")
}
{{end}}
if len(object) != 0 {
a.AdditionalProperties = make(map[string]{{$addType}})
for fieldName, fieldBuf := range object {
var fieldVal {{$addType}}
err := json.Unmarshal(fieldBuf, &fieldVal)
if err != nil {
return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err)
}
a.AdditionalProperties[fieldName] = fieldVal
}
}
return nil
}

// Override default JSON handling for {{.TypeName}} to handle AdditionalProperties
func (a {{.TypeName}}) MarshalJSON() ([]byte, error) {
var err error
object := make(map[string]json.RawMessage)
{{range .Schema.Properties}}
{{if not .Required}}if a.{{.GoFieldName}} != nil { {{end}}
object["{{.JsonFieldName}}"], err = json.Marshal(a.{{.GoFieldName}})
if err != nil {
return nil, fmt.Errorf("error marshaling '{{.JsonFieldName}}': %w", err)
}
{{if not .Required}} }{{end}}
{{end}}
for fieldName, field := range a.AdditionalProperties {
object[fieldName], err = json.Marshal(field)
if err != nil {
return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err)
}
}
return json.Marshal(object)
}
{{end}}
{{end}}

// </editor-fold> //////////////////////////////////////////////////////////////
53 changes: 53 additions & 0 deletions internal/idmc/templates/imports.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{{- if opts.Generate.StdHTTPServer }}//go:build go1.22
{{- end }}
// <editor-fold desc="imports" defaultstate="collapsed"> ///////////////////////

// Package {{.PackageName}} provides primitives to interact with the openapi HTTP API.
//
// Code generated by {{.ModuleName}} version {{.Version}} DO NOT EDIT.
package {{.PackageName}}

import (
"bytes"
"compress/gzip"
"context"
"encoding/base64"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"gopkg.in/yaml.v2"
"io"
"os"
"mime"
"mime/multipart"
"net/http"
"net/url"
"path"
"strings"
"time"

"github.com/oapi-codegen/runtime"
"github.com/oapi-codegen/nullable"
strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo"
strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin"
strictiris "github.com/oapi-codegen/runtime/strictmiddleware/iris"
strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp"
openapi_types "github.com/oapi-codegen/runtime/types"
"github.com/getkin/kin-openapi/openapi3"
"github.com/go-chi/chi/v5"
"github.com/labstack/echo/v4"
"github.com/gin-gonic/gin"
"github.com/gofiber/fiber/v2"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/core/router"
"github.com/gorilla/mux"
{{- range .ExternalImports}}
{{ . }}
{{- end}}
{{- range .AdditionalImports}}
{{.Alias}} "{{.Package}}"
{{- end}}
)

// </editor-fold> //////////////////////////////////////////////////////////////
91 changes: 91 additions & 0 deletions internal/idmc/templates/inline.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// <editor-fold desc="inline" defaultstate="collapsed"> ////////////////////////

// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
{{range .SpecParts}}
"{{.}}",{{end}}
}

// GetSwagger returns the content of the embedded swagger specification file
// or error if failed to decode
func decodeSpec() ([]byte, error) {
zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
if err != nil {
return nil, fmt.Errorf("error base64 decoding spec: %w", err)
}
zr, err := gzip.NewReader(bytes.NewReader(zipped))
if err != nil {
return nil, fmt.Errorf("error decompressing spec: %w", err)
}
var buf bytes.Buffer
_, err = buf.ReadFrom(zr)
if err != nil {
return nil, fmt.Errorf("error decompressing spec: %w", err)
}

return buf.Bytes(), nil
}

var rawSpec = decodeSpecCached()

// a naive cached of a decoded swagger spec
func decodeSpecCached() func() ([]byte, error) {
data, err := decodeSpec()
return func() ([]byte, error) {
return data, err
}
}

// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
res := make(map[string]func() ([]byte, error))
if len(pathToFile) > 0 {
res[pathToFile] = rawSpec
}
{{ if .ImportMapping }}
pathPrefix := path.Dir(pathToFile)
{{ end }}
{{ range $key, $value := .ImportMapping }}
for rawPath, rawFunc := range {{ $value.Name }}.PathToRawSpec(path.Join(pathPrefix, "{{ $key }}")) {
if _, ok := res[rawPath]; ok {
// it is not possible to compare functions in golang, so always overwrite the old value
}
res[rawPath] = rawFunc
}
{{- end }}
return res
}

// GetSwagger returns the Swagger specification corresponding to the generated code
// in this file. The external references of Swagger specification are resolved.
// The logic of resolving external references is tightly connected to "import-mapping" feature.
// Externally referenced files must be embedded in the corresponding golang packages.
// Urls can be supported but this task was out of the scope.
func GetSwagger() (swagger *openapi3.T, err error) {
resolvePath := PathToRawSpec("")

loader := openapi3.NewLoader()
loader.IsExternalRefsAllowed = true
loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
pathToFile := url.String()
pathToFile = path.Clean(pathToFile)
getSpec, ok := resolvePath[pathToFile]
if !ok {
err1 := fmt.Errorf("path not found: %s", pathToFile)
return nil, err1
}
return getSpec()
}
var specData []byte
specData, err = rawSpec()
if err != nil {
return
}
swagger, err = loader.LoadFromData(specData)
if err != nil {
return
}
return
}

// </editor-fold> //////////////////////////////////////////////////////////////
8 changes: 8 additions & 0 deletions internal/idmc/templates/typedef.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// <editor-fold desc="typedef" defaultstate="collapsed"> ///////////////////////

{{range .Types}}
{{ if .Schema.Description }}{{ toGoComment .Schema.Description .TypeName }}{{ else }}// {{.TypeName}} defines model for {{.JsonName}}.{{ end }}
type {{.TypeName}} {{if .IsAlias }}={{end}} {{.Schema.TypeDecl}}
{{end}}

// </editor-fold> //////////////////////////////////////////////////////////////
74 changes: 74 additions & 0 deletions internal/idmc/templates/union-and-additional-properties.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{{range .Types}}

{{$addType := .Schema.AdditionalPropertiesType.TypeDecl}}
{{$typeName := .TypeName -}}
{{$discriminator := .Schema.Discriminator}}
{{$properties := .Schema.Properties -}}

// Override default JSON handling for {{.TypeName}} to handle AdditionalProperties and union
func (a *{{.TypeName}}) UnmarshalJSON(b []byte) error {
err := a.union.UnmarshalJSON(b)
if err != nil {
return err
}
object := make(map[string]json.RawMessage)
err = json.Unmarshal(b, &object)
if err != nil {
return err
}
{{range .Schema.Properties}}
if raw, found := object["{{.JsonFieldName}}"]; found {
err = json.Unmarshal(raw, &a.{{.GoFieldName}})
if err != nil {
return fmt.Errorf("error reading '{{.JsonFieldName}}': %w", err)
}
delete(object, "{{.JsonFieldName}}")
}
{{end}}
if len(object) != 0 {
a.AdditionalProperties = make(map[string]{{$addType}})
for fieldName, fieldBuf := range object {
var fieldVal {{$addType}}
err := json.Unmarshal(fieldBuf, &fieldVal)
if err != nil {
return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err)
}
a.AdditionalProperties[fieldName] = fieldVal
}
}
return nil
}

// Override default JSON handling for {{.TypeName}} to handle AdditionalProperties and union
func (a {{.TypeName}}) MarshalJSON() ([]byte, error) {
var err error
b, err := a.union.MarshalJSON()
if err != nil {
return nil, err
}
object := make(map[string]json.RawMessage)
if a.union != nil {
err = json.Unmarshal(b, &object)
if err != nil {
return nil, err
}
}
{{range .Schema.Properties}}
{{if not .Required}}if a.{{.GoFieldName}} != nil { {{end}}
object["{{.JsonFieldName}}"], err = json.Marshal(a.{{.GoFieldName}})
if err != nil {
return nil, fmt.Errorf("error marshaling '{{.JsonFieldName}}': %w", err)
}
{{if not .Required}} }{{end}}
{{end}}
for fieldName, field := range a.AdditionalProperties {
object[fieldName], err = json.Marshal(field)
if err != nil {
return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err)
}
}
return json.Marshal(object)
}
{{end}}

// </editor-fold> //////////////////////////////////////////////////////////////
Loading

0 comments on commit da30852

Please sign in to comment.