Skip to content

Commit

Permalink
Merge pull request #151 from danielgtaylor/schema-round-trip
Browse files Browse the repository at this point in the history
fix: add $schema field to input to allow round-trips
  • Loading branch information
danielgtaylor authored Oct 23, 2023
2 parents ebefca4 + bda18b4 commit 80c75e4
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 14 deletions.
21 changes: 21 additions & 0 deletions huma_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,27 @@ func TestFeatures(t *testing.T) {
assert.Equal(t, 256, resp.Code)
},
},
{
// Simulate a request with a body that came from another call, which
// includes the `$schema` field. It should be allowed to be passed
// to the new operation as input without modification.
Name: "round-trip-schema-field",
Register: func(t *testing.T, api API) {
Register(api, Operation{
Method: http.MethodPut,
Path: "/round-trip",
}, func(ctx context.Context, input *struct {
Body struct {
Name string `json:"name"`
}
}) (*struct{}, error) {
return nil, nil
})
},
Method: http.MethodPut,
URL: "/round-trip",
Body: `{"$schema": "...", "name": "foo"}`,
},
{
Name: "one-of input",
Register: func(t *testing.T, api API) {
Expand Down
40 changes: 26 additions & 14 deletions transforms.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,32 +47,44 @@ func NewSchemaLinkTransformer(prefix, schemasPath string) *SchemaLinkTransformer
}
}

func (t *SchemaLinkTransformer) addSchemaField(oapi *OpenAPI, content *MediaType) bool {
if content == nil || content.Schema == nil || content.Schema.Ref == "" {
return true
}

schema := oapi.Components.Schemas.SchemaFromRef(content.Schema.Ref)
if schema.Type != TypeObject || (schema.Properties != nil && schema.Properties["$schema"] != nil) {
return true
}

schema.Properties["$schema"] = &Schema{
Type: TypeString,
Format: "uri",
Description: "A URL to the JSON Schema for this object.",
ReadOnly: true,
}
return false
}

// OnAddOperation is triggered whenever a new operation is added to the API,
// enabling this transformer to precompute & cache information about the
// response and schema.
func (t *SchemaLinkTransformer) OnAddOperation(oapi *OpenAPI, op *Operation) {
// Update registry to be able to get the type from a schema ref.
// Register the type in t.types with the generated ref
if op.RequestBody != nil && op.RequestBody.Content != nil {
for _, content := range op.RequestBody.Content {
t.addSchemaField(oapi, content)
}
}

registry := oapi.Components.Schemas
for _, resp := range op.Responses {
for _, content := range resp.Content {
if content == nil || content.Schema == nil || content.Schema.Ref == "" {
if t.addSchemaField(oapi, content) {
continue
}

schema := registry.SchemaFromRef(content.Schema.Ref)
if schema.Type != TypeObject || (schema.Properties != nil && schema.Properties["$schema"] != nil) {
continue
}

// First, modify the schema to have the $schema field.
schema.Properties["$schema"] = &Schema{
Type: TypeString,
Format: "uri",
Description: "A URL to the JSON Schema for this object.",
ReadOnly: true,
}

// Then, create the wrapper Go type that has the $schema field.
typ := deref(registry.TypeFromRef(content.Schema.Ref))

Expand Down

0 comments on commit 80c75e4

Please sign in to comment.