feat(inbound-api): nested Object/List extended-type validation (#13)
Object/List parameters and return values were shape-validated only (object vs
array), with no field-level/nested type checks — type-wrong nested data passed
inbound validation and failed only at script runtime. Add recursive type
validation (declared Object field types, List element type, scalars at any depth)
with path-qualified errors, symmetric across ParameterValidator and ReturnValueValidator.
Both validators now parse the canonical JSON Schema definition format (the
Central UI / MigrateParametersToJsonSchema output) via a shared recursive engine,
Commons.Types.InboundApi.InboundApiSchema, instead of the legacy flat
[{name,type}] array which they could not even deserialize from migrated rows.
The legacy flat-array form is still accepted on read for transition safety.
Undeclared fields are rejected at every level (consistent with the existing
top-level unexpected-parameter rejection); a present-but-null value satisfies
any type, only absence of a required field is an error.
This commit is contained in:
@@ -36,28 +36,28 @@ public class ApiMethod
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } // route segment
|
||||
public string Script { get; set; } // Roslyn C# script body
|
||||
public string? ParameterDefinitions { get; set; } // JSON: List<ParameterDefinition>
|
||||
public string? ReturnDefinition { get; set; } // JSON: List<ReturnFieldDefinition>
|
||||
public string? ParameterDefinitions { get; set; } // JSON Schema (object) describing parameters
|
||||
public string? ReturnDefinition { get; set; } // JSON Schema describing the return value
|
||||
public int TimeoutSeconds { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
`ParameterDefinitions` and `ReturnDefinition` are stored as JSON strings to keep the schema simple; both are deserialized on every request by `ParameterValidator` and `ReturnValueValidator`.
|
||||
`ParameterDefinitions` and `ReturnDefinition` are stored as JSON Schema strings (canonical form: `{"type":"object","properties":{…},"required":[…]}`, arrays via `"items"`); both are parsed on every request by `ParameterValidator` and `ReturnValueValidator` into a shared recursive `InboundApiSchema` (Commons). The legacy flat-array form (`[{name,type,required,itemType?}]`) is still accepted on read.
|
||||
|
||||
### Extended type system
|
||||
|
||||
Parameter and return field definitions share the same six-type vocabulary:
|
||||
Parameter and return definitions share the same six-type vocabulary (JSON Schema type tokens in parentheses):
|
||||
|
||||
| Type | JSON shape | C# value after coercion |
|
||||
|-----------|----------------------|-------------------------------------|
|
||||
| `Boolean` | `true` / `false` | `bool` |
|
||||
| `Integer` | number (whole) | `long` |
|
||||
| `Float` | number | `double` |
|
||||
| `String` | string | `string` |
|
||||
| `Object` | JSON object | `Dictionary<string, object?>` |
|
||||
| `List` | JSON array | `List<object?>` |
|
||||
| Type | JSON Schema token | JSON shape | C# value after coercion |
|
||||
|-----------|-------------------|------------------|-------------------------------|
|
||||
| `Boolean` | `boolean` | `true` / `false` | `bool` |
|
||||
| `Integer` | `integer` | number (whole) | `long` |
|
||||
| `Float` | `number` | number | `double` |
|
||||
| `String` | `string` | string | `string` |
|
||||
| `Object` | `object` | JSON object | `Dictionary<string, object?>` |
|
||||
| `List` | `array` | JSON array | `List<object?>` |
|
||||
|
||||
`Object` and `List` are validated for JSON shape only — field-level or element-level type constraints are the script's responsibility. Template attributes use only the four primitive types; the extended types apply here and in the External System Gateway.
|
||||
`Object` and `List` are validated **recursively**: a declared object validates each field against its declared (nested) type and rejects undeclared fields; a list validates every element against the declared `items` type. Scalars are checked at any depth and errors are path-qualified (e.g. `order.items[2].quantity`). A bare `{"type":"object"}` / `{"type":"array"}` (no `properties` / `items`) stays shape-only. Template attributes use only the four primitive types; the extended types apply here and in the External System Gateway.
|
||||
|
||||
## Architecture
|
||||
|
||||
|
||||
Reference in New Issue
Block a user