fix(m9/T32b): resolve $ref in InboundAPI runtime validators (no deploy-passes/runtime-400); diamond test; ref-annotation message
This commit is contained in:
@@ -30,21 +30,45 @@ public static class ParameterValidator
|
||||
/// </summary>
|
||||
/// <param name="body">The parsed JSON request body; null or undefined if no body was supplied.</param>
|
||||
/// <param name="parameterDefinitions">JSON Schema describing the method's parameters (an object schema), or null/empty when no parameters are defined. The legacy flat-array form is also accepted.</param>
|
||||
/// <param name="resolveRef">
|
||||
/// M9-T32b: optional JSON-Schema <c>$ref</c> resolution seam mapping a
|
||||
/// <c>{"$ref":"lib:Name"}</c> reference target's name to the referenced schema JSON
|
||||
/// (or <c>null</c> when the library entry does not exist). The endpoint pre-loads the
|
||||
/// shared-schema library (backed by <c>ISharedSchemaRepository</c>) and supplies it
|
||||
/// ONLY when the definition actually uses a <c>$ref</c>. <c>null</c> means no resolver:
|
||||
/// schemas with no <c>$ref</c> behave exactly as before; a <c>$ref</c> with no resolver
|
||||
/// (or a dangling one) is surfaced as a CLEAR invalid result naming the missing
|
||||
/// reference — NOT an opaque parse-error 400 from a thrown <see cref="JsonException"/>.
|
||||
/// </param>
|
||||
/// <returns>A <see cref="ParameterValidationResult"/> with coerced parameter values on success, or an error message on failure.</returns>
|
||||
public static ParameterValidationResult Validate(
|
||||
JsonElement? body,
|
||||
string? parameterDefinitions)
|
||||
string? parameterDefinitions,
|
||||
Func<string, string?>? resolveRef = null)
|
||||
{
|
||||
InboundApiSchema? schema;
|
||||
// M9-T32b: parse through the ref-COLLECTING path. A {"$ref":"lib:Name"} that the
|
||||
// resolver can satisfy is resolved inline; a dangling/cyclic/over-depth ref is
|
||||
// collected (not thrown) so the runtime returns a descriptive "could not be
|
||||
// resolved" message instead of an opaque "Invalid parameter definitions" — the
|
||||
// deploy-passes/runtime-400 defect this fix closes.
|
||||
SchemaParseResult parsed;
|
||||
try
|
||||
{
|
||||
schema = InboundApiSchema.Parse(parameterDefinitions);
|
||||
parsed = InboundApiSchema.ParseWithRefs(parameterDefinitions, resolveRef);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return ParameterValidationResult.Invalid("Invalid parameter definitions in method configuration");
|
||||
}
|
||||
|
||||
if (parsed.UnresolvedReferences.Count > 0)
|
||||
{
|
||||
return ParameterValidationResult.Invalid(
|
||||
$"Parameter definitions reference {DescribeUnresolved(parsed.UnresolvedReferences)} which could not be resolved");
|
||||
}
|
||||
|
||||
InboundApiSchema? schema = parsed.Schema;
|
||||
|
||||
// No parameters defined (or an object schema with no declared fields) —
|
||||
// the body is unconstrained and yields an empty parameter set.
|
||||
if (schema is null || schema.Type != "object" || schema.Fields.Count == 0)
|
||||
@@ -94,6 +118,18 @@ public static class ParameterValidator
|
||||
return ParameterValidationResult.Valid(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// M9-T32b: renders the unresolved <c>{"$ref":"lib:Name"}</c> references for a clear,
|
||||
/// descriptive runtime error — the bare <c>lib:</c>-qualified pointer name stays
|
||||
/// separate from any parenthesised reason (cyclic/over-depth), so the message reads
|
||||
/// e.g. <c>schema(s) 'lib:Foo' (cyclic reference)</c> rather than embedding the
|
||||
/// annotation inside the <c>lib:</c>-looking string.
|
||||
/// </summary>
|
||||
/// <param name="unresolved">The references that could not be resolved.</param>
|
||||
/// <returns>A human-readable description of the unresolved schema reference(s).</returns>
|
||||
internal static string DescribeUnresolved(IReadOnlyList<UnresolvedSchemaRef> unresolved) =>
|
||||
$"schema(s) {string.Join(", ", unresolved.Select(r => $"'{r.Describe()}'"))}";
|
||||
|
||||
/// <summary>
|
||||
/// Converts a validated JSON element to the CLR value handed to the script.
|
||||
/// Validation has already passed, so this only shapes the value: scalars to
|
||||
|
||||
Reference in New Issue
Block a user