Skip to content
Merged
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: 4 additions & 4 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"hash/crc64"
"log"
"reflect"
"sort"
"strings"
"time"
)
Expand Down Expand Up @@ -96,6 +95,9 @@ func buildSchema(body any) (s Schema) {
if body == nil {
return s
}
if jBody, ok := body.(JSONString); ok {
body = jBody.ToMap()
}

value := reflect.ValueOf(body)
typ := reflect.TypeOf(body)
Expand Down Expand Up @@ -125,9 +127,7 @@ func buildSchema(body any) (s Schema) {
sKeys = append(sKeys, k.String())
s.Properties[k.String()] = buildSchema(value.MapIndex(k).Interface())
}
sort.Strings(sKeys)
// create a unique short, somewhat readable title
s.Title = hash16(strings.Join(sKeys, ""))
s.Title = GetSchemaName(sKeys)

case reflect.Struct:
s.Title = typ.String()
Expand Down
26 changes: 25 additions & 1 deletion build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func TestBuildSchema(t *testing.T) {
F1 int `json:"f1_int"`
F2 bool `json:"f2_bool"`
}
setJSON := JSONString(`{"error": "invalid"}`).SetName("error_message")

fn := func(i any) (Schema, error) {
return buildSchema(i), nil
Expand Down Expand Up @@ -70,6 +71,30 @@ func TestBuildSchema(t *testing.T) {
},
},
},
"jsonString": {
Input: JSONString(`{"key": "value"}`),
Expected: Schema{
Type: "object",
Title: "2292dac000000000",
Properties: map[string]Schema{"key": {Type: "string"}},
},
},
"jsonString_named": {
Input: setJSON,
Expected: Schema{
Type: "object",
Title: "error_message",
Properties: map[string]Schema{"error": {Type: "string"}},
},
},
/*"jsonString_array": {
Input: JSONString(`["value1", "value2"]`),
Expected: Schema{
Type: "array",
Items: &Schema{Type: "string"},
Title: "2292dac000000000",
},
},*/
"map_simple": {
Input: map[string]string{
"key": "value",
Expand Down Expand Up @@ -230,7 +255,6 @@ func TestBuildSchema(t *testing.T) {
}

func TestCompile(t *testing.T) {

type abc struct {
Date time.Time
Price float64
Expand Down
65 changes: 65 additions & 0 deletions custom.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package openapi

import (
"encoding/json"
"errors"
"fmt"
"log"
"sort"
"strings"
"time"
)

Expand Down Expand Up @@ -50,3 +55,63 @@ func (t *Time) UnmarshalText(data []byte) error {
t.Time, err = time.Parse(t.Format, string(data))
return err
}

// JSONString is used to denote this string should be treated as a JSON
type JSONString string

func (s JSONString) ToMap() any {
var m any
if s[0] == '[' && s[len(s)-1] == ']' {
m = make([]any, 0)
} else {
m = make(map[string]any)
}
err := json.Unmarshal([]byte(s), &m)
if err != nil {
// return a response with the error message
return fmt.Sprintf("invalid JSON: %v %v", s, err)
}
return m
}

// SetName lets you define a readable name for the JSONString.
// This only needs to be called once
func (s JSONString) SetName(name string) JSONString {
m, ok := s.ToMap().(map[string]any)
if !ok {
return s // not a map, cannot set name
}
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
SetSchemaName(name, keys)
return s
}

// SetSchemaName sets a name for the hash16 values of the JSON keys provided.
// This is used for the JSONString type to create a unique schema name based on the keys of the JSON object.
func SetSchemaName(name string, keys []string) {
sort.Strings(keys)
key := hash16(strings.Join(keys, ""))
if v, exists := namedSchemas[key]; exists {
if v == name {
return // already set to the same name, no need to log
}
log.Printf("%v overrides named schema: %v -> %v", key, v, name)
}
namedSchemas[key] = name
}

var namedSchemas = map[string]string{} // [hash16_key]name

// GetSchemaName returns a unique name for the schema based on the keys provided.
// it will use a predefined name if it exists
func GetSchemaName(keys []string) string {
sort.Strings(keys)
key := hash16(strings.Join(keys, ""))
if name, exists := namedSchemas[key]; exists {
return name
}
return key
}
12 changes: 8 additions & 4 deletions paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ type Response struct {
// WithJSONString takes a json string object and adds a json Content to the Response
// s is unmarshalled into a map to extract the key and value pairs
// JSONStringResp || resp.JSONString(s)
// Deprecated: use WithExample(JSONString(s)) instead
func (r Response) WithJSONString(s string) Response {
return r.WithNamedJsonString("", s)
}
Expand All @@ -174,6 +175,7 @@ func (r Response) WithExample(i any) Response {
// WithNamedJsonString takes a json string object and adds a json Content to the Response
// s is unmarshalled into a map to extract the key and value pairs
// JSONStringResp || resp.JSONString(s)
// Deprecated: use WithNamedExample(name, JSONString(s)) instead
func (r Response) WithNamedJsonString(name string, s string) Response {
var m any
if s[0] == '[' && s[len(s)-1] == ']' {
Expand Down Expand Up @@ -238,11 +240,8 @@ type RequestBody struct {
Required bool `json:"required,omitempty"` // Determines if the request body is required in the request. Defaults to false.
}

// Deprecated: use WithNamedExample(name, JSONString(s)) instead
func (r RequestBody) WithNamedJsonString(name string, s string) RequestBody {
return r.WithNamedExample(name, s)
}

func (r RequestBody) WithJSONString(s string) RequestBody {
var m any
if s[0] == '[' && s[len(s)-1] == ']' {
m = make([]any, 0)
Expand All @@ -260,6 +259,11 @@ func (r RequestBody) WithJSONString(s string) RequestBody {
return r.WithExample(m)
}

// Deprecated: use WithExample(JSONString(s)) instead
func (r RequestBody) WithJSONString(s string) RequestBody {
return r.WithNamedJsonString("", s)
}

func (r RequestBody) WithExample(i any) RequestBody {
return r.WithNamedExample("", i)
}
Expand Down