feat: complete API-driven projects with infrastructure and business acceptance
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -15,14 +16,26 @@ import (
|
||||
)
|
||||
|
||||
type Schema struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Properties map[string]*Schema `json:"properties,omitempty"`
|
||||
Required []string `json:"required,omitempty"`
|
||||
Items *Schema `json:"items,omitempty"`
|
||||
AdditionalProperties *Schema `json:"additionalProperties,omitempty"`
|
||||
Nullable bool `json:"nullable,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Properties map[string]*Schema `json:"properties,omitempty"`
|
||||
Required []string `json:"required,omitempty"`
|
||||
Items *Schema `json:"items,omitempty"`
|
||||
AdditionalProperties *Schema `json:"additionalProperties,omitempty"`
|
||||
AdditionalPropertiesAllowed *bool `json:"-"`
|
||||
Nullable bool `json:"nullable,omitempty"`
|
||||
Minimum *float64 `json:"minimum,omitempty"`
|
||||
Maximum *float64 `json:"maximum,omitempty"`
|
||||
ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"`
|
||||
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"`
|
||||
MinLength *int `json:"minLength,omitempty"`
|
||||
MaxLength *int `json:"maxLength,omitempty"`
|
||||
MinItems *int `json:"minItems,omitempty"`
|
||||
MaxItems *int `json:"maxItems,omitempty"`
|
||||
Enum []any `json:"enum,omitempty"`
|
||||
Default any `json:"default,omitempty"`
|
||||
Not *Schema `json:"not,omitempty"`
|
||||
}
|
||||
type Parameter struct {
|
||||
Name string `json:"name"`
|
||||
@@ -135,7 +148,9 @@ func (a *API) Handle(router Registrar, op Operation, handler httpx.Handler, midd
|
||||
return err
|
||||
}
|
||||
var frozen any
|
||||
if err = json.Unmarshal(encoded, &frozen); err != nil {
|
||||
decoder := json.NewDecoder(bytes.NewReader(encoded))
|
||||
decoder.UseNumber()
|
||||
if err = decoder.Decode(&frozen); err != nil {
|
||||
return err
|
||||
}
|
||||
a.mu.Lock()
|
||||
|
||||
47
openapi/schemaJSON.go
Normal file
47
openapi/schemaJSON.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// OpenAPI permits both a schema and a boolean for additionalProperties. Keep
|
||||
// the existing schema field source-compatible and expose the boolean separately.
|
||||
func (s Schema) MarshalJSON() ([]byte, error) {
|
||||
type alias Schema
|
||||
if s.AdditionalPropertiesAllowed == nil {
|
||||
return json.Marshal(alias(s))
|
||||
}
|
||||
return json.Marshal(struct {
|
||||
alias
|
||||
Allowed bool `json:"additionalProperties"`
|
||||
}{alias(s), *s.AdditionalPropertiesAllowed})
|
||||
}
|
||||
func (s *Schema) UnmarshalJSON(data []byte) error {
|
||||
type alias Schema
|
||||
var raw map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
var allowed *bool
|
||||
if value, ok := raw["additionalProperties"]; ok && (string(value) == "true" || string(value) == "false") {
|
||||
var v bool
|
||||
_ = json.Unmarshal(value, &v)
|
||||
allowed = &v
|
||||
delete(raw, "additionalProperties")
|
||||
var err error
|
||||
data, err = json.Marshal(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var a alias
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&a); err != nil {
|
||||
return err
|
||||
}
|
||||
*s = Schema(a)
|
||||
s.AdditionalPropertiesAllowed = allowed
|
||||
return nil
|
||||
}
|
||||
48
openapi/schemaJSON_test.go
Normal file
48
openapi/schemaJSON_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.yuntuo.hk/k2safe/go-omni/httpx"
|
||||
)
|
||||
|
||||
func TestSchemaAdditionalPropertiesAndExactNumbers(t *testing.T) {
|
||||
for _, text := range []string{
|
||||
`{"type":"object","additionalProperties":false}`,
|
||||
`{"type":"object","additionalProperties":true}`,
|
||||
`{"type":"object","additionalProperties":{"type":"string"}}`,
|
||||
`{"type":"integer","default":18446744073709551615,"enum":[18446744073709551615]}`,
|
||||
} {
|
||||
var s Schema
|
||||
if err := json.Unmarshal([]byte(text), &s); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(text, "18446744073709551615") && !strings.Contains(string(b), "18446744073709551615") {
|
||||
t.Fatalf("lost precision: %s", b)
|
||||
}
|
||||
var roundTrip Schema
|
||||
if err = json.Unmarshal(b, &roundTrip); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
api, _ := New("Exact numbers", "1")
|
||||
s := &Schema{Type: "integer", Enum: []any{json.Number("18446744073709551615")}}
|
||||
err := api.Handle(discardSchemaRoutes{}, Operation{Method: "get", Path: "/id", OperationID: "readId", Responses: map[int]Response{200: {Description: "ok", Schema: s}}}, func(*httpx.Context) error { return nil })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, _ := api.JSON()
|
||||
if !strings.Contains(string(data), "18446744073709551615") {
|
||||
t.Fatal(string(data))
|
||||
}
|
||||
}
|
||||
|
||||
type discardSchemaRoutes struct{}
|
||||
|
||||
func (discardSchemaRoutes) Handle(string, string, httpx.Handler, ...httpx.Middleware) {}
|
||||
Reference in New Issue
Block a user