From fda05d465ef84f2c4c755aca2252e2672ad40107 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 4 Feb 2025 12:18:22 -0500 Subject: [PATCH 01/10] fix: makes reference fields immutable --- .../Models/OpenApiReference.cs | 39 +++++++++++++------ .../Reader/V3/OpenApiV3VersionService.cs | 14 ++++--- .../Services/ReferenceHostDocumentSetter.cs | 5 ++- .../TryLoadReferenceV2Tests.cs | 6 ++- .../V3Tests/OpenApiDocumentTests.cs | 12 +++--- .../PublicApi/PublicApi.approved.txt | 12 +++--- 6 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiReference.cs b/src/Microsoft.OpenApi/Models/OpenApiReference.cs index c055dd072..ea614ae0a 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiReference.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiReference.cs @@ -4,14 +4,28 @@ using System; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Writers; +#if !NET5_0_OR_GREATER +namespace System.Runtime.CompilerServices { + using System.ComponentModel; + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal static class IsExternalInit { + } +} +#endif + namespace Microsoft.OpenApi.Models { /// /// A simple object to allow referencing other components in the specification, internally and externally. /// - public class OpenApiReference : IOpenApiSerializable + public class OpenApiReference : IOpenApiSerializable, IOpenApiDescribedElement, IOpenApiSummarizedElement { /// /// A short summary which by default SHOULD override that of the referenced component. @@ -32,13 +46,13 @@ public class OpenApiReference : IOpenApiSerializable /// 1. a absolute/relative file path, for example: ../commons/pet.json /// 2. a Url, for example: http://localhost/pet.json /// - public string ExternalResource { get; set; } + public string ExternalResource { get; init; } /// /// The element type referenced. /// /// This must be present if is not present. - public ReferenceType? Type { get; set; } + public ReferenceType? Type { get; init; } /// /// The identifier of the reusable component of one particular ReferenceType. @@ -47,7 +61,7 @@ public class OpenApiReference : IOpenApiSerializable /// If ExternalResource is not present, this is the name of the component without the reference type name. /// For example, if the reference is '#/components/schemas/componentName', the Id is 'componentName'. /// - public string Id { get; set; } + public string Id { get; init; } /// /// Gets a flag indicating whether this reference is an external reference. @@ -62,12 +76,12 @@ public class OpenApiReference : IOpenApiSerializable /// /// Gets a flag indicating whether a file is a valid OpenAPI document or a fragment /// - public bool IsFragment = false; + public bool IsFragment { get; init; } /// /// The OpenApiDocument that is hosting the OpenApiReference instance. This is used to enable dereferencing the reference. /// - public OpenApiDocument HostDocument { get; set; } + public OpenApiDocument HostDocument { get; init; } /// /// Gets the full reference string for v3.0. @@ -145,12 +159,13 @@ public OpenApiReference() { } /// public OpenApiReference(OpenApiReference reference) { - Summary = reference?.Summary; - Description = reference?.Description; - ExternalResource = reference?.ExternalResource; - Type = reference?.Type; - Id = reference?.Id; - HostDocument = reference?.HostDocument; + Utils.CheckArgumentNull(reference); + Summary = reference.Summary; + Description = reference.Description; + ExternalResource = reference.ExternalResource; + Type = reference.Type; + Id = reference.Id; + HostDocument = reference.HostDocument; } /// diff --git a/src/Microsoft.OpenApi/Reader/V3/OpenApiV3VersionService.cs b/src/Microsoft.OpenApi/Reader/V3/OpenApiV3VersionService.cs index ffb7431fc..c10bf6ddf 100644 --- a/src/Microsoft.OpenApi/Reader/V3/OpenApiV3VersionService.cs +++ b/src/Microsoft.OpenApi/Reader/V3/OpenApiV3VersionService.cs @@ -116,7 +116,7 @@ public OpenApiReference ConvertToOpenApiReference( } // Where fragments point into a non-OpenAPI document, the id will be the complete fragment identifier var id = segments[1]; - var openApiReference = new OpenApiReference(); + var isFragment = false; // $ref: externalSource.yaml#/Pet if (id.StartsWith("/components/", StringComparison.Ordinal)) @@ -152,12 +152,16 @@ public OpenApiReference ConvertToOpenApiReference( } else { - openApiReference.IsFragment = true; + isFragment = true; } - openApiReference.ExternalResource = segments[0]; - openApiReference.Type = type; - openApiReference.Id = id; + var openApiReference = new OpenApiReference + { + ExternalResource = segments[0], + Type = type, + Id = id, + IsFragment = isFragment, + }; return openApiReference; } diff --git a/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs b/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs index 146c8941d..7a9685ba4 100644 --- a/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs +++ b/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs @@ -23,7 +23,10 @@ public override void Visit(IOpenApiReferenceHolder referenceHolder) { if (referenceHolder.Reference != null) { - referenceHolder.Reference.HostDocument = _currentDocument; + referenceHolder.Reference = new OpenApiReference(referenceHolder.Reference) + { + HostDocument = _currentDocument, + }; } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs b/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs index 9d1400de6..2380c07e3 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs @@ -118,7 +118,11 @@ public async Task LoadResponseAndSchemaReference() } }; - ((OpenApiSchemaReference)expected.Content["application/json"].Schema).Reference.HostDocument = result.Document; + var schemaReference = (OpenApiSchemaReference)expected.Content["application/json"].Schema; + schemaReference.Reference = new OpenApiReference(schemaReference.Reference) + { + HostDocument = result.Document, + }; var actual = reference.Target; // Assert diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index c59aec6fb..44775f27d 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -1046,11 +1046,11 @@ public async Task ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() } }; - tagReference1.Reference.HostDocument = expected; - tagReference2.Reference.HostDocument = expected; - petSchemaReference.Reference.HostDocument = expected; - newPetSchemaReference.Reference.HostDocument = expected; - errorModelSchemaReference.Reference.HostDocument = expected; + tagReference1.Reference = new OpenApiReference(tagReference1.Reference) {HostDocument = expected }; + tagReference2.Reference = new OpenApiReference(tagReference2.Reference) {HostDocument = expected }; + petSchemaReference.Reference = new OpenApiReference(petSchemaReference.Reference) {HostDocument = expected }; + newPetSchemaReference.Reference = new OpenApiReference(newPetSchemaReference.Reference) {HostDocument = expected }; + errorModelSchemaReference.Reference = new OpenApiReference(errorModelSchemaReference.Reference) {HostDocument = expected }; actual.Document.Should().BeEquivalentTo(expected, options => options .IgnoringCyclicReferences() @@ -1284,7 +1284,7 @@ public async Task ParseDocWithRefsUsingProxyReferencesSucceeds() var outputDoc = (await doc.SerializeAsYamlAsync(OpenApiSpecVersion.OpenApi3_0)).MakeLineBreaksEnvironmentNeutral(); var expectedParam = expected.Paths["/pets"].Operations[OperationType.Get].Parameters[0]; var expectedParamReference = Assert.IsType(expectedParam); - expectedParamReference.Reference.HostDocument = doc; + expectedParamReference.Reference = new OpenApiReference(expectedParamReference.Reference) {HostDocument = doc}; var actualParamReference = Assert.IsType(actualParam); diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 343b8731c..dfaca710b 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -957,21 +957,21 @@ namespace Microsoft.OpenApi.Models public OpenApiPaths() { } public OpenApiPaths(Microsoft.OpenApi.Models.OpenApiPaths paths) { } } - public class OpenApiReference : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiSerializable + public class OpenApiReference : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Models.Interfaces.IOpenApiDescribedElement, Microsoft.OpenApi.Models.Interfaces.IOpenApiSummarizedElement { - public bool IsFragment; public OpenApiReference() { } public OpenApiReference(Microsoft.OpenApi.Models.OpenApiReference reference) { } public string Description { get; set; } - public string ExternalResource { get; set; } - public Microsoft.OpenApi.Models.OpenApiDocument HostDocument { get; set; } - public string Id { get; set; } public bool IsExternal { get; } public bool IsLocal { get; } public string ReferenceV2 { get; } public string ReferenceV3 { get; } public string Summary { get; set; } - public Microsoft.OpenApi.Models.ReferenceType? Type { get; set; } + public string ExternalResource { get; init; } + public Microsoft.OpenApi.Models.OpenApiDocument HostDocument { get; init; } + public string Id { get; init; } + public bool IsFragment { get; init; } + public Microsoft.OpenApi.Models.ReferenceType? Type { get; init; } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } From 89881fd5fa28148969eba75fad07ac26d4fb4e3d Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 4 Feb 2025 12:32:27 -0500 Subject: [PATCH 02/10] fix: makes target field read only Signed-off-by: Vincent Biret --- .../References/BaseOpenApiReferenceHolder.cs | 7 ++++--- .../Models/References/OpenApiTagReference.cs | 4 ++-- .../Models/OpenApiCallbackTests.cs | 6 +++--- .../Models/OpenApiExampleTests.cs | 6 +++--- .../Models/OpenApiHeaderTests.cs | 6 +++--- .../Models/OpenApiLinkTests.cs | 6 +++--- .../Models/OpenApiParameterTests.cs | 16 ++++++++-------- .../Models/OpenApiRequestBodyTests.cs | 6 +++--- .../Models/OpenApiResponseTests.cs | 14 +++++++------- .../Models/OpenApiSecuritySchemeTests.cs | 16 ++++++++-------- .../PublicApi/PublicApi.approved.txt | 2 +- 11 files changed, 45 insertions(+), 44 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs b/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs index 4a5da8025..1658a4fb4 100644 --- a/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs +++ b/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs @@ -13,14 +13,14 @@ public abstract class BaseOpenApiReferenceHolder : IOpenApiReferenceHolder /// /// The resolved target object. /// - protected T _target; + protected readonly T _target; /// public virtual T Target { get { - _target ??= Reference.HostDocument?.ResolveReferenceTo(Reference); - return _target; + if (_target is not null) return _target; + return Reference.HostDocument?.ResolveReferenceTo(Reference); } } /// @@ -36,6 +36,7 @@ protected BaseOpenApiReferenceHolder(BaseOpenApiReferenceHolder source) } private protected BaseOpenApiReferenceHolder(T target, string referenceId, ReferenceType referenceType) { + Utils.CheckArgumentNull(target); _target = target; Reference = new OpenApiReference() diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs index 70ca44dbc..2b7d3e727 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs @@ -21,8 +21,8 @@ public override OpenApiTag Target { get { - _target ??= Reference.HostDocument?.Tags.FirstOrDefault(t => StringComparer.Ordinal.Equals(t.Name, Reference.Id)); - return _target; + if (_target is not null) return _target; + return Reference.HostDocument?.Tags.FirstOrDefault(t => StringComparer.Ordinal.Equals(t.Name, Reference.Id)); } } diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs index 5600610de..f60fa3a1a 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.OpenApi.Tests.Models [Collection("DefaultSettings")] public class OpenApiCallbackTests { - public static OpenApiCallback AdvancedCallback = new() + private static OpenApiCallback AdvancedCallback => new() { PathItems = { @@ -54,9 +54,9 @@ public class OpenApiCallbackTests } }; - public static OpenApiCallbackReference CallbackProxy = new(ReferencedCallback, "simpleHook"); + private static OpenApiCallbackReference CallbackProxy => new(ReferencedCallback, "simpleHook"); - public static OpenApiCallback ReferencedCallback = new() + private static OpenApiCallback ReferencedCallback => new() { PathItems = { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs index d1e2cd8f5..438d2a2fe 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.OpenApi.Tests.Models [Collection("DefaultSettings")] public class OpenApiExampleTests { - public static OpenApiExample AdvancedExample = new() + private static OpenApiExample AdvancedExample => new() { Value = new JsonObject { @@ -57,8 +57,8 @@ public class OpenApiExampleTests } }; - public static OpenApiExampleReference OpenApiExampleReference = new(ReferencedExample, "example1"); - public static OpenApiExample ReferencedExample = new() + private static OpenApiExampleReference OpenApiExampleReference => new(ReferencedExample, "example1"); + private static OpenApiExample ReferencedExample => new() { Value = new JsonObject { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs index f6d4343cb..2bd3aa0c7 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.OpenApi.Tests.Models [Collection("DefaultSettings")] public class OpenApiHeaderTests { - public static OpenApiHeader AdvancedHeader = new() + private static OpenApiHeader AdvancedHeader => new() { Description = "sampleHeader", Schema = new OpenApiSchema() @@ -25,9 +25,9 @@ public class OpenApiHeaderTests } }; - public static OpenApiHeaderReference OpenApiHeaderReference = new(ReferencedHeader, "example1"); + private static OpenApiHeaderReference OpenApiHeaderReference => new(ReferencedHeader, "example1"); - public static OpenApiHeader ReferencedHeader = new() + private static OpenApiHeader ReferencedHeader => new() { Description = "sampleHeader", Schema = new OpenApiSchema() diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs index e97fbb6b8..c8bf27a29 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs @@ -18,7 +18,7 @@ namespace Microsoft.OpenApi.Tests.Models [Collection("DefaultSettings")] public class OpenApiLinkTests { - public static readonly OpenApiLink AdvancedLink = new() + private static OpenApiLink AdvancedLink => new() { OperationId = "operationId1", Parameters = @@ -42,8 +42,8 @@ public class OpenApiLinkTests } }; - public static readonly OpenApiLinkReference LinkReference = new(ReferencedLink, "example1"); - public static readonly OpenApiLink ReferencedLink = new() + private static OpenApiLinkReference LinkReference => new(ReferencedLink, "example1"); + private static OpenApiLink ReferencedLink => new() { OperationId = "operationId1", Parameters = diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs index 944920fab..bfbae32a8 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs @@ -19,20 +19,20 @@ namespace Microsoft.OpenApi.Tests.Models [Collection("DefaultSettings")] public class OpenApiParameterTests { - public static OpenApiParameter BasicParameter = new() + private static OpenApiParameter BasicParameter => new() { Name = "name1", In = ParameterLocation.Path }; - public static OpenApiParameterReference OpenApiParameterReference = new(ReferencedParameter, "example1"); - public static OpenApiParameter ReferencedParameter = new() + private static OpenApiParameterReference OpenApiParameterReference => new(ReferencedParameter, "example1"); + private static OpenApiParameter ReferencedParameter => new() { Name = "name1", In = ParameterLocation.Path }; - public static OpenApiParameter AdvancedPathParameterWithSchema = new() + private static OpenApiParameter AdvancedPathParameterWithSchema => new() { Name = "name1", In = ParameterLocation.Path, @@ -61,7 +61,7 @@ public class OpenApiParameterTests } }; - public static OpenApiParameter ParameterWithFormStyleAndExplodeFalse = new() + private static OpenApiParameter ParameterWithFormStyleAndExplodeFalse => new() { Name = "name1", In = ParameterLocation.Query, @@ -82,7 +82,7 @@ public class OpenApiParameterTests } }; - public static OpenApiParameter ParameterWithFormStyleAndExplodeTrue = new() + private static OpenApiParameter ParameterWithFormStyleAndExplodeTrue => new() { Name = "name1", In = ParameterLocation.Query, @@ -103,7 +103,7 @@ public class OpenApiParameterTests } }; - public static OpenApiParameter QueryParameterWithMissingStyle = new OpenApiParameter + private static OpenApiParameter QueryParameterWithMissingStyle => new OpenApiParameter { Name = "id", In = ParameterLocation.Query, @@ -117,7 +117,7 @@ public class OpenApiParameterTests } }; - public static OpenApiParameter AdvancedHeaderParameterWithSchemaTypeObject = new() + private static OpenApiParameter AdvancedHeaderParameterWithSchemaTypeObject => new() { Name = "name1", In = ParameterLocation.Header, diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs index 31d876b11..5ca281dae 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.OpenApi.Tests.Models [Collection("DefaultSettings")] public class OpenApiRequestBodyTests { - public static OpenApiRequestBody AdvancedRequestBody = new() + private static OpenApiRequestBody AdvancedRequestBody => new() { Description = "description", Required = true, @@ -31,8 +31,8 @@ public class OpenApiRequestBodyTests } }; - public static OpenApiRequestBodyReference OpenApiRequestBodyReference = new(ReferencedRequestBody, "example1"); - public static OpenApiRequestBody ReferencedRequestBody = new() + private static OpenApiRequestBodyReference OpenApiRequestBodyReference => new(ReferencedRequestBody, "example1"); + private static OpenApiRequestBody ReferencedRequestBody => new() { Description = "description", Required = true, diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs index 374d43772..1c4137d1f 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs @@ -20,9 +20,9 @@ namespace Microsoft.OpenApi.Tests.Models [Collection("DefaultSettings")] public class OpenApiResponseTests { - public static OpenApiResponse BasicResponse = new OpenApiResponse(); + private static OpenApiResponse BasicResponse => new OpenApiResponse(); - public static OpenApiResponse AdvancedV2Response = new OpenApiResponse + private static OpenApiResponse AdvancedV2Response => new OpenApiResponse { Description = "A complex object array response", Content = @@ -61,7 +61,7 @@ public class OpenApiResponseTests }, } }; - public static OpenApiResponse AdvancedV3Response = new OpenApiResponse + private static OpenApiResponse AdvancedV3Response => new OpenApiResponse { Description = "A complex object array response", Content = @@ -101,8 +101,8 @@ public class OpenApiResponseTests } }; - public static OpenApiResponseReference V2OpenApiResponseReference = new OpenApiResponseReference(ReferencedV2Response, "example1"); - public static OpenApiResponse ReferencedV2Response = new OpenApiResponse + private static OpenApiResponseReference V2OpenApiResponseReference => new OpenApiResponseReference(ReferencedV2Response, "example1"); + private static OpenApiResponse ReferencedV2Response => new OpenApiResponse { Description = "A complex object array response", Content = @@ -136,9 +136,9 @@ public class OpenApiResponseTests }, } }; - public static OpenApiResponseReference V3OpenApiResponseReference = new OpenApiResponseReference(ReferencedV3Response, "example1"); + private static OpenApiResponseReference V3OpenApiResponseReference => new OpenApiResponseReference(ReferencedV3Response, "example1"); - public static OpenApiResponse ReferencedV3Response = new OpenApiResponse + private static OpenApiResponse ReferencedV3Response => new OpenApiResponse { Description = "A complex object array response", Content = diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs index 780b2116a..991c31847 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs @@ -17,7 +17,7 @@ namespace Microsoft.OpenApi.Tests.Models [Collection("DefaultSettings")] public class OpenApiSecuritySchemeTests { - public static OpenApiSecurityScheme ApiKeySecurityScheme = new() + private static OpenApiSecurityScheme ApiKeySecurityScheme => new() { Description = "description1", Name = "parameterName", @@ -25,14 +25,14 @@ public class OpenApiSecuritySchemeTests In = ParameterLocation.Query, }; - public static OpenApiSecurityScheme HttpBasicSecurityScheme = new() + private static OpenApiSecurityScheme HttpBasicSecurityScheme => new() { Description = "description1", Type = SecuritySchemeType.Http, Scheme = OpenApiConstants.Basic }; - public static OpenApiSecurityScheme HttpBearerSecurityScheme = new() + private static OpenApiSecurityScheme HttpBearerSecurityScheme => new() { Description = "description1", Type = SecuritySchemeType.Http, @@ -40,7 +40,7 @@ public class OpenApiSecuritySchemeTests BearerFormat = OpenApiConstants.Jwt }; - public static OpenApiSecurityScheme OAuth2SingleFlowSecurityScheme = new() + private static OpenApiSecurityScheme OAuth2SingleFlowSecurityScheme => new() { Description = "description1", Type = SecuritySchemeType.OAuth2, @@ -58,7 +58,7 @@ public class OpenApiSecuritySchemeTests } }; - public static OpenApiSecurityScheme OAuth2MultipleFlowSecurityScheme = new() + private static OpenApiSecurityScheme OAuth2MultipleFlowSecurityScheme => new() { Description = "description1", Type = SecuritySchemeType.OAuth2, @@ -96,7 +96,7 @@ public class OpenApiSecuritySchemeTests } }; - public static OpenApiSecurityScheme OpenIdConnectSecurityScheme = new() + private static OpenApiSecurityScheme OpenIdConnectSecurityScheme => new() { Description = "description1", Type = SecuritySchemeType.OpenIdConnect, @@ -104,8 +104,8 @@ public class OpenApiSecuritySchemeTests OpenIdConnectUrl = new("https://example.com/openIdConnect") }; - public static OpenApiSecuritySchemeReference OpenApiSecuritySchemeReference = new(ReferencedSecurityScheme, "sampleSecurityScheme"); - public static OpenApiSecurityScheme ReferencedSecurityScheme = new() + private static OpenApiSecuritySchemeReference OpenApiSecuritySchemeReference => new(ReferencedSecurityScheme, "sampleSecurityScheme"); + private static OpenApiSecurityScheme ReferencedSecurityScheme => new() { Description = "description1", Type = SecuritySchemeType.OpenIdConnect, diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index dfaca710b..f8679c32d 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1240,7 +1240,7 @@ namespace Microsoft.OpenApi.Models.References where T : class, Microsoft.OpenApi.Interfaces.IOpenApiReferenceable, V where V : Microsoft.OpenApi.Interfaces.IOpenApiSerializable { - protected T _target; + protected readonly T _target; protected BaseOpenApiReferenceHolder(Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder source) { } protected BaseOpenApiReferenceHolder(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, Microsoft.OpenApi.Models.ReferenceType referenceType, string externalResource = null) { } public Microsoft.OpenApi.Models.OpenApiReference Reference { get; set; } From 92877e08d5689b06ead8a79bfdf6442858010dae Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 4 Feb 2025 12:36:42 -0500 Subject: [PATCH 03/10] docs: adds considerations for why the target is readonly Signed-off-by: Vincent Biret --- .../Models/References/BaseOpenApiReferenceHolder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs b/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs index 1658a4fb4..86b31fa06 100644 --- a/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs +++ b/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs @@ -11,7 +11,7 @@ namespace Microsoft.OpenApi.Models.References; public abstract class BaseOpenApiReferenceHolder : IOpenApiReferenceHolder where T : class, IOpenApiReferenceable, V where V : IOpenApiSerializable { /// - /// The resolved target object. + /// The resolved target object. This should remain readonly, otherwise mutating the reference will have side effects. /// protected readonly T _target; /// From a182f44bfb74ccbbb5b4bbf842693de48d60dac1 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 4 Feb 2025 13:02:18 -0500 Subject: [PATCH 04/10] fix: makes reference of holder immutable Signed-off-by: Vincent Biret --- .../Interfaces/IOpenApiReferenceHolder.cs | 2 +- src/Microsoft.OpenApi/Models/OpenApiReference.cs | 14 +++++++++++++- .../References/BaseOpenApiReferenceHolder.cs | 2 +- .../Services/ReferenceHostDocumentSetter.cs | 8 +------- .../ReferenceService/TryLoadReferenceV2Tests.cs | 6 +----- .../V3Tests/OpenApiDocumentTests.cs | 12 ++++++------ .../PublicApi/PublicApi.approved.txt | 4 ++-- 7 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceHolder.cs b/src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceHolder.cs index c244263f6..8883a90f5 100644 --- a/src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceHolder.cs +++ b/src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceHolder.cs @@ -34,6 +34,6 @@ public interface IOpenApiReferenceHolder : IOpenApiSerializable /// /// Reference object. /// - OpenApiReference Reference { get; set; } + OpenApiReference Reference { get; init; } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiReference.cs b/src/Microsoft.OpenApi/Models/OpenApiReference.cs index ea614ae0a..bed22a7c3 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiReference.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiReference.cs @@ -78,10 +78,11 @@ public class OpenApiReference : IOpenApiSerializable, IOpenApiDescribedElement, /// public bool IsFragment { get; init; } + private OpenApiDocument openApiDocument; /// /// The OpenApiDocument that is hosting the OpenApiReference instance. This is used to enable dereferencing the reference. /// - public OpenApiDocument HostDocument { get; init; } + public OpenApiDocument HostDocument { get => openApiDocument; init => openApiDocument = value; } /// /// Gets the full reference string for v3.0. @@ -291,5 +292,16 @@ private string GetReferenceTypeNameAsV2(ReferenceType type) // to indicate that the reference is not pointing to any object. }; } + + /// + /// Sets the host document after deserialization or before serialization. + /// This method is internal on purpose to avoid consumers mutating the host document. + /// + /// Host document to set if none is present + internal void EnsureHostDocumentIsSet(OpenApiDocument currentDocument) + { + Utils.CheckArgumentNull(currentDocument); + openApiDocument ??= currentDocument; + } } } diff --git a/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs b/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs index 86b31fa06..4d1eaf4c0 100644 --- a/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs +++ b/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs @@ -71,7 +71,7 @@ protected BaseOpenApiReferenceHolder(string referenceId, OpenApiDocument hostDoc /// public bool UnresolvedReference { get => Reference is null || Target is null; } /// - public OpenApiReference Reference { get; set; } + public OpenApiReference Reference { get; init; } /// public abstract V CopyReferenceAsTargetElementWithOverrides(V source); /// diff --git a/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs b/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs index 7a9685ba4..c660d21bd 100644 --- a/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs +++ b/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs @@ -21,13 +21,7 @@ public ReferenceHostDocumentSetter(OpenApiDocument currentDocument) /// public override void Visit(IOpenApiReferenceHolder referenceHolder) { - if (referenceHolder.Reference != null) - { - referenceHolder.Reference = new OpenApiReference(referenceHolder.Reference) - { - HostDocument = _currentDocument, - }; - } + referenceHolder.Reference?.EnsureHostDocumentIsSet(_currentDocument); } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs b/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs index 2380c07e3..9208fabd8 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs @@ -118,11 +118,7 @@ public async Task LoadResponseAndSchemaReference() } }; - var schemaReference = (OpenApiSchemaReference)expected.Content["application/json"].Schema; - schemaReference.Reference = new OpenApiReference(schemaReference.Reference) - { - HostDocument = result.Document, - }; + ((OpenApiSchemaReference)expected.Content["application/json"].Schema).Reference.EnsureHostDocumentIsSet(result.Document); var actual = reference.Target; // Assert diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index 44775f27d..6a5d80f0f 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -1046,11 +1046,11 @@ public async Task ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() } }; - tagReference1.Reference = new OpenApiReference(tagReference1.Reference) {HostDocument = expected }; - tagReference2.Reference = new OpenApiReference(tagReference2.Reference) {HostDocument = expected }; - petSchemaReference.Reference = new OpenApiReference(petSchemaReference.Reference) {HostDocument = expected }; - newPetSchemaReference.Reference = new OpenApiReference(newPetSchemaReference.Reference) {HostDocument = expected }; - errorModelSchemaReference.Reference = new OpenApiReference(errorModelSchemaReference.Reference) {HostDocument = expected }; + tagReference1.Reference.EnsureHostDocumentIsSet(expected); + tagReference2.Reference.EnsureHostDocumentIsSet(expected); + petSchemaReference.Reference.EnsureHostDocumentIsSet(expected); + newPetSchemaReference.Reference.EnsureHostDocumentIsSet(expected); + errorModelSchemaReference.Reference.EnsureHostDocumentIsSet(expected); actual.Document.Should().BeEquivalentTo(expected, options => options .IgnoringCyclicReferences() @@ -1284,7 +1284,7 @@ public async Task ParseDocWithRefsUsingProxyReferencesSucceeds() var outputDoc = (await doc.SerializeAsYamlAsync(OpenApiSpecVersion.OpenApi3_0)).MakeLineBreaksEnvironmentNeutral(); var expectedParam = expected.Paths["/pets"].Operations[OperationType.Get].Parameters[0]; var expectedParamReference = Assert.IsType(expectedParam); - expectedParamReference.Reference = new OpenApiReference(expectedParamReference.Reference) {HostDocument = doc}; + expectedParamReference.Reference.EnsureHostDocumentIsSet(doc); var actualParamReference = Assert.IsType(actualParam); diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index f8679c32d..5b6c34d8e 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -224,8 +224,8 @@ namespace Microsoft.OpenApi.Interfaces } public interface IOpenApiReferenceHolder : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { - Microsoft.OpenApi.Models.OpenApiReference Reference { get; set; } bool UnresolvedReference { get; } + Microsoft.OpenApi.Models.OpenApiReference Reference { get; init; } } public interface IOpenApiReferenceHolder : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiSerializable where out T : Microsoft.OpenApi.Interfaces.IOpenApiReferenceable, V @@ -1243,9 +1243,9 @@ namespace Microsoft.OpenApi.Models.References protected readonly T _target; protected BaseOpenApiReferenceHolder(Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder source) { } protected BaseOpenApiReferenceHolder(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, Microsoft.OpenApi.Models.ReferenceType referenceType, string externalResource = null) { } - public Microsoft.OpenApi.Models.OpenApiReference Reference { get; set; } public virtual T Target { get; } public bool UnresolvedReference { get; } + public Microsoft.OpenApi.Models.OpenApiReference Reference { get; init; } public abstract V CopyReferenceAsTargetElementWithOverrides(V source); public virtual void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public virtual void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } From ee6fae221b3f76045353ba8c33ff44e14318d3b9 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 4 Feb 2025 14:07:26 -0500 Subject: [PATCH 05/10] chore: linting Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi/IsExternalInit.cs | 13 +++++++++++++ .../Models/OpenApiReference.cs | 19 +++---------------- 2 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 src/Microsoft.OpenApi/IsExternalInit.cs diff --git a/src/Microsoft.OpenApi/IsExternalInit.cs b/src/Microsoft.OpenApi/IsExternalInit.cs new file mode 100644 index 000000000..9c8e2ad1a --- /dev/null +++ b/src/Microsoft.OpenApi/IsExternalInit.cs @@ -0,0 +1,13 @@ +//TODO remove this if we ever remove the netstandard2.0 target +#if !NET5_0_OR_GREATER +namespace System.Runtime.CompilerServices { + using System.ComponentModel; + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal static class IsExternalInit { + } +} +#endif diff --git a/src/Microsoft.OpenApi/Models/OpenApiReference.cs b/src/Microsoft.OpenApi/Models/OpenApiReference.cs index bed22a7c3..43d307fad 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiReference.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiReference.cs @@ -7,19 +7,6 @@ using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Writers; -#if !NET5_0_OR_GREATER -namespace System.Runtime.CompilerServices { - using System.ComponentModel; - /// - /// Reserved to be used by the compiler for tracking metadata. - /// This class should not be used by developers in source code. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - internal static class IsExternalInit { - } -} -#endif - namespace Microsoft.OpenApi.Models { /// @@ -78,11 +65,11 @@ public class OpenApiReference : IOpenApiSerializable, IOpenApiDescribedElement, /// public bool IsFragment { get; init; } - private OpenApiDocument openApiDocument; + private OpenApiDocument hostDocument; /// /// The OpenApiDocument that is hosting the OpenApiReference instance. This is used to enable dereferencing the reference. /// - public OpenApiDocument HostDocument { get => openApiDocument; init => openApiDocument = value; } + public OpenApiDocument HostDocument { get => hostDocument; init => hostDocument = value; } /// /// Gets the full reference string for v3.0. @@ -301,7 +288,7 @@ private string GetReferenceTypeNameAsV2(ReferenceType type) internal void EnsureHostDocumentIsSet(OpenApiDocument currentDocument) { Utils.CheckArgumentNull(currentDocument); - openApiDocument ??= currentDocument; + hostDocument ??= currentDocument; } } } From 317ad10fd88140e5a4640f132000cd7c3f514dd0 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 4 Feb 2025 15:20:52 -0500 Subject: [PATCH 06/10] chore: code linting Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi/Models/OpenApiMediaType.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs index 64917f95d..7ba469bc6 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs @@ -88,7 +88,7 @@ public void SerializeAsV3(IOpenApiWriter writer) private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version, Action callback) { - Utils.CheckArgumentNull(writer);; + Utils.CheckArgumentNull(writer); writer.WriteStartObject(); From 754f763c2b148c04f0ba11b9c8e948557cc91b14 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 4 Feb 2025 15:37:49 -0500 Subject: [PATCH 07/10] feat: makes document optional Signed-off-by: Vincent Biret --- .../Models/OpenApiDocument.cs | 7 ++ .../References/BaseOpenApiReferenceHolder.cs | 20 +--- .../References/OpenApiCallbackReference.cs | 6 +- .../References/OpenApiExampleReference.cs | 6 +- .../References/OpenApiHeaderReference.cs | 6 +- .../Models/References/OpenApiLinkReference.cs | 5 +- .../References/OpenApiParameterReference.cs | 6 +- .../References/OpenApiPathItemReference.cs | 6 +- .../References/OpenApiRequestBodyReference.cs | 5 +- .../References/OpenApiResponseReference.cs | 6 +- .../References/OpenApiSchemaReference.cs | 6 +- .../OpenApiSecuritySchemeReference.cs | 5 +- .../Models/References/OpenApiTagReference.cs | 12 +-- .../TryLoadReferenceV2Tests.cs | 16 +--- .../V3Tests/OpenApiDocumentTests.cs | 22 +++-- .../V3Tests/OpenApiParameterTests.cs | 2 +- .../Models/OpenApiCallbackTests.cs | 2 +- .../Models/OpenApiExampleTests.cs | 2 +- .../Models/OpenApiHeaderTests.cs | 2 +- .../Models/OpenApiLinkTests.cs | 2 +- .../Models/OpenApiOperationTests.cs | 95 ++++++++----------- .../Models/OpenApiParameterTests.cs | 2 +- .../Models/OpenApiRequestBodyTests.cs | 2 +- .../Models/OpenApiResponseTests.cs | 4 +- .../Models/OpenApiSecurityRequirementTests.cs | 76 ++++++++++----- .../Models/OpenApiSecuritySchemeTests.cs | 2 +- .../Models/OpenApiTagTests.cs | 2 +- .../PublicApi/PublicApi.approved.txt | 26 ++--- .../OpenApiReferenceValidationTests.cs | 4 +- .../Walkers/WalkerLocationTests.cs | 10 +- .../Workspaces/OpenApiWorkspaceTests.cs | 2 +- .../Writers/OpenApiYamlWriterTests.cs | 10 +- 32 files changed, 170 insertions(+), 209 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 0844edf1f..7820a6f8e 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -26,6 +26,13 @@ namespace Microsoft.OpenApi.Models /// public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable { + /// + /// Register components in the document to the workspace + /// + public void RegisterComponents() + { + Workspace?.RegisterComponents(this); + } /// /// Related workspace containing components that are referenced in a document /// diff --git a/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs b/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs index 4d1eaf4c0..e01545c61 100644 --- a/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs +++ b/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs @@ -10,16 +10,11 @@ namespace Microsoft.OpenApi.Models.References; /// The interface type for the model. public abstract class BaseOpenApiReferenceHolder : IOpenApiReferenceHolder where T : class, IOpenApiReferenceable, V where V : IOpenApiSerializable { - /// - /// The resolved target object. This should remain readonly, otherwise mutating the reference will have side effects. - /// - protected readonly T _target; /// public virtual T Target { get { - if (_target is not null) return _target; return Reference.HostDocument?.ResolveReferenceTo(Reference); } } @@ -34,17 +29,6 @@ protected BaseOpenApiReferenceHolder(BaseOpenApiReferenceHolder source) //no need to copy summary and description as if they are not overridden, they will be fetched from the target //if they are, the reference copy will handle it } - private protected BaseOpenApiReferenceHolder(T target, string referenceId, ReferenceType referenceType) - { - Utils.CheckArgumentNull(target); - _target = target; - - Reference = new OpenApiReference() - { - Id = referenceId, - Type = referenceType, - }; - } /// /// Constructor initializing the reference object. /// @@ -56,9 +40,11 @@ private protected BaseOpenApiReferenceHolder(T target, string referenceId, Refer /// 1. a absolute/relative file path, for example: ../commons/pet.json /// 2. a Url, for example: http://localhost/pet.json /// - protected BaseOpenApiReferenceHolder(string referenceId, OpenApiDocument hostDocument, ReferenceType referenceType, string externalResource = null) + protected BaseOpenApiReferenceHolder(string referenceId, OpenApiDocument hostDocument, ReferenceType referenceType, string externalResource) { Utils.CheckArgumentNullOrEmpty(referenceId); + // we're not checking for null hostDocument as it's optional and can be set via additional methods by a walker + // this way object initialization of a whole document is supported Reference = new OpenApiReference() { diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiCallbackReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiCallbackReference.cs index c9884877e..4c30328d4 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiCallbackReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiCallbackReference.cs @@ -25,7 +25,7 @@ public class OpenApiCallbackReference : BaseOpenApiReferenceHolder - public OpenApiCallbackReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Callback, externalResource) + public OpenApiCallbackReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Callback, externalResource) { } /// @@ -37,10 +37,6 @@ private OpenApiCallbackReference(OpenApiCallbackReference callback):base(callbac } - internal OpenApiCallbackReference(OpenApiCallback target, string referenceId):base(target, referenceId, ReferenceType.Callback) - { - } - /// public Dictionary PathItems { get => Target?.PathItems; } diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiExampleReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiExampleReference.cs index 41c2109cb..edfe27b61 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiExampleReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiExampleReference.cs @@ -25,7 +25,7 @@ public class OpenApiExampleReference : BaseOpenApiReferenceHolder - public OpenApiExampleReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Example, externalResource) + public OpenApiExampleReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Example, externalResource) { } /// @@ -36,10 +36,6 @@ private OpenApiExampleReference(OpenApiExampleReference example):base(example) { } - internal OpenApiExampleReference(OpenApiExample target, string referenceId):base(target, referenceId, ReferenceType.Example) - { - } - /// public string Description { diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiHeaderReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiHeaderReference.cs index c62aa9f00..719cdce3a 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiHeaderReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiHeaderReference.cs @@ -24,7 +24,7 @@ public class OpenApiHeaderReference : BaseOpenApiReferenceHolder - public OpenApiHeaderReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Header, externalResource) + public OpenApiHeaderReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Header, externalResource) { } @@ -36,10 +36,6 @@ private OpenApiHeaderReference(OpenApiHeaderReference header):base(header) { } - internal OpenApiHeaderReference(OpenApiHeader target, string referenceId):base(target, referenceId, ReferenceType.Header) - { - } - /// public string Description { diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiLinkReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiLinkReference.cs index c658f32fc..f91b5711b 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiLinkReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiLinkReference.cs @@ -24,7 +24,7 @@ public class OpenApiLinkReference : BaseOpenApiReferenceHolder - public OpenApiLinkReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Link, externalResource) + public OpenApiLinkReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Link, externalResource) { } /// @@ -34,9 +34,6 @@ public OpenApiLinkReference(string referenceId, OpenApiDocument hostDocument, st private OpenApiLinkReference(OpenApiLinkReference reference):base(reference) { } - internal OpenApiLinkReference(OpenApiLink target, string referenceId):base(target, referenceId, ReferenceType.Link) - { - } /// public string Description diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiParameterReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiParameterReference.cs index 59929ea14..d337b841e 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiParameterReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiParameterReference.cs @@ -23,7 +23,7 @@ public class OpenApiParameterReference : BaseOpenApiReferenceHolder - public OpenApiParameterReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Parameter, externalResource) + public OpenApiParameterReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Parameter, externalResource) { } @@ -35,10 +35,6 @@ private OpenApiParameterReference(OpenApiParameterReference parameter):base(para { } - internal OpenApiParameterReference(OpenApiParameter target, string referenceId):base(target, referenceId, ReferenceType.Parameter) - { - } - /// public string Name { get => Target?.Name; } diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiPathItemReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiPathItemReference.cs index 8ee78384b..038e1cb13 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiPathItemReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiPathItemReference.cs @@ -24,7 +24,7 @@ public class OpenApiPathItemReference : BaseOpenApiReferenceHolder - public OpenApiPathItemReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null): base(referenceId, hostDocument, ReferenceType.PathItem, externalResource) + public OpenApiPathItemReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null): base(referenceId, hostDocument, ReferenceType.PathItem, externalResource) { } @@ -37,10 +37,6 @@ private OpenApiPathItemReference(OpenApiPathItemReference pathItem):base(pathIte } - internal OpenApiPathItemReference(OpenApiPathItem target, string referenceId):base(target, referenceId, ReferenceType.PathItem) - { - } - /// public string Summary { diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiRequestBodyReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiRequestBodyReference.cs index dc6ca082c..966d3aad1 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiRequestBodyReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiRequestBodyReference.cs @@ -25,7 +25,7 @@ public class OpenApiRequestBodyReference : BaseOpenApiReferenceHolder - public OpenApiRequestBodyReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.RequestBody, externalResource) + public OpenApiRequestBodyReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.RequestBody, externalResource) { } /// @@ -35,9 +35,6 @@ public OpenApiRequestBodyReference(string referenceId, OpenApiDocument hostDocum private OpenApiRequestBodyReference(OpenApiRequestBodyReference openApiRequestBodyReference):base(openApiRequestBodyReference) { - } - internal OpenApiRequestBodyReference(OpenApiRequestBody target, string referenceId):base(target, referenceId, ReferenceType.RequestBody) - { } /// diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiResponseReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiResponseReference.cs index c4ddf59d7..9fbfb47a0 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiResponseReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiResponseReference.cs @@ -23,7 +23,7 @@ public class OpenApiResponseReference : BaseOpenApiReferenceHolder - public OpenApiResponseReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Response, externalResource) + public OpenApiResponseReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Response, externalResource) { } /// @@ -35,10 +35,6 @@ private OpenApiResponseReference(OpenApiResponseReference openApiResponseReferen } - internal OpenApiResponseReference(OpenApiResponse target, string referenceId):base(target, referenceId, ReferenceType.Response) - { - } - /// public string Description { diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs index f0c9a9f47..9252d6b89 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs @@ -25,7 +25,7 @@ public class OpenApiSchemaReference : BaseOpenApiReferenceHolder - public OpenApiSchemaReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Schema, externalResource) + public OpenApiSchemaReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Schema, externalResource) { } /// @@ -36,10 +36,6 @@ private OpenApiSchemaReference(OpenApiSchemaReference schema):base(schema) { } - internal OpenApiSchemaReference(OpenApiSchema target, string referenceId):base(target, referenceId, ReferenceType.Schema) - { - } - /// public string Description { diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiSecuritySchemeReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiSecuritySchemeReference.cs index dd379c808..75ca30573 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiSecuritySchemeReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiSecuritySchemeReference.cs @@ -19,7 +19,7 @@ public class OpenApiSecuritySchemeReference : BaseOpenApiReferenceHolderThe reference Id. /// The host OpenAPI document. /// The externally referenced file. - public OpenApiSecuritySchemeReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.SecurityScheme, externalResource) + public OpenApiSecuritySchemeReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.SecurityScheme, externalResource) { } /// @@ -29,9 +29,6 @@ public OpenApiSecuritySchemeReference(string referenceId, OpenApiDocument hostDo private OpenApiSecuritySchemeReference(OpenApiSecuritySchemeReference openApiSecuritySchemeReference):base(openApiSecuritySchemeReference) { - } - internal OpenApiSecuritySchemeReference(OpenApiSecurityScheme target, string referenceId):base(target, referenceId, ReferenceType.SecurityScheme) - { } /// diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs index 2b7d3e727..6f218fc13 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs @@ -21,7 +21,6 @@ public override OpenApiTag Target { get { - if (_target is not null) return _target; return Reference.HostDocument?.Tags.FirstOrDefault(t => StringComparer.Ordinal.Equals(t.Name, Reference.Id)); } } @@ -31,7 +30,12 @@ public override OpenApiTag Target /// /// The reference Id. /// The host OpenAPI document. - public OpenApiTagReference(string referenceId, OpenApiDocument hostDocument):base(referenceId, hostDocument, ReferenceType.Tag) + /// Optional: External resource in the reference. + /// It may be: + /// 1. a absolute/relative file path, for example: ../commons/pet.json + /// 2. a Url, for example: http://localhost/pet.json + /// + public OpenApiTagReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Tag, externalResource) { } /// @@ -43,10 +47,6 @@ private OpenApiTagReference(OpenApiTagReference openApiTagReference):base(openAp } - internal OpenApiTagReference(OpenApiTag target, string referenceId):base(target, referenceId, ReferenceType.Tag) - { - } - /// public string Description { diff --git a/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs b/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs index 9208fabd8..3edc9ac67 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs @@ -99,21 +99,7 @@ public async Task LoadResponseAndSchemaReference() { ["application/json"] = new() { - Schema = new OpenApiSchemaReference(new OpenApiSchema() - { - Description = "Sample description", - Required = new HashSet {"name" }, - Properties = { - ["name"] = new OpenApiSchema() - { - Type = JsonSchemaType.String - }, - ["tag"] = new OpenApiSchema() - { - Type = JsonSchemaType.String - } - }, - }, "SampleObject2") + Schema = new OpenApiSchemaReference("SampleObject2") } } }; diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index 6a5d80f0f..ccf3a9407 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -684,21 +684,21 @@ public async Task ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() var petSchemaSource = Assert.IsType(components.Schemas["pet1"]); var petSchema = await CloneAsync(petSchemaSource); var castPetSchema = Assert.IsType(petSchema); - var petSchemaReference = new OpenApiSchemaReference(castPetSchema, "pet1"); + var petSchemaReference = new OpenApiSchemaReference("pet1"); var newPetSchemaSource = Assert.IsType(components.Schemas["newPet"]); var newPetSchema = await CloneAsync(newPetSchemaSource); var castNewPetSchema = Assert.IsType(newPetSchema); - var newPetSchemaReference = new OpenApiSchemaReference(castNewPetSchema, "newPet"); + var newPetSchemaReference = new OpenApiSchemaReference("newPet"); var errorModelSchemaSource = Assert.IsType(components.Schemas["errorModel"]); var errorModelSchema = await CloneAsync(errorModelSchemaSource); var castErrorModelSchema = Assert.IsType(errorModelSchema); - var errorModelSchemaReference = new OpenApiSchemaReference(castErrorModelSchema, "errorModel"); + var errorModelSchemaReference = new OpenApiSchemaReference("errorModel"); - var tagReference1 = new OpenApiTagReference("tagName1", null); + var tagReference1 = new OpenApiTagReference("tagName1"); - var tagReference2 = new OpenApiTagReference("tagName2", null); + var tagReference2 = new OpenApiTagReference("tagName2"); var securityScheme1Cast = Assert.IsType(components.SecuritySchemes["securitySchemeName1"]); var securityScheme1 = await CloneSecuritySchemeAsync(securityScheme1Cast); @@ -889,8 +889,8 @@ public async Task ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() { new OpenApiSecurityRequirement { - [new OpenApiSecuritySchemeReference(securityScheme1, "securitySchemeName1")] = new List(), - [new OpenApiSecuritySchemeReference(securityScheme2, "securitySchemeName2")] = new List + [new OpenApiSecuritySchemeReference("securitySchemeName1")] = new List(), + [new OpenApiSecuritySchemeReference("securitySchemeName2")] = new List { "scope1", "scope2" @@ -1035,8 +1035,8 @@ public async Task ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() { new OpenApiSecurityRequirement { - [new OpenApiSecuritySchemeReference(securityScheme1, "securitySchemeName1")] = new List(), - [new OpenApiSecuritySchemeReference(securityScheme2, "securitySchemeName2")] = new List + [new OpenApiSecuritySchemeReference("securitySchemeName1")] = new List(), + [new OpenApiSecuritySchemeReference("securitySchemeName2")] = new List { "scope1", "scope2", @@ -1045,6 +1045,8 @@ public async Task ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() } } }; + expected.RegisterComponents(); + expected.SetReferenceHostDocument(); tagReference1.Reference.EnsureHostDocumentIsSet(expected); tagReference2.Reference.EnsureHostDocumentIsSet(expected); @@ -1238,7 +1240,7 @@ public async Task ParseDocWithRefsUsingProxyReferencesSucceeds() Summary = "Returns all pets", Parameters = [ - new OpenApiParameterReference(parameter, "LimitParameter"), + new OpenApiParameterReference("LimitParameter"), ], Responses = new OpenApiResponses() } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiParameterTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiParameterTests.cs index 2ee63165c..efdb87110 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiParameterTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiParameterTests.cs @@ -343,7 +343,7 @@ public void ParseParameterWithReferenceWorks() OperationId = "findPets", Parameters = [ - new OpenApiParameterReference (parameter, "tagsParameter"), + new OpenApiParameterReference("tagsParameter"), ], } } diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs index f60fa3a1a..fc232fa3a 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs @@ -54,7 +54,7 @@ public class OpenApiCallbackTests } }; - private static OpenApiCallbackReference CallbackProxy => new(ReferencedCallback, "simpleHook"); + private static OpenApiCallbackReference CallbackProxy => new("simpleHook"); private static OpenApiCallback ReferencedCallback => new() { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs index 438d2a2fe..fd59b4250 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs @@ -57,7 +57,7 @@ public class OpenApiExampleTests } }; - private static OpenApiExampleReference OpenApiExampleReference => new(ReferencedExample, "example1"); + private static OpenApiExampleReference OpenApiExampleReference => new("example1"); private static OpenApiExample ReferencedExample => new() { Value = new JsonObject diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs index 2bd3aa0c7..afda460f7 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs @@ -25,7 +25,7 @@ public class OpenApiHeaderTests } }; - private static OpenApiHeaderReference OpenApiHeaderReference => new(ReferencedHeader, "example1"); + private static OpenApiHeaderReference OpenApiHeaderReference => new("example1"); private static OpenApiHeader ReferencedHeader => new() { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs index c8bf27a29..a6b4bc500 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs @@ -42,7 +42,7 @@ public class OpenApiLinkTests } }; - private static OpenApiLinkReference LinkReference => new(ReferencedLink, "example1"); + private static OpenApiLinkReference LinkReference => new("example1"); private static OpenApiLink ReferencedLink => new() { OperationId = "operationId1", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs index 138888d71..af22284b9 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System.Collections.Generic; +using System.Text.Json.Nodes; using System.Threading.Tasks; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; @@ -58,21 +59,7 @@ public class OpenApiOperationTests }, Responses = new() { - ["200"] = new OpenApiResponseReference(new OpenApiResponse() - { - Content = new Dictionary - { - ["application/json"] = new() - { - Schema = new OpenApiSchema() - { - Type = JsonSchemaType.Number, - Minimum = 5, - Maximum = 10 - } - } - } - }, "response1"), + ["200"] = new OpenApiResponseReference("response1"), ["400"] = new OpenApiResponse() { Content = new Dictionary @@ -100,7 +87,7 @@ public class OpenApiOperationTests Annotations = new Dictionary { { "key1", "value1" }, { "key2", 2 } }, }; - private static readonly OpenApiOperation _advancedOperationWithTagsAndSecurity = new() + private static OpenApiOperation _advancedOperationWithTagsAndSecurity => new() { Tags = new List { @@ -146,21 +133,7 @@ public class OpenApiOperationTests }, Responses = new() { - ["200"] = new OpenApiResponseReference(new OpenApiResponse() - { - Content = new Dictionary - { - ["application/json"] = new() - { - Schema = new OpenApiSchema() - { - Type = JsonSchemaType.Number, - Minimum = 5, - Maximum = 10 - } - } - } - }, "response1"), + ["200"] = new OpenApiResponseReference("response1"), ["400"] = new OpenApiResponse() { Content = new Dictionary @@ -181,8 +154,8 @@ public class OpenApiOperationTests { new() { - [new OpenApiSecuritySchemeReference(new OpenApiSecurityScheme(), "securitySchemeId1")] = new List(), - [new OpenApiSecuritySchemeReference(new OpenApiSecurityScheme(), "securitySchemeId2")] = new List + [new OpenApiSecuritySchemeReference("securitySchemeId1", __advancedOperationWithTagsAndSecurity_supportingDocument)] = new List(), + [new OpenApiSecuritySchemeReference("securitySchemeId2", __advancedOperationWithTagsAndSecurity_supportingDocument)] = new List { "scopeName1", "scopeName2" @@ -198,6 +171,34 @@ public class OpenApiOperationTests } } }; + private static OpenApiDocument __advancedOperationWithTagsAndSecurity_supportingDocument + { + get + { + var document = new OpenApiDocument() + { + Components = new() + { + SecuritySchemes = new Dictionary + { + ["securitySchemeId1"] = new OpenApiSecurityScheme + { + Type = SecuritySchemeType.ApiKey, + Name = "apiKeyName1", + In = ParameterLocation.Header, + }, + ["securitySchemeId2"] = new OpenApiSecurityScheme + { + Type = SecuritySchemeType.OpenIdConnect, + OpenIdConnectUrl = new("http://example.com"), + } + } + } + }; + document.RegisterComponents(); + return document; + } + } private static readonly OpenApiOperation _operationWithFormData = new() @@ -455,9 +456,7 @@ public async Task SerializeAdvancedOperationWithTagAndSecurityAsV3JsonWorks() var actual = await _advancedOperationWithTagsAndSecurity.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } [Fact] @@ -475,9 +474,7 @@ public async Task SerializeBasicOperationAsV2JsonWorks() var actual = await _basicOperation.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } [Fact] @@ -554,9 +551,7 @@ public async Task SerializeOperationWithFormDataAsV3JsonWorks() var actual = await _operationWithFormData.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } [Fact] @@ -610,9 +605,7 @@ public async Task SerializeOperationWithFormDataAsV2JsonWorks() var actual = await _operationWithFormData.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } [Fact] @@ -679,9 +672,7 @@ public async Task SerializeOperationWithBodyAsV2JsonWorks() var actual = await _operationWithBody.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } [Fact] @@ -760,9 +751,7 @@ public async Task SerializeAdvancedOperationWithTagAndSecurityAsV2JsonWorks() var actual = await _advancedOperationWithTagsAndSecurity.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } [Fact] @@ -785,9 +774,7 @@ public async Task SerializeOperationWithNullCollectionAsV2JsonWorks() var actual = await operation.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } [Fact] diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs index bfbae32a8..da0c00d44 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs @@ -25,7 +25,7 @@ public class OpenApiParameterTests In = ParameterLocation.Path }; - private static OpenApiParameterReference OpenApiParameterReference => new(ReferencedParameter, "example1"); + private static OpenApiParameterReference OpenApiParameterReference => new("example1"); private static OpenApiParameter ReferencedParameter => new() { Name = "name1", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs index 5ca281dae..863ce5145 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs @@ -31,7 +31,7 @@ public class OpenApiRequestBodyTests } }; - private static OpenApiRequestBodyReference OpenApiRequestBodyReference => new(ReferencedRequestBody, "example1"); + private static OpenApiRequestBodyReference OpenApiRequestBodyReference => new("example1"); private static OpenApiRequestBody ReferencedRequestBody => new() { Description = "description", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs index 1c4137d1f..7d077b540 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs @@ -101,7 +101,7 @@ public class OpenApiResponseTests } }; - private static OpenApiResponseReference V2OpenApiResponseReference => new OpenApiResponseReference(ReferencedV2Response, "example1"); + private static OpenApiResponseReference V2OpenApiResponseReference => new OpenApiResponseReference("example1"); private static OpenApiResponse ReferencedV2Response => new OpenApiResponse { Description = "A complex object array response", @@ -136,7 +136,7 @@ public class OpenApiResponseTests }, } }; - private static OpenApiResponseReference V3OpenApiResponseReference => new OpenApiResponseReference(ReferencedV3Response, "example1"); + private static OpenApiResponseReference V3OpenApiResponseReference => new OpenApiResponseReference("example1"); private static OpenApiResponse ReferencedV3Response => new OpenApiResponse { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.cs index 19900f215..cfbe0ae50 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.cs @@ -13,6 +13,8 @@ using VerifyXunit; using Microsoft.OpenApi.Models.References; using Xunit; +using System.Text.Json.Nodes; +using Microsoft.OpenApi.Models.Interfaces; namespace Microsoft.OpenApi.Tests.Models { @@ -25,7 +27,7 @@ public class OpenApiSecurityRequirementTests new() { [ - new OpenApiSecuritySchemeReference(new OpenApiSecurityScheme(), "scheme1") + new OpenApiSecuritySchemeReference("scheme1", SecurityRequirementWithReferencedSecurityScheme_supportingDocument) ] = new List { "scope1", @@ -33,22 +35,56 @@ public class OpenApiSecurityRequirementTests "scope3", }, [ - new OpenApiSecuritySchemeReference(new OpenApiSecurityScheme(), "scheme2") + new OpenApiSecuritySchemeReference("scheme2", SecurityRequirementWithReferencedSecurityScheme_supportingDocument) ] = new List { "scope4", "scope5", }, [ - new OpenApiSecuritySchemeReference(new OpenApiSecurityScheme(), "scheme3") + new OpenApiSecuritySchemeReference("scheme3", SecurityRequirementWithReferencedSecurityScheme_supportingDocument) ] = new List() }; + public static OpenApiDocument SecurityRequirementWithReferencedSecurityScheme_supportingDocument + { + get + { + var document = new OpenApiDocument() + { + Components = new() + { + SecuritySchemes = new Dictionary + { + ["scheme1"] = new OpenApiSecurityScheme + { + Type = SecuritySchemeType.ApiKey, + Name = "apiKeyName1", + In = ParameterLocation.Header, + }, + ["scheme2"] = new OpenApiSecurityScheme + { + Type = SecuritySchemeType.OpenIdConnect, + OpenIdConnectUrl = new("http://example.com"), + }, + ["scheme3"] = new OpenApiSecurityScheme + { + Type = SecuritySchemeType.Http, + Scheme = "bearer", + BearerFormat = "JWT", + }, + } + } + }; + document.RegisterComponents(); + return document; + } + } public static OpenApiSecurityRequirement SecurityRequirementWithUnreferencedSecurityScheme = new() { [ - new OpenApiSecuritySchemeReference(new OpenApiSecurityScheme(), "scheme1") + new OpenApiSecuritySchemeReference("scheme1", SecurityRequirementWithReferencedSecurityScheme_supportingDocument) ] = new List { "scope1", @@ -56,14 +92,14 @@ public class OpenApiSecurityRequirementTests "scope3", }, [ - new OpenApiSecuritySchemeReference("brokenUnreferencedScheme", hostDocument: null) + new OpenApiSecuritySchemeReference("brokenUnreferencedScheme", SecurityRequirementWithReferencedSecurityScheme_supportingDocument) ] = new List { "scope4", "scope5", }, [ - new OpenApiSecuritySchemeReference(new OpenApiSecurityScheme(), "scheme3") + new OpenApiSecuritySchemeReference("scheme3", SecurityRequirementWithReferencedSecurityScheme_supportingDocument) ] = new List() }; @@ -123,9 +159,7 @@ public async Task SerializeSecurityRequirementWithReferencedSecuritySchemeAsV3Js var actual = await SecurityRequirementWithReferencedSecurityScheme.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } [Fact] @@ -152,9 +186,7 @@ public async Task SerializeSecurityRequirementWithReferencedSecuritySchemeAsV2Js var actual = await SecurityRequirementWithReferencedSecurityScheme.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } [Fact] @@ -177,9 +209,7 @@ public async Task SerializeSecurityRequirementWithUnreferencedSecuritySchemeAsV3 var actual = await SecurityRequirementWithUnreferencedSecurityScheme.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } [Fact] @@ -203,9 +233,7 @@ public async Task SerializeSecurityRequirementWithUnreferencedSecuritySchemeAsV2 await SecurityRequirementWithUnreferencedSecurityScheme.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } [Fact] @@ -242,13 +270,13 @@ public void SchemesShouldConsiderOnlyReferenceIdForEquality() }; // Act - securityRequirement.Add(new OpenApiSecuritySchemeReference(securityScheme1, "securityScheme1"), new List()); - securityRequirement.Add(new OpenApiSecuritySchemeReference(securityScheme2, "securityScheme2"), new List { "scope1", "scope2" }); + securityRequirement.Add(new OpenApiSecuritySchemeReference("securityScheme1"), new List()); + securityRequirement.Add(new OpenApiSecuritySchemeReference("securityScheme2"), new List { "scope1", "scope2" }); var addSecurityScheme1Duplicate = () => - securityRequirement.Add(new OpenApiSecuritySchemeReference(securityScheme1Duplicate, "securityScheme1"), new List()); + securityRequirement.Add(new OpenApiSecuritySchemeReference("securityScheme1"), new List()); var addSecurityScheme1WithDifferentProperties = () => - securityRequirement.Add(new OpenApiSecuritySchemeReference(securityScheme1WithDifferentProperties, "securityScheme1"), new List()); + securityRequirement.Add(new OpenApiSecuritySchemeReference("securityScheme1"), new List()); // Assert // Only the first two should be added successfully since the latter two are duplicates of securityScheme1. @@ -263,8 +291,8 @@ public void SchemesShouldConsiderOnlyReferenceIdForEquality() { // This should work with any security scheme object // as long as Reference.Id os securityScheme1 - [new OpenApiSecuritySchemeReference(securityScheme1WithDifferentProperties, "securityScheme1")] = new List(), - [new OpenApiSecuritySchemeReference(securityScheme2, "securityScheme2")] = new List { "scope1", "scope2" }, + [new OpenApiSecuritySchemeReference("securityScheme1", null)] = new List(), + [new OpenApiSecuritySchemeReference("securityScheme2", null)] = new List { "scope1", "scope2" }, }); } } diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs index 991c31847..12db6c1e8 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs @@ -104,7 +104,7 @@ public class OpenApiSecuritySchemeTests OpenIdConnectUrl = new("https://example.com/openIdConnect") }; - private static OpenApiSecuritySchemeReference OpenApiSecuritySchemeReference => new(ReferencedSecurityScheme, "sampleSecurityScheme"); + private static OpenApiSecuritySchemeReference OpenApiSecuritySchemeReference => new("sampleSecurityScheme"); private static OpenApiSecurityScheme ReferencedSecurityScheme => new() { Description = "description1", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs index c987592d4..73ac0d0b7 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs @@ -31,7 +31,7 @@ public class OpenApiTagTests } }; - public static IOpenApiTag ReferencedTag = new OpenApiTagReference(AdvancedTag, "pet"); + public static IOpenApiTag ReferencedTag = new OpenApiTagReference("pet"); [Theory] [InlineData(true)] diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 5b6c34d8e..a667f6ef3 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -724,6 +724,7 @@ namespace Microsoft.OpenApi.Models public Microsoft.OpenApi.Services.OpenApiWorkspace? Workspace { get; set; } public bool AddComponent(string id, T componentToRegister) { } public System.Threading.Tasks.Task GetHashCodeAsync(System.Threading.CancellationToken cancellationToken = default) { } + public void RegisterComponents() { } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } @@ -1240,9 +1241,8 @@ namespace Microsoft.OpenApi.Models.References where T : class, Microsoft.OpenApi.Interfaces.IOpenApiReferenceable, V where V : Microsoft.OpenApi.Interfaces.IOpenApiSerializable { - protected readonly T _target; protected BaseOpenApiReferenceHolder(Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder source) { } - protected BaseOpenApiReferenceHolder(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, Microsoft.OpenApi.Models.ReferenceType referenceType, string externalResource = null) { } + protected BaseOpenApiReferenceHolder(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, Microsoft.OpenApi.Models.ReferenceType referenceType, string externalResource) { } public virtual T Target { get; } public bool UnresolvedReference { get; } public Microsoft.OpenApi.Models.OpenApiReference Reference { get; init; } @@ -1253,7 +1253,7 @@ namespace Microsoft.OpenApi.Models.References } public class OpenApiCallbackReference : Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReadOnlyExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Interfaces.IShallowCopyable, Microsoft.OpenApi.Models.Interfaces.IOpenApiCallback { - public OpenApiCallbackReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, string externalResource = null) { } + public OpenApiCallbackReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument = null, string externalResource = null) { } public System.Collections.Generic.IDictionary Extensions { get; } public System.Collections.Generic.Dictionary PathItems { get; } public override Microsoft.OpenApi.Models.Interfaces.IOpenApiCallback CopyReferenceAsTargetElementWithOverrides(Microsoft.OpenApi.Models.Interfaces.IOpenApiCallback source) { } @@ -1262,7 +1262,7 @@ namespace Microsoft.OpenApi.Models.References } public class OpenApiExampleReference : Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReadOnlyExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Interfaces.IShallowCopyable, Microsoft.OpenApi.Models.Interfaces.IOpenApiDescribedElement, Microsoft.OpenApi.Models.Interfaces.IOpenApiExample, Microsoft.OpenApi.Models.Interfaces.IOpenApiSummarizedElement { - public OpenApiExampleReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, string externalResource = null) { } + public OpenApiExampleReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument = null, string externalResource = null) { } public string Description { get; set; } public System.Collections.Generic.IDictionary Extensions { get; } public string ExternalValue { get; } @@ -1274,7 +1274,7 @@ namespace Microsoft.OpenApi.Models.References } public class OpenApiHeaderReference : Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReadOnlyExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Interfaces.IShallowCopyable, Microsoft.OpenApi.Models.Interfaces.IOpenApiDescribedElement, Microsoft.OpenApi.Models.Interfaces.IOpenApiHeader { - public OpenApiHeaderReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, string externalResource = null) { } + public OpenApiHeaderReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument = null, string externalResource = null) { } public bool AllowEmptyValue { get; } public bool AllowReserved { get; } public System.Collections.Generic.IDictionary Content { get; } @@ -1292,7 +1292,7 @@ namespace Microsoft.OpenApi.Models.References } public class OpenApiLinkReference : Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReadOnlyExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Interfaces.IShallowCopyable, Microsoft.OpenApi.Models.Interfaces.IOpenApiDescribedElement, Microsoft.OpenApi.Models.Interfaces.IOpenApiLink { - public OpenApiLinkReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, string externalResource = null) { } + public OpenApiLinkReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument = null, string externalResource = null) { } public string Description { get; set; } public System.Collections.Generic.IDictionary Extensions { get; } public string OperationId { get; } @@ -1306,7 +1306,7 @@ namespace Microsoft.OpenApi.Models.References } public class OpenApiParameterReference : Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReadOnlyExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Interfaces.IShallowCopyable, Microsoft.OpenApi.Models.Interfaces.IOpenApiDescribedElement, Microsoft.OpenApi.Models.Interfaces.IOpenApiParameter { - public OpenApiParameterReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, string externalResource = null) { } + public OpenApiParameterReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument = null, string externalResource = null) { } public bool AllowEmptyValue { get; } public bool AllowReserved { get; } public System.Collections.Generic.IDictionary Content { get; } @@ -1326,7 +1326,7 @@ namespace Microsoft.OpenApi.Models.References } public class OpenApiPathItemReference : Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReadOnlyExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Interfaces.IShallowCopyable, Microsoft.OpenApi.Models.Interfaces.IOpenApiDescribedElement, Microsoft.OpenApi.Models.Interfaces.IOpenApiPathItem, Microsoft.OpenApi.Models.Interfaces.IOpenApiSummarizedElement { - public OpenApiPathItemReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, string externalResource = null) { } + public OpenApiPathItemReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument = null, string externalResource = null) { } public string Description { get; set; } public System.Collections.Generic.IDictionary Extensions { get; } public System.Collections.Generic.IDictionary Operations { get; } @@ -1339,7 +1339,7 @@ namespace Microsoft.OpenApi.Models.References } public class OpenApiRequestBodyReference : Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReadOnlyExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Interfaces.IShallowCopyable, Microsoft.OpenApi.Models.Interfaces.IOpenApiDescribedElement, Microsoft.OpenApi.Models.Interfaces.IOpenApiRequestBody { - public OpenApiRequestBodyReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, string externalResource = null) { } + public OpenApiRequestBodyReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument = null, string externalResource = null) { } public System.Collections.Generic.IDictionary Content { get; } public string Description { get; set; } public System.Collections.Generic.IDictionary Extensions { get; } @@ -1352,7 +1352,7 @@ namespace Microsoft.OpenApi.Models.References } public class OpenApiResponseReference : Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReadOnlyExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Interfaces.IShallowCopyable, Microsoft.OpenApi.Models.Interfaces.IOpenApiDescribedElement, Microsoft.OpenApi.Models.Interfaces.IOpenApiResponse { - public OpenApiResponseReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, string externalResource = null) { } + public OpenApiResponseReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument = null, string externalResource = null) { } public System.Collections.Generic.IDictionary Content { get; } public string Description { get; set; } public System.Collections.Generic.IDictionary Extensions { get; } @@ -1363,7 +1363,7 @@ namespace Microsoft.OpenApi.Models.References } public class OpenApiSchemaReference : Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReadOnlyExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Interfaces.IShallowCopyable, Microsoft.OpenApi.Models.Interfaces.IOpenApiDescribedElement, Microsoft.OpenApi.Models.Interfaces.IOpenApiSchema { - public OpenApiSchemaReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, string externalResource = null) { } + public OpenApiSchemaReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument = null, string externalResource = null) { } public Microsoft.OpenApi.Models.Interfaces.IOpenApiSchema AdditionalProperties { get; } public bool AdditionalPropertiesAllowed { get; } public System.Collections.Generic.IList AllOf { get; } @@ -1424,7 +1424,7 @@ namespace Microsoft.OpenApi.Models.References } public class OpenApiSecuritySchemeReference : Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReadOnlyExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Interfaces.IShallowCopyable, Microsoft.OpenApi.Models.Interfaces.IOpenApiDescribedElement, Microsoft.OpenApi.Models.Interfaces.IOpenApiSecurityScheme { - public OpenApiSecuritySchemeReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, string externalResource = null) { } + public OpenApiSecuritySchemeReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument = null, string externalResource = null) { } public string BearerFormat { get; } public string Description { get; set; } public System.Collections.Generic.IDictionary Extensions { get; } @@ -1439,7 +1439,7 @@ namespace Microsoft.OpenApi.Models.References } public class OpenApiTagReference : Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReadOnlyExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable, Microsoft.OpenApi.Interfaces.IShallowCopyable, Microsoft.OpenApi.Models.Interfaces.IOpenApiReadOnlyDescribedElement, Microsoft.OpenApi.Models.Interfaces.IOpenApiTag { - public OpenApiTagReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument) { } + public OpenApiTagReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument = null, string externalResource = null) { } public string Description { get; } public System.Collections.Generic.IDictionary Extensions { get; } public Microsoft.OpenApi.Models.OpenApiExternalDocs ExternalDocs { get; } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs index ea9a9660a..b9a73da40 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs @@ -50,7 +50,7 @@ public void ReferencedSchemaShouldOnlyBeValidatedOnce() { ["application/json"] = new() { - Schema = new OpenApiSchemaReference(sharedSchema, "test") + Schema = new OpenApiSchemaReference("test") } } } @@ -104,7 +104,7 @@ public void UnresolvedSchemaReferencedShouldNotBeValidated() { ["application/json"] = new() { - Schema = new OpenApiSchemaReference(sharedSchema, "test") + Schema = new OpenApiSchemaReference("test") } } } diff --git a/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs b/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs index 44000b024..ce45186c6 100644 --- a/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs @@ -166,14 +166,14 @@ public void LocateReferences() var derivedSchema = new OpenApiSchema { - AnyOf = new List { new OpenApiSchemaReference(baseSchema, "base") }, + AnyOf = new List { new OpenApiSchemaReference("base") }, }; var testHeader = new OpenApiHeader() { - Schema = new OpenApiSchemaReference(derivedSchema, "derived"), + Schema = new OpenApiSchemaReference("derived"), }; - var testHeaderReference = new OpenApiHeaderReference(testHeader, "test-header"); + var testHeaderReference = new OpenApiHeaderReference("test-header"); var doc = new OpenApiDocument { @@ -193,7 +193,7 @@ public void LocateReferences() { ["application/json"] = new() { - Schema = new OpenApiSchemaReference(derivedSchema, "derived") + Schema = new OpenApiSchemaReference("derived") } }, Headers = @@ -219,7 +219,7 @@ public void LocateReferences() }, SecuritySchemes = new Dictionary { - ["test-secScheme"] = new OpenApiSecuritySchemeReference("reference-to-scheme", null, null) + ["test-secScheme"] = new OpenApiSecuritySchemeReference("reference-to-scheme") } } }; diff --git a/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs b/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs index 38a9b2d8d..8c5478cf7 100644 --- a/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs +++ b/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs @@ -39,7 +39,7 @@ public void OpenApiWorkspacesCanAddComponentsFromAnotherDocument() { ["application/json"] = new OpenApiMediaType() { - Schema = new OpenApiSchemaReference(testSchema, "test") + Schema = new OpenApiSchemaReference("test") } } } diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiYamlWriterTests.cs b/test/Microsoft.OpenApi.Tests/Writers/OpenApiYamlWriterTests.cs index 403922622..669f4cb13 100644 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiYamlWriterTests.cs +++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiYamlWriterTests.cs @@ -463,10 +463,10 @@ private static OpenApiDocument CreateDocWithSimpleSchemaToInline() { Description = "OK", Content = { - ["application/json"] = new() - { - Schema = new OpenApiSchemaReference(thingSchema, "thing") - } + ["application/json"] = new() + { + Schema = new OpenApiSchemaReference("thing") + } } } } @@ -480,6 +480,8 @@ private static OpenApiDocument CreateDocWithSimpleSchemaToInline() ["thing"] = thingSchema} } }; + doc.RegisterComponents(); + doc.SetReferenceHostDocument(); return doc; } From 8ad773ef4df4f7b12106e5adb40b1c13cd848b2f Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 4 Feb 2025 15:42:32 -0500 Subject: [PATCH 08/10] chore: code linting Signed-off-by: Vincent Biret --- .../V3Tests/OpenApiDocumentTests.cs | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index ccf3a9407..fb7524c42 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -49,23 +49,6 @@ private static async Task CloneAsync(T element) where T : class, IOpenApiS return OpenApiModelFactory.Parse(result, OpenApiSpecVersion.OpenApi3_0, new(), out var _); } - private static async Task CloneSecuritySchemeAsync(OpenApiSecurityScheme element) - { - using var stream = new MemoryStream(); - var streamWriter = new FormattingStreamWriter(stream, CultureInfo.InvariantCulture); - var writer = new OpenApiJsonWriter(streamWriter, new OpenApiJsonWriterSettings() - { - InlineLocalReferences = true - }); - element.SerializeAsV3(writer); - await writer.FlushAsync(); - stream.Position = 0; - - using var streamReader = new StreamReader(stream); - var result = await streamReader.ReadToEndAsync(); - return OpenApiModelFactory.Parse(result, OpenApiSpecVersion.OpenApi3_0, new(), out var _); - } - [Fact] public void ParseDocumentFromInlineStringShouldSucceed() { @@ -683,28 +666,26 @@ public async Task ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() // Create a clone of the schema to avoid modifying things in components. var petSchemaSource = Assert.IsType(components.Schemas["pet1"]); var petSchema = await CloneAsync(petSchemaSource); - var castPetSchema = Assert.IsType(petSchema); + Assert.IsType(petSchema); var petSchemaReference = new OpenApiSchemaReference("pet1"); var newPetSchemaSource = Assert.IsType(components.Schemas["newPet"]); var newPetSchema = await CloneAsync(newPetSchemaSource); - var castNewPetSchema = Assert.IsType(newPetSchema); + Assert.IsType(newPetSchema); var newPetSchemaReference = new OpenApiSchemaReference("newPet"); var errorModelSchemaSource = Assert.IsType(components.Schemas["errorModel"]); var errorModelSchema = await CloneAsync(errorModelSchemaSource); - var castErrorModelSchema = Assert.IsType(errorModelSchema); + Assert.IsType(errorModelSchema); var errorModelSchemaReference = new OpenApiSchemaReference("errorModel"); var tagReference1 = new OpenApiTagReference("tagName1"); var tagReference2 = new OpenApiTagReference("tagName2"); - var securityScheme1Cast = Assert.IsType(components.SecuritySchemes["securitySchemeName1"]); - var securityScheme1 = await CloneSecuritySchemeAsync(securityScheme1Cast); + Assert.IsType(components.SecuritySchemes["securitySchemeName1"]); - var securityScheme2Cast = Assert.IsType(components.SecuritySchemes["securitySchemeName2"]); - var securityScheme2 = await CloneSecuritySchemeAsync(securityScheme2Cast); + Assert.IsType(components.SecuritySchemes["securitySchemeName2"]); var expected = new OpenApiDocument { @@ -1048,12 +1029,6 @@ public async Task ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() expected.RegisterComponents(); expected.SetReferenceHostDocument(); - tagReference1.Reference.EnsureHostDocumentIsSet(expected); - tagReference2.Reference.EnsureHostDocumentIsSet(expected); - petSchemaReference.Reference.EnsureHostDocumentIsSet(expected); - newPetSchemaReference.Reference.EnsureHostDocumentIsSet(expected); - errorModelSchemaReference.Reference.EnsureHostDocumentIsSet(expected); - actual.Document.Should().BeEquivalentTo(expected, options => options .IgnoringCyclicReferences() .Excluding(x => x.Paths["/pets"].Operations[OperationType.Get].Tags[0].Reference) From be3c552ce1f4c529ac06833b4fe817ed0fdfa5ca Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 4 Feb 2025 15:48:45 -0500 Subject: [PATCH 09/10] chore: code linting Signed-off-by: Vincent Biret --- .../V3Tests/OpenApiDocumentTests.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index fb7524c42..e61ee8fbc 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -1230,8 +1230,12 @@ public async Task ParseDocWithRefsUsingProxyReferencesSucceeds() } } }; + expected.RegisterComponents(); + expected.SetReferenceHostDocument(); - var expectedSerializedDoc = @"openapi: 3.0.4 + var expectedSerializedDoc = +""" +openapi: 3.0.4 info: title: Pet Store with Referenceable Parameter version: 1.0.0 @@ -1251,7 +1255,8 @@ public async Task ParseDocWithRefsUsingProxyReferencesSucceeds() schema: type: integer format: int32 - default: 10"; + default: 10 +"""; using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "minifiedPetStore.yaml")); @@ -1261,7 +1266,6 @@ public async Task ParseDocWithRefsUsingProxyReferencesSucceeds() var outputDoc = (await doc.SerializeAsYamlAsync(OpenApiSpecVersion.OpenApi3_0)).MakeLineBreaksEnvironmentNeutral(); var expectedParam = expected.Paths["/pets"].Operations[OperationType.Get].Parameters[0]; var expectedParamReference = Assert.IsType(expectedParam); - expectedParamReference.Reference.EnsureHostDocumentIsSet(doc); var actualParamReference = Assert.IsType(actualParam); From e0aba68aaa9ce27ad8f3fb5078792a4571d68a4e Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 4 Feb 2025 15:51:06 -0500 Subject: [PATCH 10/10] chore: code linting Signed-off-by: Vincent Biret --- .../Reader/V3/OpenApiV3VersionService.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi/Reader/V3/OpenApiV3VersionService.cs b/src/Microsoft.OpenApi/Reader/V3/OpenApiV3VersionService.cs index c10bf6ddf..612c59dfb 100644 --- a/src/Microsoft.OpenApi/Reader/V3/OpenApiV3VersionService.cs +++ b/src/Microsoft.OpenApi/Reader/V3/OpenApiV3VersionService.cs @@ -183,11 +183,12 @@ public T LoadElement(ParseNode node, OpenApiDocument doc) where T : IOpenApiE /// public string GetReferenceScalarValues(MapNode mapNode, string scalarValue) { - if (mapNode.Any(static x => !"$ref".Equals(x.Name, StringComparison.OrdinalIgnoreCase))) + if (mapNode.Any(static x => !"$ref".Equals(x.Name, StringComparison.OrdinalIgnoreCase)) && + mapNode + .Where(x => x.Name.Equals(scalarValue)) + .Select(static x => x.Value) + .OfType().FirstOrDefault() is {} valueNode) { - var valueNode = mapNode.Where(x => x.Name.Equals(scalarValue)) - .Select(static x => x.Value).OfType().FirstOrDefault(); - return valueNode.GetScalarValue(); }