diff --git a/backend/controller/console.go b/backend/controller/console.go index 4ac283b979..bbe9f4f6de 100644 --- a/backend/controller/console.go +++ b/backend/controller/console.go @@ -101,7 +101,7 @@ func (c *ConsoleService) GetModules(ctx context.Context, req *connect.Request[pb var jsonRequestSchema string if verbSchema.Request != nil { if requestData, ok := verbSchema.Request.(*schema.Ref); ok { - jsonSchema, err := schema.DataToJSONSchema(sch, *requestData) + jsonSchema, err := schema.RequestResponseToJSONSchema(sch, *requestData) if err != nil { return nil, err } diff --git a/backend/controller/ingress/request.go b/backend/controller/ingress/request.go index ce3bd3eeb0..6672829c9f 100644 --- a/backend/controller/ingress/request.go +++ b/backend/controller/ingress/request.go @@ -217,18 +217,20 @@ func buildRequestMap(route *dal.IngressRoute, r *http.Request, ref *schema.Ref, requestMap[k] = v } default: - data, err := sch.ResolveMonomorphised(ref) + symbol, err := sch.ResolveRequestResponseType(ref) if err != nil { return nil, err } - queryMap, err := parseQueryParams(r.URL.Query(), data) - if err != nil { - return nil, fmt.Errorf("HTTP query params are not valid: %w", err) - } + if data, ok := symbol.(*schema.Data); ok { + queryMap, err := parseQueryParams(r.URL.Query(), data) + if err != nil { + return nil, fmt.Errorf("HTTP query params are not valid: %w", err) + } - for key, value := range queryMap { - requestMap[key] = value + for key, value := range queryMap { + requestMap[key] = value + } } } diff --git a/backend/schema/jsonschema.go b/backend/schema/jsonschema.go index bd0d17c2d4..9ba1ad9797 100644 --- a/backend/schema/jsonschema.go +++ b/backend/schema/jsonschema.go @@ -7,21 +7,21 @@ import ( "github.com/swaggest/jsonschema-go" ) -// DataToJSONSchema converts the schema for a Data object to a JSON Schema. +// RequestResponseToJSONSchema converts the schema for a Verb request or response object to a JSON Schema. // // It takes in the full schema in order to resolve and define references. -func DataToJSONSchema(sch *Schema, ref Ref) (*jsonschema.Schema, error) { - data, err := sch.ResolveMonomorphised(&ref) +func RequestResponseToJSONSchema(sch *Schema, ref Ref) (*jsonschema.Schema, error) { + symbol, err := sch.ResolveRequestResponseType(&ref) if err != nil { return nil, err } - if data == nil { - return nil, fmt.Errorf("unknown data type %s", ref) + if symbol == nil { + return nil, fmt.Errorf("unknown request/response reference %s", ref) } // Encode root, and collect all data types reachable from the root. refs := map[RefKey]*Ref{} - root := nodeToJSSchema(data, refs) + root := nodeToJSSchema(symbol, refs) if len(refs) == 0 { return root, nil } diff --git a/backend/schema/jsonschema_test.go b/backend/schema/jsonschema_test.go index ad9e58954f..cf68b2c373 100644 --- a/backend/schema/jsonschema_test.go +++ b/backend/schema/jsonschema_test.go @@ -77,7 +77,7 @@ var jsonSchemaSample = &Schema{ } func TestDataToJSONSchema(t *testing.T) { - schema, err := DataToJSONSchema(jsonSchemaSample, Ref{Module: "foo", Name: "Foo"}) + schema, err := RequestResponseToJSONSchema(jsonSchemaSample, Ref{Module: "foo", Name: "Foo"}) assert.NoError(t, err) actual, err := json.MarshalIndent(schema, "", " ") assert.NoError(t, err) @@ -317,7 +317,7 @@ func TestJSONSchemaValidation(t *testing.T) { } ` - schema, err := DataToJSONSchema(jsonSchemaSample, Ref{Module: "foo", Name: "Foo"}) + schema, err := RequestResponseToJSONSchema(jsonSchemaSample, Ref{Module: "foo", Name: "Foo"}) assert.NoError(t, err) schemaJSON, err := json.MarshalIndent(schema, "", " ") assert.NoError(t, err) @@ -355,7 +355,7 @@ func TestInvalidEnumValidation(t *testing.T) { } ` - schema, err := DataToJSONSchema(jsonSchemaSample, Ref{Module: "foo", Name: "Foo"}) + schema, err := RequestResponseToJSONSchema(jsonSchemaSample, Ref{Module: "foo", Name: "Foo"}) assert.NoError(t, err) schemaJSON, err := json.MarshalIndent(schema, "", " ") assert.NoError(t, err) diff --git a/backend/schema/jsonvalidate.go b/backend/schema/jsonvalidate.go index 8c131549a0..940e26be34 100644 --- a/backend/schema/jsonvalidate.go +++ b/backend/schema/jsonvalidate.go @@ -218,28 +218,29 @@ func ValidateJSONValue(fieldType Type, path path, value any, sch *Schema) error // ValidateRequestMap validates a given JSON map against the provided schema. func ValidateRequestMap(ref *Ref, path path, request map[string]any, sch *Schema) error { - data, err := sch.ResolveMonomorphised(ref) + symbol, err := sch.ResolveRequestResponseType(ref) if err != nil { return err } var errs []error - for _, field := range data.Fields { - fieldPath := append(path, "."+field.Name) //nolint:gocritic - - value, haveValue := request[field.Name] - if !haveValue && !allowMissingField(field) { - errs = append(errs, fmt.Errorf("%s is required", fieldPath)) - continue - } + if data, ok := symbol.(*Data); ok { + for _, field := range data.Fields { + fieldPath := append(path, "."+field.Name) //nolint:gocritic + + value, haveValue := request[field.Name] + if !haveValue && !allowMissingField(field) { + errs = append(errs, fmt.Errorf("%s is required", fieldPath)) + continue + } - if haveValue { - err := ValidateJSONValue(field.Type, fieldPath, value, sch) - if err != nil { - errs = append(errs, err) + if haveValue { + err := ValidateJSONValue(field.Type, fieldPath, value, sch) + if err != nil { + errs = append(errs, err) + } } } - } return errors.Join(errs...) diff --git a/backend/schema/schema.go b/backend/schema/schema.go index b8d5568d77..adcc26e4dd 100644 --- a/backend/schema/schema.go +++ b/backend/schema/schema.go @@ -47,7 +47,26 @@ func (s *Schema) Hash() [sha256.Size]byte { return sha256.Sum256([]byte(s.String())) } +// ResolveRequestResponseType resolves a reference to a supported request/response type, which can be a Data or an Any, +// or a TypeAlias over either supported type. +func (s *Schema) ResolveRequestResponseType(ref *Ref) (Symbol, error) { + decl, ok := s.Resolve(ref).Get() + if !ok { + return nil, fmt.Errorf("unknown ref %s", ref) + } + + if ta, ok := decl.(*TypeAlias); ok { + if typ, ok := ta.Type.(*Any); ok { + return typ, nil + } + } + + return s.resolveToDataMonomorphised(ref, nil) +} + // ResolveMonomorphised resolves a reference to a monomorphised Data type. +// Also supports resolving the monomorphised Data type underlying a TypeAlias, where applicable. +// // If a Ref is not found, returns ErrNotFound. func (s *Schema) ResolveMonomorphised(ref *Ref) (*Data, error) { return s.resolveToDataMonomorphised(ref, nil)