|
2 | 2 | // Licensed under the MIT license. |
3 | 3 |
|
4 | 4 | using System; |
5 | | -using System.Text.Json.Nodes; |
6 | | -using Microsoft.OpenApi.Reader; |
7 | 5 |
|
8 | 6 | namespace Microsoft.OpenApi |
9 | 7 | { |
@@ -39,101 +37,54 @@ public static class OpenApiDocumentRules |
39 | 37 | { |
40 | 38 | const string RuleName = nameof(OpenApiDocumentReferencesAreValid); |
41 | 39 |
|
42 | | - JsonNode document; |
43 | | - |
44 | | - using (var textWriter = new System.IO.StringWriter(System.Globalization.CultureInfo.InvariantCulture)) |
45 | | - { |
46 | | - var writer = new OpenApiJsonWriter(textWriter); |
47 | | - |
48 | | - item.SerializeAsV31(writer); |
49 | | - |
50 | | - var json = textWriter.ToString(); |
51 | | - |
52 | | - document = JsonNode.Parse(json)!; |
53 | | - } |
54 | | - |
55 | | - var visitor = new OpenApiSchemaReferenceVisitor(RuleName, context, document); |
| 40 | + var visitor = new OpenApiSchemaReferenceVisitor(RuleName, context); |
56 | 41 | var walker = new OpenApiWalker(visitor); |
57 | 42 |
|
58 | 43 | walker.Walk(item); |
59 | 44 | }); |
60 | 45 |
|
61 | 46 | private sealed class OpenApiSchemaReferenceVisitor( |
62 | 47 | string ruleName, |
63 | | - IValidationContext context, |
64 | | - JsonNode document) : OpenApiVisitorBase |
| 48 | + IValidationContext context) : OpenApiVisitorBase |
65 | 49 | { |
66 | 50 | public override void Visit(IOpenApiReferenceHolder referenceHolder) |
67 | 51 | { |
68 | | - if (referenceHolder is OpenApiSchemaReference { Reference.IsLocal: true } reference) |
| 52 | + if (referenceHolder is OpenApiSchemaReference reference) |
69 | 53 | { |
70 | 54 | ValidateSchemaReference(reference); |
71 | 55 | } |
72 | 56 | } |
73 | 57 |
|
74 | 58 | public override void Visit(IOpenApiSchema schema) |
75 | 59 | { |
76 | | - if (schema is OpenApiSchemaReference { Reference.IsLocal: true } reference) |
| 60 | + if (schema is OpenApiSchemaReference reference) |
77 | 61 | { |
78 | 62 | ValidateSchemaReference(reference); |
79 | 63 | } |
80 | 64 | } |
81 | 65 |
|
82 | 66 | private void ValidateSchemaReference(OpenApiSchemaReference reference) |
83 | 67 | { |
84 | | - try |
| 68 | + if (!reference.Reference.IsLocal) |
85 | 69 | { |
86 | | - if (reference.RecursiveTarget is not null) |
87 | | - { |
88 | | - // The reference was followed to a valid schema somewhere in the document |
89 | | - return; |
90 | | - } |
91 | | - } |
92 | | - catch (InvalidOperationException ex) |
93 | | - { |
94 | | - context.Enter(GetSegment()); |
95 | | - context.CreateWarning(ruleName, ex.Message); |
96 | | - context.Exit(); |
97 | | - |
98 | 70 | return; |
99 | 71 | } |
100 | 72 |
|
101 | | - var id = reference.Reference.ReferenceV3; |
102 | | - |
103 | | - if (id is { Length: > 0 } && !IsValidSchemaReference(id, document)) |
| 73 | + try |
104 | 74 | { |
105 | | - var isValid = false; |
106 | | - |
107 | | - // Sometimes ReferenceV3 is not a JSON valid JSON pointer, but the $ref |
108 | | - // associated with it still points to a valid location in the document. |
109 | | - // In these cases, we need to find it manually to verify that fact before |
110 | | - // generating a warning that the schema reference is indeed invalid. |
111 | | - // TODO Why is this, and can it be avoided? |
112 | | - var parent = Find(PathString, document); |
113 | | - |
114 | | - if (parent?["$ref"] is { } @ref && |
115 | | - @ref.GetValueKind() is System.Text.Json.JsonValueKind.String && |
116 | | - @ref.GetValue<string>() is { Length: > 0 } refId) |
117 | | - { |
118 | | - id = refId; |
119 | | - isValid = IsValidSchemaReference(id, document); |
120 | | - } |
121 | | - |
122 | | - if (!isValid) |
| 75 | + if (reference.RecursiveTarget is null) |
123 | 76 | { |
| 77 | + // The reference was not followed to a valid schema somewhere in the document |
124 | 78 | context.Enter(GetSegment()); |
125 | | - context.CreateWarning(ruleName, string.Format(SRResource.Validation_SchemaReferenceDoesNotExist, id)); |
| 79 | + context.CreateWarning(ruleName, string.Format(SRResource.Validation_SchemaReferenceDoesNotExist, reference.Reference.ReferenceV3)); |
126 | 80 | context.Exit(); |
127 | 81 | } |
128 | 82 | } |
129 | | - |
130 | | - static bool IsValidSchemaReference(string id, JsonNode baseNode) |
131 | | - => Find(id, baseNode) is not null; |
132 | | - |
133 | | - static JsonNode? Find(string id, JsonNode baseNode) |
| 83 | + catch (InvalidOperationException ex) |
134 | 84 | { |
135 | | - var pointer = new JsonPointer(id.Replace("#/", "/")); |
136 | | - return pointer.Find(baseNode); |
| 85 | + context.Enter(GetSegment()); |
| 86 | + context.CreateWarning(ruleName, ex.Message); |
| 87 | + context.Exit(); |
137 | 88 | } |
138 | 89 |
|
139 | 90 | string GetSegment() |
|
0 commit comments