From 30ee6ed9ac8e6a6a7d1931bed9da22e0116ec9af Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 27 Dec 2024 12:17:14 -0500 Subject: [PATCH 01/15] fix: single copy and maintain for references Signed-off-by: Vincent Biret --- .../Models/References/OpenApiSchemaReference.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs index 011e0b930..ad8f5e88e 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs @@ -21,14 +21,21 @@ public class OpenApiSchemaReference : OpenApiSchema private JsonNode _example; private IList _examples; + #nullable enable + private OpenApiSchema? _targetProxy; + #nullable restore + private OpenApiSchema Target { get { _target ??= Reference.HostDocument?.ResolveReferenceTo(_reference); - OpenApiSchema resolved = new OpenApiSchema(_target); - if (!string.IsNullOrEmpty(_description)) resolved.Description = _description; - return resolved; + if (_targetProxy is null) + { + _targetProxy = new OpenApiSchema(_target); + if (!string.IsNullOrEmpty(_description)) _targetProxy.Description = _description; + } + return _targetProxy; } } From 6f4e7a245376cb816367ff86c497cbba023b6faf Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 27 Dec 2024 15:30:40 -0500 Subject: [PATCH 02/15] fix: uses backing fields instead of schema copy Signed-off-by: Vincent Biret --- .../References/OpenApiSchemaReference.cs | 171 +++++++++++------- 1 file changed, 105 insertions(+), 66 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs index ad8f5e88e..adb3a5162 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs @@ -14,28 +14,68 @@ namespace Microsoft.OpenApi.Models.References /// public class OpenApiSchemaReference : OpenApiSchema { - internal OpenApiSchema _target; + #nullable enable + private OpenApiSchema? _target; private readonly OpenApiReference _reference; - private string _description; - private JsonNode _default; - private JsonNode _example; - private IList _examples; + private string? _description; + private JsonNode? _default; + private JsonNode? _example; + private IList? _examples; + private bool? _nullable; + private IDictionary? _properties; + private string? _title; + private string? _schema; + private string? _comment; + private string? _id; + private string? _dynamicRef; + private string? _dynamicAnchor; + private IDictionary? _vocabulary; + private IDictionary? _definitions; + private decimal? _v31ExclusiveMaximum; + private decimal? _v31ExclusiveMinimum; + private bool? _unEvaluatedProperties; + private JsonSchemaType? _type; + private string? _const; + private string? _format; + private decimal? _maximum; + private bool? _exclusiveMaximum; + private decimal? _minimum; + private bool? _exclusiveMinimum; + private int? _maxLength; + private int? _minLength; + private string? _pattern; + private decimal? _multipleOf; + private bool? _readOnly; + private bool? _writeOnly; + private IList? _allOf; + private IList? _oneOf; + private IList? _anyOf; + private OpenApiSchema? _not; + private ISet? _required; + private OpenApiSchema _items; + private int? _maxItems; + private int? _minItems; + private bool? _uniqueItems; + private IDictionary? _patternProperties; + private int? _maxProperties; + private int? _minProperties; + private bool? _additionalPropertiesAllowed; + private OpenApiSchema? _additionalProperties; + private OpenApiDiscriminator? _discriminator; + private OpenApiExternalDocs? _externalDocs; + private bool? _deprecated; + private OpenApiXml? _xml; + private IDictionary? _extensions; + private bool? _unevaluatedProperties; + private IList? _enum; - #nullable enable - private OpenApiSchema? _targetProxy; + private OpenApiSchema? Target #nullable restore - - private OpenApiSchema Target { get { _target ??= Reference.HostDocument?.ResolveReferenceTo(_reference); - if (_targetProxy is null) - { - _targetProxy = new OpenApiSchema(_target); - if (!string.IsNullOrEmpty(_description)) _targetProxy.Description = _description; - } - return _targetProxy; + return _target; } } @@ -76,33 +116,33 @@ internal OpenApiSchemaReference(OpenApiSchema target, string referenceId) } /// - public override string Title { get => Target.Title; set => Target.Title = value; } + public override string Title { get => string.IsNullOrEmpty(_title) ? Target.Title : _title; set => _title = value; } /// - public override string Schema { get => Target.Schema; set => Target.Schema = value; } + public override string Schema { get => string.IsNullOrEmpty(_schema) ? Target.Schema : _schema; set => _schema = value; } /// - public override string Id { get => Target.Id; set => Target.Id = value; } + public override string Id { get => string.IsNullOrEmpty(_id) ? Target.Id : _id; set => _id = value; } /// - public override string Comment { get => Target.Comment; set => Target.Comment = value; } + public override string Comment { get => string.IsNullOrEmpty(_comment) ? Target.Comment : _comment; set => _comment = value; } /// - public override IDictionary Vocabulary { get => Target.Vocabulary; set => Target.Vocabulary = value; } + public override IDictionary Vocabulary { get => _vocabulary is not null ? _vocabulary : Target.Vocabulary; set => _vocabulary = value; } /// - public override string DynamicRef { get => Target.DynamicRef; set => Target.DynamicRef = value; } + public override string DynamicRef { get => string.IsNullOrEmpty(_dynamicRef) ? Target.DynamicRef : _dynamicRef; set => _dynamicRef = value; } /// - public override string DynamicAnchor { get => Target.DynamicAnchor; set => Target.DynamicAnchor = value; } + public override string DynamicAnchor { get => string.IsNullOrEmpty(_dynamicAnchor) ? Target.DynamicAnchor : _dynamicAnchor; set => _dynamicAnchor = value; } /// - public override IDictionary Definitions { get => Target.Definitions; set => Target.Definitions = value; } + public override IDictionary Definitions { get => _definitions is not null ? _definitions : Target.Definitions; set => _definitions = value; } /// - public override decimal? V31ExclusiveMaximum { get => Target.V31ExclusiveMaximum; set => Target.V31ExclusiveMaximum = value; } + public override decimal? V31ExclusiveMaximum { get => _v31ExclusiveMaximum is not null ? _v31ExclusiveMaximum.Value : Target.V31ExclusiveMaximum; set => _v31ExclusiveMaximum = value; } /// - public override decimal? V31ExclusiveMinimum { get => Target.V31ExclusiveMinimum; set => Target.V31ExclusiveMinimum = value; } + public override decimal? V31ExclusiveMinimum { get => _v31ExclusiveMinimum is not null ? _v31ExclusiveMinimum.Value : Target.V31ExclusiveMinimum; set => _v31ExclusiveMinimum = value; } /// - public override bool UnEvaluatedProperties { get => Target.UnEvaluatedProperties; set => Target.UnEvaluatedProperties = value; } + public override bool UnEvaluatedProperties { get => _unEvaluatedProperties is not null ? _unEvaluatedProperties.Value : Target.UnEvaluatedProperties; set => _unEvaluatedProperties = value; } /// - public override JsonSchemaType? Type { get => Target.Type; set => Target.Type = value; } + public override JsonSchemaType? Type { get => _type is not null ? _type.Value : Target.Type; set => _type = value; } /// - public override string Const { get => Target.Const; set => Target.Const = value; } + public override string Const { get => string.IsNullOrEmpty(_const) ? Target.Const : _const; set => _const = value; } /// - public override string Format { get => Target.Format; set => Target.Format = value; } + public override string Format { get => string.IsNullOrEmpty(_format) ? Target.Format : _format; set => _format = value; } /// public override string Description { @@ -110,89 +150,89 @@ public override string Description set => _description = value; } /// - public override decimal? Maximum { get => Target.Maximum; set => Target.Maximum = value; } + public override decimal? Maximum { get => _maximum is not null ? _maximum : Target.Maximum; set => _maximum = value; } /// - public override bool? ExclusiveMaximum { get => Target.ExclusiveMaximum; set => Target.ExclusiveMaximum = value; } + public override bool? ExclusiveMaximum { get => _exclusiveMaximum is not null ? _exclusiveMaximum : Target.ExclusiveMaximum; set => _exclusiveMaximum = value; } /// - public override decimal? Minimum { get => Target.Minimum; set => Target.Minimum = value; } + public override decimal? Minimum { get => _minimum is not null ? _minimum : Target.Minimum; set => _minimum = value; } /// - public override bool? ExclusiveMinimum { get => Target.ExclusiveMinimum; set => Target.ExclusiveMinimum = value; } + public override bool? ExclusiveMinimum { get => _exclusiveMinimum is not null ? _exclusiveMinimum : Target.ExclusiveMinimum; set => _exclusiveMinimum = value; } /// - public override int? MaxLength { get => Target.MaxLength; set => Target.MaxLength = value; } + public override int? MaxLength { get => _maxLength is not null ? _maxLength : Target.MaxLength; set => _maxLength = value; } /// - public override int? MinLength { get => Target.MinLength; set => Target.MinLength = value; } + public override int? MinLength { get => _minLength is not null ? _minLength : Target.MinLength; set => _minLength = value; } /// - public override string Pattern { get => Target.Pattern; set => Target.Pattern = value; } + public override string Pattern { get => string.IsNullOrEmpty(_pattern) ? Target.Pattern : _pattern; set => _pattern = value; } /// - public override decimal? MultipleOf { get => Target.MultipleOf; set => Target.MultipleOf = value; } + public override decimal? MultipleOf { get => _multipleOf is not null ? _multipleOf : Target.MultipleOf; set => _multipleOf = value; } /// public override JsonNode Default { - get => _default ??= Target.Default; + get => _default ??= Target.Default; //TODO normalize like other properties set => _default = value; } /// - public override bool ReadOnly { get => Target.ReadOnly; set => Target.ReadOnly = value; } + public override bool ReadOnly { get => _readOnly is not null ? _readOnly.Value : Target.ReadOnly; set => _readOnly = value; } /// - public override bool WriteOnly { get => Target.WriteOnly; set => Target.WriteOnly = value; } + public override bool WriteOnly { get => _writeOnly is not null ? _writeOnly.Value : Target.WriteOnly; set => _writeOnly = value; } /// - public override IList AllOf { get => Target.AllOf; set => Target.AllOf = value; } + public override IList AllOf { get => _allOf is not null ? _allOf : Target.AllOf; set => _allOf = value; } /// - public override IList OneOf { get => Target.OneOf; set => Target.OneOf = value; } + public override IList OneOf { get => _oneOf is not null ? _oneOf : Target.OneOf; set => _oneOf = value; } /// - public override IList AnyOf { get => Target.AnyOf; set => Target.AnyOf = value; } + public override IList AnyOf { get => _anyOf is not null ? _anyOf : Target.AnyOf; set => _anyOf = value; } /// - public override OpenApiSchema Not { get => Target.Not; set => Target.Not = value; } + public override OpenApiSchema Not { get => _not is not null ? _not : Target.Not; set => _not = value; } /// - public override ISet Required { get => Target.Required; set => Target.Required = value; } + public override ISet Required { get => _required is not null ? _required : Target.Required; set => _required = value; } /// - public override OpenApiSchema Items { get => Target.Items; set => Target.Items = value; } + public override OpenApiSchema Items { get => _items is not null ? _items : Target.Items; set => _items = value; } /// - public override int? MaxItems { get => Target.MaxItems; set => Target.MaxItems = value; } + public override int? MaxItems { get => _maxItems is not null ? _maxItems : Target.MaxItems; set => _maxItems = value; } /// - public override int? MinItems { get => Target.MinItems; set => Target.MinItems = value; } + public override int? MinItems { get => _minItems is not null ? _minItems : Target.MinItems; set => _minItems = value; } /// - public override bool? UniqueItems { get => Target.UniqueItems; set => Target.UniqueItems = value; } + public override bool? UniqueItems { get => _uniqueItems is not null ? _uniqueItems : Target.UniqueItems; set => _uniqueItems = value; } /// - public override IDictionary Properties { get => Target.Properties; set => Target.Properties = value; } + public override IDictionary Properties { get => _properties is not null ? _properties : Target.Properties ; set => _properties = value; } /// - public override IDictionary PatternProperties { get => Target.PatternProperties; set => Target.PatternProperties = value; } + public override IDictionary PatternProperties { get => _patternProperties is not null ? _patternProperties : Target.PatternProperties; set => _patternProperties = value; } /// - public override int? MaxProperties { get => Target.MaxProperties; set => Target.MaxProperties = value; } + public override int? MaxProperties { get => _maxProperties is not null ? _maxProperties : Target.MaxProperties; set => _maxProperties = value; } /// - public override int? MinProperties { get => Target.MinProperties; set => Target.MinProperties = value; } + public override int? MinProperties { get => _minProperties is not null ? _minProperties : Target.MinProperties; set => _minProperties = value; } /// - public override bool AdditionalPropertiesAllowed { get => Target.AdditionalPropertiesAllowed; set => Target.AdditionalPropertiesAllowed = value; } + public override bool AdditionalPropertiesAllowed { get => _additionalPropertiesAllowed is not null ? _additionalPropertiesAllowed.Value : Target.AdditionalPropertiesAllowed; set => _additionalPropertiesAllowed = value; } /// - public override OpenApiSchema AdditionalProperties { get => Target.AdditionalProperties; set => Target.AdditionalProperties = value; } + public override OpenApiSchema AdditionalProperties { get => _additionalProperties is not null ? _additionalProperties : Target.AdditionalProperties; set => _additionalProperties = value; } /// - public override OpenApiDiscriminator Discriminator { get => Target.Discriminator; set => Target.Discriminator = value; } + public override OpenApiDiscriminator Discriminator { get => _discriminator is not null ? _discriminator : Target.Discriminator; set => _discriminator = value; } /// public override JsonNode Example { - get => _example ??= Target.Example; + get => _example ??= Target.Example; //TODO normalize like other properties set => _example = value; } /// public override IList Examples { - get => _examples ??= Target.Examples; + get => _examples ??= Target.Examples; //TODO normalize like other properties set => Target.Examples = value; } /// - public override IList Enum { get => Target.Enum; set => Target.Enum = value; } + public override IList Enum { get => _enum is not null ? _enum : Target.Enum; set => _enum = value; } /// - public override bool Nullable { get => Target.Nullable; set => Target.Nullable = value; } + public override bool Nullable { get => _nullable is null ? Target.Nullable : _nullable.Value; set => _nullable = value; } /// - public override bool UnevaluatedProperties { get => Target.UnevaluatedProperties; set => Target.UnevaluatedProperties = value; } + public override bool UnevaluatedProperties { get => _unevaluatedProperties is not null ? _unevaluatedProperties.Value : Target.UnevaluatedProperties; set => _unevaluatedProperties = value; } /// - public override OpenApiExternalDocs ExternalDocs { get => Target.ExternalDocs; set => Target.ExternalDocs = value; } + public override OpenApiExternalDocs ExternalDocs { get => _externalDocs is not null ? _externalDocs : Target.ExternalDocs; set => _externalDocs = value; } /// - public override bool Deprecated { get => Target.Deprecated; set => Target.Deprecated = value; } + public override bool Deprecated { get => _deprecated is not null ? _deprecated.Value : Target.Deprecated; set => _deprecated = value; } /// - public override OpenApiXml Xml { get => Target.Xml; set => Target.Xml = value; } + public override OpenApiXml Xml { get => _xml is not null ? _xml : Target.Xml; set => _xml = value; } /// - public override IDictionary Extensions { get => Target.Extensions; set => Target.Extensions = value; } + public override IDictionary Extensions { get => _extensions is not null ? _extensions : Target.Extensions; set => _extensions = value; } /// public override void SerializeAsV31(IOpenApiWriter writer) @@ -240,7 +280,6 @@ public override void SerializeAsV2(IOpenApiWriter writer) if (!writer.GetSettings().ShouldInlineReference(_reference)) { _reference.SerializeAsV2(writer); - return; } else { From 0a7b4f6a8265f390ea452fc6bc9ab528c33bd2de Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 30 Dec 2024 09:29:57 -0500 Subject: [PATCH 03/15] chore: linting Signed-off-by: Vincent Biret --- .../Services/OpenApiWorkspace.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs index b227b06e9..813a2296b 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs @@ -54,6 +54,7 @@ public int ComponentsCount() { return _IOpenApiReferenceableRegistry.Count + _artifactsRegistry.Count; } + private const string ComponentSegmentSeparator = "/"; /// /// Registers a document's components into the workspace @@ -69,7 +70,7 @@ public void RegisterComponents(OpenApiDocument document) // Register Schema foreach (var item in document.Components.Schemas) { - location = item.Value.Id ?? baseUri + ReferenceType.Schema.GetDisplayName() + "/" + item.Key; + location = item.Value.Id ?? baseUri + ReferenceType.Schema.GetDisplayName() + ComponentSegmentSeparator + item.Key; RegisterComponent(location, item.Value); } @@ -77,63 +78,63 @@ public void RegisterComponents(OpenApiDocument document) // Register Parameters foreach (var item in document.Components.Parameters) { - location = baseUri + ReferenceType.Parameter.GetDisplayName() + "/" + item.Key; + location = baseUri + ReferenceType.Parameter.GetDisplayName() + ComponentSegmentSeparator + item.Key; RegisterComponent(location, item.Value); } // Register Responses foreach (var item in document.Components.Responses) { - location = baseUri + ReferenceType.Response.GetDisplayName() + "/" + item.Key; + location = baseUri + ReferenceType.Response.GetDisplayName() + ComponentSegmentSeparator + item.Key; RegisterComponent(location, item.Value); } // Register RequestBodies foreach (var item in document.Components.RequestBodies) { - location = baseUri + ReferenceType.RequestBody.GetDisplayName() + "/" + item.Key; + location = baseUri + ReferenceType.RequestBody.GetDisplayName() + ComponentSegmentSeparator + item.Key; RegisterComponent(location, item.Value); } // Register Links foreach (var item in document.Components.Links) { - location = baseUri + ReferenceType.Link.GetDisplayName() + "/" + item.Key; + location = baseUri + ReferenceType.Link.GetDisplayName() + ComponentSegmentSeparator + item.Key; RegisterComponent(location, item.Value); } // Register Callbacks foreach (var item in document.Components.Callbacks) { - location = baseUri + ReferenceType.Callback.GetDisplayName() + "/" + item.Key; + location = baseUri + ReferenceType.Callback.GetDisplayName() + ComponentSegmentSeparator + item.Key; RegisterComponent(location, item.Value); } // Register PathItems foreach (var item in document.Components.PathItems) { - location = baseUri + ReferenceType.PathItem.GetDisplayName() + "/" + item.Key; + location = baseUri + ReferenceType.PathItem.GetDisplayName() + ComponentSegmentSeparator + item.Key; RegisterComponent(location, item.Value); } // Register Examples foreach (var item in document.Components.Examples) { - location = baseUri + ReferenceType.Example.GetDisplayName() + "/" + item.Key; + location = baseUri + ReferenceType.Example.GetDisplayName() + ComponentSegmentSeparator + item.Key; RegisterComponent(location, item.Value); } // Register Headers foreach (var item in document.Components.Headers) { - location = baseUri + ReferenceType.Header.GetDisplayName() + "/" + item.Key; + location = baseUri + ReferenceType.Header.GetDisplayName() + ComponentSegmentSeparator + item.Key; RegisterComponent(location, item.Value); } // Register SecuritySchemes foreach (var item in document.Components.SecuritySchemes) { - location = baseUri + ReferenceType.SecurityScheme.GetDisplayName() + "/" + item.Key; + location = baseUri + ReferenceType.SecurityScheme.GetDisplayName() + ComponentSegmentSeparator + item.Key; RegisterComponent(location, item.Value); } } From 10e548ac943d6e87b132a2fcd3784c21d320346d Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 30 Dec 2024 10:51:11 -0500 Subject: [PATCH 04/15] feat: adds components registration method for schemas Signed-off-by: Vincent Biret --- .../Services/OpenApiWorkspace.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs index 813a2296b..1d8aa2799 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs @@ -64,7 +64,7 @@ public void RegisterComponents(OpenApiDocument document) { if (document?.Components == null) return; - string baseUri = document.BaseUri + OpenApiConstants.ComponentsSegment; + string baseUri = getBaseUri(document); string location; // Register Schema @@ -139,6 +139,33 @@ public void RegisterComponents(OpenApiDocument document) } } + private string getBaseUri(OpenApiDocument openApiDocument) + { + return openApiDocument.BaseUri + OpenApiConstants.ComponentsSegment; + } + + /// + /// Registers a schema for a document in the workspace + /// + /// The document to register the schema for. + /// The schema to register. + /// The id of the schema. + /// true if the schema is successfully registered; otherwise false. + /// openApiDocument is null + /// openApiSchema is null + /// id is null or empty + public bool RegisterSchemaForDocument(OpenApiDocument openApiDocument, OpenApiSchema openApiSchema, string id) + { + Utils.CheckArgumentNull(openApiDocument); + Utils.CheckArgumentNull(openApiSchema); + Utils.CheckArgumentNullOrEmpty(id); + + var baseUri = getBaseUri(openApiDocument); + + var location = baseUri + ReferenceType.Schema.GetDisplayName() + ComponentSegmentSeparator + id; + + return RegisterComponent(location, openApiSchema); + } /// /// Registers a component in the component registry. From 72db982037a45ebed212357b7926431a47d730c7 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 30 Dec 2024 11:55:13 -0500 Subject: [PATCH 05/15] chore: linting Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi/Models/OpenApiComponents.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiComponents.cs b/src/Microsoft.OpenApi/Models/OpenApiComponents.cs index f672b7dd1..0db85ed77 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiComponents.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiComponents.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Writers; From efd2bfba4368a68bb09741ac9983efc09172d1e2 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 30 Dec 2024 11:56:06 -0500 Subject: [PATCH 06/15] feat; adds a method to register and add the component schemas Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi/Models/OpenApiDocument.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 268007141..d680495c8 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -587,6 +587,21 @@ public static ReadResult Parse(string input, { return OpenApiModelFactory.Parse(input, format, settings); } + /// + /// Adds a schema to the components object of the current document. + /// + /// The schema to add + /// The id for the component + /// Whether the schema was added to the components. + public bool AddComponentSchema(string id, OpenApiSchema openApiSchema) + { + Utils.CheckArgumentNull(openApiSchema); + Utils.CheckArgumentNullOrEmpty(id); + Components ??= new(); + Components.Schemas ??= new Dictionary(); + Components.Schemas.Add(id, openApiSchema); + return Workspace?.RegisterSchemaForDocument(this, openApiSchema, id) ?? false; + } } internal class FindSchemaReferences : OpenApiVisitorBase From 7994691db279c23e3ac120a54b5d96cc7f88ae3f Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 30 Dec 2024 13:58:38 -0500 Subject: [PATCH 07/15] fix: null propagation for most failed reference lookup Signed-off-by: Vincent Biret --- .../References/OpenApiSchemaReference.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs index adb3a5162..5c4bb8ff0 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs @@ -116,7 +116,7 @@ internal OpenApiSchemaReference(OpenApiSchema target, string referenceId) } /// - public override string Title { get => string.IsNullOrEmpty(_title) ? Target.Title : _title; set => _title = value; } + public override string Title { get => string.IsNullOrEmpty(_title) ? Target?.Title : _title; set => _title = value; } /// public override string Schema { get => string.IsNullOrEmpty(_schema) ? Target.Schema : _schema; set => _schema = value; } /// @@ -124,13 +124,13 @@ internal OpenApiSchemaReference(OpenApiSchema target, string referenceId) /// public override string Comment { get => string.IsNullOrEmpty(_comment) ? Target.Comment : _comment; set => _comment = value; } /// - public override IDictionary Vocabulary { get => _vocabulary is not null ? _vocabulary : Target.Vocabulary; set => _vocabulary = value; } + public override IDictionary Vocabulary { get => _vocabulary is not null ? _vocabulary : Target?.Vocabulary; set => _vocabulary = value; } /// public override string DynamicRef { get => string.IsNullOrEmpty(_dynamicRef) ? Target.DynamicRef : _dynamicRef; set => _dynamicRef = value; } /// public override string DynamicAnchor { get => string.IsNullOrEmpty(_dynamicAnchor) ? Target.DynamicAnchor : _dynamicAnchor; set => _dynamicAnchor = value; } /// - public override IDictionary Definitions { get => _definitions is not null ? _definitions : Target.Definitions; set => _definitions = value; } + public override IDictionary Definitions { get => _definitions is not null ? _definitions : Target?.Definitions; set => _definitions = value; } /// public override decimal? V31ExclusiveMaximum { get => _v31ExclusiveMaximum is not null ? _v31ExclusiveMaximum.Value : Target.V31ExclusiveMaximum; set => _v31ExclusiveMaximum = value; } /// @@ -176,15 +176,15 @@ public override JsonNode Default /// public override bool WriteOnly { get => _writeOnly is not null ? _writeOnly.Value : Target.WriteOnly; set => _writeOnly = value; } /// - public override IList AllOf { get => _allOf is not null ? _allOf : Target.AllOf; set => _allOf = value; } + public override IList AllOf { get => _allOf is not null ? _allOf : Target?.AllOf; set => _allOf = value; } /// - public override IList OneOf { get => _oneOf is not null ? _oneOf : Target.OneOf; set => _oneOf = value; } + public override IList OneOf { get => _oneOf is not null ? _oneOf : Target?.OneOf; set => _oneOf = value; } /// - public override IList AnyOf { get => _anyOf is not null ? _anyOf : Target.AnyOf; set => _anyOf = value; } + public override IList AnyOf { get => _anyOf is not null ? _anyOf : Target?.AnyOf; set => _anyOf = value; } /// public override OpenApiSchema Not { get => _not is not null ? _not : Target.Not; set => _not = value; } /// - public override ISet Required { get => _required is not null ? _required : Target.Required; set => _required = value; } + public override ISet Required { get => _required is not null ? _required : Target?.Required; set => _required = value; } /// public override OpenApiSchema Items { get => _items is not null ? _items : Target.Items; set => _items = value; } /// @@ -194,9 +194,9 @@ public override JsonNode Default /// public override bool? UniqueItems { get => _uniqueItems is not null ? _uniqueItems : Target.UniqueItems; set => _uniqueItems = value; } /// - public override IDictionary Properties { get => _properties is not null ? _properties : Target.Properties ; set => _properties = value; } + public override IDictionary Properties { get => _properties is not null ? _properties : Target?.Properties ; set => _properties = value; } /// - public override IDictionary PatternProperties { get => _patternProperties is not null ? _patternProperties : Target.PatternProperties; set => _patternProperties = value; } + public override IDictionary PatternProperties { get => _patternProperties is not null ? _patternProperties : Target?.PatternProperties; set => _patternProperties = value; } /// public override int? MaxProperties { get => _maxProperties is not null ? _maxProperties : Target.MaxProperties; set => _maxProperties = value; } /// @@ -220,7 +220,7 @@ public override IList Examples set => Target.Examples = value; } /// - public override IList Enum { get => _enum is not null ? _enum : Target.Enum; set => _enum = value; } + public override IList Enum { get => _enum is not null ? _enum : Target?.Enum; set => _enum = value; } /// public override bool Nullable { get => _nullable is null ? Target.Nullable : _nullable.Value; set => _nullable = value; } /// @@ -232,7 +232,7 @@ public override IList Examples /// public override OpenApiXml Xml { get => _xml is not null ? _xml : Target.Xml; set => _xml = value; } /// - public override IDictionary Extensions { get => _extensions is not null ? _extensions : Target.Extensions; set => _extensions = value; } + public override IDictionary Extensions { get => _extensions is not null ? _extensions : Target?.Extensions; set => _extensions = value; } /// public override void SerializeAsV31(IOpenApiWriter writer) From e3325b9d4cedee6a9734899906e938888f023506 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 31 Dec 2024 08:26:27 -0500 Subject: [PATCH 08/15] fix: aligns missing properties for override Signed-off-by: Vincent Biret --- .../References/OpenApiSchemaReference.cs | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs index 5c4bb8ff0..44a86bb01 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs @@ -166,11 +166,7 @@ public override string Description /// public override decimal? MultipleOf { get => _multipleOf is not null ? _multipleOf : Target.MultipleOf; set => _multipleOf = value; } /// - public override JsonNode Default - { - get => _default ??= Target.Default; //TODO normalize like other properties - set => _default = value; - } + public override JsonNode Default { get => _default is not null ? _default : Target.Default; set => _default = value; } /// public override bool ReadOnly { get => _readOnly is not null ? _readOnly.Value : Target.ReadOnly; set => _readOnly = value; } /// @@ -182,11 +178,11 @@ public override JsonNode Default /// public override IList AnyOf { get => _anyOf is not null ? _anyOf : Target?.AnyOf; set => _anyOf = value; } /// - public override OpenApiSchema Not { get => _not is not null ? _not : Target.Not; set => _not = value; } + public override OpenApiSchema Not { get => _not is not null ? _not : Target?.Not; set => _not = value; } /// public override ISet Required { get => _required is not null ? _required : Target?.Required; set => _required = value; } /// - public override OpenApiSchema Items { get => _items is not null ? _items : Target.Items; set => _items = value; } + public override OpenApiSchema Items { get => _items is not null ? _items : Target?.Items; set => _items = value; } /// public override int? MaxItems { get => _maxItems is not null ? _maxItems : Target.MaxItems; set => _maxItems = value; } /// @@ -204,21 +200,13 @@ public override JsonNode Default /// public override bool AdditionalPropertiesAllowed { get => _additionalPropertiesAllowed is not null ? _additionalPropertiesAllowed.Value : Target.AdditionalPropertiesAllowed; set => _additionalPropertiesAllowed = value; } /// - public override OpenApiSchema AdditionalProperties { get => _additionalProperties is not null ? _additionalProperties : Target.AdditionalProperties; set => _additionalProperties = value; } + public override OpenApiSchema AdditionalProperties { get => _additionalProperties is not null ? _additionalProperties : Target?.AdditionalProperties; set => _additionalProperties = value; } /// public override OpenApiDiscriminator Discriminator { get => _discriminator is not null ? _discriminator : Target.Discriminator; set => _discriminator = value; } /// - public override JsonNode Example - { - get => _example ??= Target.Example; //TODO normalize like other properties - set => _example = value; - } + public override JsonNode Example { get => _example is not null ? _example : Target.Example; set => _example = value; } /// - public override IList Examples - { - get => _examples ??= Target.Examples; //TODO normalize like other properties - set => Target.Examples = value; - } + public override IList Examples { get => _examples is not null ? _examples : Target?.Examples; set => _examples = value; } /// public override IList Enum { get => _enum is not null ? _enum : Target?.Enum; set => _enum = value; } /// @@ -226,7 +214,7 @@ public override IList Examples /// public override bool UnevaluatedProperties { get => _unevaluatedProperties is not null ? _unevaluatedProperties.Value : Target.UnevaluatedProperties; set => _unevaluatedProperties = value; } /// - public override OpenApiExternalDocs ExternalDocs { get => _externalDocs is not null ? _externalDocs : Target.ExternalDocs; set => _externalDocs = value; } + public override OpenApiExternalDocs ExternalDocs { get => _externalDocs is not null ? _externalDocs : Target?.ExternalDocs; set => _externalDocs = value; } /// public override bool Deprecated { get => _deprecated is not null ? _deprecated.Value : Target.Deprecated; set => _deprecated = value; } /// From 8d57b81d7122c9ffad4f1c348879cc6df971617c Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 31 Dec 2024 08:29:53 -0500 Subject: [PATCH 09/15] fix: aligns to null propagation operator Signed-off-by: Vincent Biret --- .../References/OpenApiSchemaReference.cs | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs index 44a86bb01..d8890efb1 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs @@ -118,59 +118,59 @@ internal OpenApiSchemaReference(OpenApiSchema target, string referenceId) /// public override string Title { get => string.IsNullOrEmpty(_title) ? Target?.Title : _title; set => _title = value; } /// - public override string Schema { get => string.IsNullOrEmpty(_schema) ? Target.Schema : _schema; set => _schema = value; } + public override string Schema { get => string.IsNullOrEmpty(_schema) ? Target?.Schema : _schema; set => _schema = value; } /// - public override string Id { get => string.IsNullOrEmpty(_id) ? Target.Id : _id; set => _id = value; } + public override string Id { get => string.IsNullOrEmpty(_id) ? Target?.Id : _id; set => _id = value; } /// - public override string Comment { get => string.IsNullOrEmpty(_comment) ? Target.Comment : _comment; set => _comment = value; } + public override string Comment { get => string.IsNullOrEmpty(_comment) ? Target?.Comment : _comment; set => _comment = value; } /// public override IDictionary Vocabulary { get => _vocabulary is not null ? _vocabulary : Target?.Vocabulary; set => _vocabulary = value; } /// - public override string DynamicRef { get => string.IsNullOrEmpty(_dynamicRef) ? Target.DynamicRef : _dynamicRef; set => _dynamicRef = value; } + public override string DynamicRef { get => string.IsNullOrEmpty(_dynamicRef) ? Target?.DynamicRef : _dynamicRef; set => _dynamicRef = value; } /// - public override string DynamicAnchor { get => string.IsNullOrEmpty(_dynamicAnchor) ? Target.DynamicAnchor : _dynamicAnchor; set => _dynamicAnchor = value; } + public override string DynamicAnchor { get => string.IsNullOrEmpty(_dynamicAnchor) ? Target?.DynamicAnchor : _dynamicAnchor; set => _dynamicAnchor = value; } /// public override IDictionary Definitions { get => _definitions is not null ? _definitions : Target?.Definitions; set => _definitions = value; } /// - public override decimal? V31ExclusiveMaximum { get => _v31ExclusiveMaximum is not null ? _v31ExclusiveMaximum.Value : Target.V31ExclusiveMaximum; set => _v31ExclusiveMaximum = value; } + public override decimal? V31ExclusiveMaximum { get => _v31ExclusiveMaximum is not null ? _v31ExclusiveMaximum.Value : Target?.V31ExclusiveMaximum; set => _v31ExclusiveMaximum = value; } /// - public override decimal? V31ExclusiveMinimum { get => _v31ExclusiveMinimum is not null ? _v31ExclusiveMinimum.Value : Target.V31ExclusiveMinimum; set => _v31ExclusiveMinimum = value; } + public override decimal? V31ExclusiveMinimum { get => _v31ExclusiveMinimum is not null ? _v31ExclusiveMinimum.Value : Target?.V31ExclusiveMinimum; set => _v31ExclusiveMinimum = value; } /// - public override bool UnEvaluatedProperties { get => _unEvaluatedProperties is not null ? _unEvaluatedProperties.Value : Target.UnEvaluatedProperties; set => _unEvaluatedProperties = value; } + public override bool UnEvaluatedProperties { get => _unEvaluatedProperties is not null ? _unEvaluatedProperties.Value : Target?.UnEvaluatedProperties ?? false; set => _unEvaluatedProperties = value; } /// - public override JsonSchemaType? Type { get => _type is not null ? _type.Value : Target.Type; set => _type = value; } + public override JsonSchemaType? Type { get => _type is not null ? _type.Value : Target?.Type; set => _type = value; } /// - public override string Const { get => string.IsNullOrEmpty(_const) ? Target.Const : _const; set => _const = value; } + public override string Const { get => string.IsNullOrEmpty(_const) ? Target?.Const : _const; set => _const = value; } /// - public override string Format { get => string.IsNullOrEmpty(_format) ? Target.Format : _format; set => _format = value; } + public override string Format { get => string.IsNullOrEmpty(_format) ? Target?.Format : _format; set => _format = value; } /// public override string Description { - get => string.IsNullOrEmpty(_description) ? Target.Description : _description; + get => string.IsNullOrEmpty(_description) ? Target?.Description : _description; set => _description = value; } /// - public override decimal? Maximum { get => _maximum is not null ? _maximum : Target.Maximum; set => _maximum = value; } + public override decimal? Maximum { get => _maximum is not null ? _maximum : Target?.Maximum; set => _maximum = value; } /// - public override bool? ExclusiveMaximum { get => _exclusiveMaximum is not null ? _exclusiveMaximum : Target.ExclusiveMaximum; set => _exclusiveMaximum = value; } + public override bool? ExclusiveMaximum { get => _exclusiveMaximum is not null ? _exclusiveMaximum : Target?.ExclusiveMaximum; set => _exclusiveMaximum = value; } /// - public override decimal? Minimum { get => _minimum is not null ? _minimum : Target.Minimum; set => _minimum = value; } + public override decimal? Minimum { get => _minimum is not null ? _minimum : Target?.Minimum; set => _minimum = value; } /// - public override bool? ExclusiveMinimum { get => _exclusiveMinimum is not null ? _exclusiveMinimum : Target.ExclusiveMinimum; set => _exclusiveMinimum = value; } + public override bool? ExclusiveMinimum { get => _exclusiveMinimum is not null ? _exclusiveMinimum : Target?.ExclusiveMinimum; set => _exclusiveMinimum = value; } /// - public override int? MaxLength { get => _maxLength is not null ? _maxLength : Target.MaxLength; set => _maxLength = value; } + public override int? MaxLength { get => _maxLength is not null ? _maxLength : Target?.MaxLength; set => _maxLength = value; } /// - public override int? MinLength { get => _minLength is not null ? _minLength : Target.MinLength; set => _minLength = value; } + public override int? MinLength { get => _minLength is not null ? _minLength : Target?.MinLength; set => _minLength = value; } /// - public override string Pattern { get => string.IsNullOrEmpty(_pattern) ? Target.Pattern : _pattern; set => _pattern = value; } + public override string Pattern { get => string.IsNullOrEmpty(_pattern) ? Target?.Pattern : _pattern; set => _pattern = value; } /// - public override decimal? MultipleOf { get => _multipleOf is not null ? _multipleOf : Target.MultipleOf; set => _multipleOf = value; } + public override decimal? MultipleOf { get => _multipleOf is not null ? _multipleOf : Target?.MultipleOf; set => _multipleOf = value; } /// - public override JsonNode Default { get => _default is not null ? _default : Target.Default; set => _default = value; } + public override JsonNode Default { get => _default is not null ? _default : Target?.Default; set => _default = value; } /// - public override bool ReadOnly { get => _readOnly is not null ? _readOnly.Value : Target.ReadOnly; set => _readOnly = value; } + public override bool ReadOnly { get => _readOnly is not null ? _readOnly.Value : Target?.ReadOnly ?? false; set => _readOnly = value; } /// - public override bool WriteOnly { get => _writeOnly is not null ? _writeOnly.Value : Target.WriteOnly; set => _writeOnly = value; } + public override bool WriteOnly { get => _writeOnly is not null ? _writeOnly.Value : Target?.WriteOnly ?? false; set => _writeOnly = value; } /// public override IList AllOf { get => _allOf is not null ? _allOf : Target?.AllOf; set => _allOf = value; } /// @@ -184,41 +184,41 @@ public override string Description /// public override OpenApiSchema Items { get => _items is not null ? _items : Target?.Items; set => _items = value; } /// - public override int? MaxItems { get => _maxItems is not null ? _maxItems : Target.MaxItems; set => _maxItems = value; } + public override int? MaxItems { get => _maxItems is not null ? _maxItems : Target?.MaxItems; set => _maxItems = value; } /// - public override int? MinItems { get => _minItems is not null ? _minItems : Target.MinItems; set => _minItems = value; } + public override int? MinItems { get => _minItems is not null ? _minItems : Target?.MinItems; set => _minItems = value; } /// - public override bool? UniqueItems { get => _uniqueItems is not null ? _uniqueItems : Target.UniqueItems; set => _uniqueItems = value; } + public override bool? UniqueItems { get => _uniqueItems is not null ? _uniqueItems : Target?.UniqueItems; set => _uniqueItems = value; } /// public override IDictionary Properties { get => _properties is not null ? _properties : Target?.Properties ; set => _properties = value; } /// public override IDictionary PatternProperties { get => _patternProperties is not null ? _patternProperties : Target?.PatternProperties; set => _patternProperties = value; } /// - public override int? MaxProperties { get => _maxProperties is not null ? _maxProperties : Target.MaxProperties; set => _maxProperties = value; } + public override int? MaxProperties { get => _maxProperties is not null ? _maxProperties : Target?.MaxProperties; set => _maxProperties = value; } /// - public override int? MinProperties { get => _minProperties is not null ? _minProperties : Target.MinProperties; set => _minProperties = value; } + public override int? MinProperties { get => _minProperties is not null ? _minProperties : Target?.MinProperties; set => _minProperties = value; } /// - public override bool AdditionalPropertiesAllowed { get => _additionalPropertiesAllowed is not null ? _additionalPropertiesAllowed.Value : Target.AdditionalPropertiesAllowed; set => _additionalPropertiesAllowed = value; } + public override bool AdditionalPropertiesAllowed { get => _additionalPropertiesAllowed is not null ? _additionalPropertiesAllowed.Value : Target?.AdditionalPropertiesAllowed ?? true; set => _additionalPropertiesAllowed = value; } /// public override OpenApiSchema AdditionalProperties { get => _additionalProperties is not null ? _additionalProperties : Target?.AdditionalProperties; set => _additionalProperties = value; } /// - public override OpenApiDiscriminator Discriminator { get => _discriminator is not null ? _discriminator : Target.Discriminator; set => _discriminator = value; } + public override OpenApiDiscriminator Discriminator { get => _discriminator is not null ? _discriminator : Target?.Discriminator; set => _discriminator = value; } /// - public override JsonNode Example { get => _example is not null ? _example : Target.Example; set => _example = value; } + public override JsonNode Example { get => _example is not null ? _example : Target?.Example; set => _example = value; } /// public override IList Examples { get => _examples is not null ? _examples : Target?.Examples; set => _examples = value; } /// public override IList Enum { get => _enum is not null ? _enum : Target?.Enum; set => _enum = value; } /// - public override bool Nullable { get => _nullable is null ? Target.Nullable : _nullable.Value; set => _nullable = value; } + public override bool Nullable { get => _nullable is not null ? _nullable.Value : Target?.Nullable ?? false; set => _nullable = value; } /// - public override bool UnevaluatedProperties { get => _unevaluatedProperties is not null ? _unevaluatedProperties.Value : Target.UnevaluatedProperties; set => _unevaluatedProperties = value; } + public override bool UnevaluatedProperties { get => _unevaluatedProperties is not null ? _unevaluatedProperties.Value : Target?.UnevaluatedProperties ?? false; set => _unevaluatedProperties = value; } /// public override OpenApiExternalDocs ExternalDocs { get => _externalDocs is not null ? _externalDocs : Target?.ExternalDocs; set => _externalDocs = value; } /// - public override bool Deprecated { get => _deprecated is not null ? _deprecated.Value : Target.Deprecated; set => _deprecated = value; } + public override bool Deprecated { get => _deprecated is not null ? _deprecated.Value : Target?.Deprecated ?? false; set => _deprecated = value; } /// - public override OpenApiXml Xml { get => _xml is not null ? _xml : Target.Xml; set => _xml = value; } + public override OpenApiXml Xml { get => _xml is not null ? _xml : Target?.Xml; set => _xml = value; } /// public override IDictionary Extensions { get => _extensions is not null ? _extensions : Target?.Extensions; set => _extensions = value; } From b727581d6fd1d814b5c1887400cb48f06dd96362 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 31 Dec 2024 08:30:38 -0500 Subject: [PATCH 10/15] fix: updates public api file Signed-off-by: Vincent Biret --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 642dd0b82..f18916ab0 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -571,6 +571,7 @@ namespace Microsoft.OpenApi.Models public System.Collections.Generic.IList? Tags { get; set; } public System.Collections.Generic.IDictionary? Webhooks { get; set; } public Microsoft.OpenApi.Services.OpenApiWorkspace? Workspace { get; set; } + public bool AddComponentSchema(string id, Microsoft.OpenApi.Models.OpenApiSchema openApiSchema) { } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } @@ -1534,6 +1535,7 @@ namespace Microsoft.OpenApi.Services public System.Uri GetDocumentId(string key) { } public bool RegisterComponent(string location, T component) { } public void RegisterComponents(Microsoft.OpenApi.Models.OpenApiDocument document) { } + public bool RegisterSchemaForDocument(Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Microsoft.OpenApi.Models.OpenApiSchema openApiSchema, string id) { } public T? ResolveReference(string location) { } } public class OperationSearch : Microsoft.OpenApi.Services.OpenApiVisitorBase From 5b4c94e71bff02202fddda6ae4b8c00f8b7b1b6f Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 31 Dec 2024 09:10:57 -0500 Subject: [PATCH 11/15] chore: linting Signed-off-by: Vincent Biret --- .../Models/References/OpenApiTagReference.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs index 664f784f3..64b815fd4 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs @@ -80,7 +80,6 @@ public override void SerializeAsV3(IOpenApiWriter writer) if (!writer.GetSettings().ShouldInlineReference(_reference)) { _reference.SerializeAsV3(writer); - return; } else { @@ -94,7 +93,6 @@ public override void SerializeAsV31(IOpenApiWriter writer) if (!writer.GetSettings().ShouldInlineReference(_reference)) { _reference.SerializeAsV31(writer); - return; } else { @@ -108,7 +106,6 @@ public override void SerializeAsV2(IOpenApiWriter writer) if (!writer.GetSettings().ShouldInlineReference(_reference)) { _reference.SerializeAsV2(writer); - return; } else { @@ -119,7 +116,7 @@ public override void SerializeAsV2(IOpenApiWriter writer) /// private void SerializeInternal(IOpenApiWriter writer) { - Utils.CheckArgumentNull(writer);; + Utils.CheckArgumentNull(writer); writer.WriteValue(Name); } } From e366566865ade9e6ada41033459ee9de9709ab29 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 31 Dec 2024 09:25:37 -0500 Subject: [PATCH 12/15] chore: linting Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceable.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceable.cs b/src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceable.cs index 0920fb1ef..43088bf6b 100644 --- a/src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceable.cs +++ b/src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceable.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Interfaces { From a2317480250129c7091ef7a90e2eb2a95d9ac48b Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 31 Dec 2024 14:02:30 -0500 Subject: [PATCH 13/15] chore: linting Signed-off-by: Vincent Biret --- .../Workspaces/OpenApiReferencableTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiReferencableTests.cs b/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiReferencableTests.cs index e015da4f4..ecadc2017 100644 --- a/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiReferencableTests.cs +++ b/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiReferencableTests.cs @@ -45,7 +45,6 @@ public class OpenApiReferencableTests { "link1", new OpenApiLink() } } }; - private static readonly OpenApiSchema _schemaFragment = new OpenApiSchema(); private static readonly OpenApiSecurityScheme _securitySchemeFragment = new OpenApiSecurityScheme(); private static readonly OpenApiTag _tagFragment = new OpenApiTag(); From 8a73b540e88bd18aeef27e86e41dbec3e7e1d2cd Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 6 Jan 2025 12:50:13 -0500 Subject: [PATCH 14/15] fix: adds support for all component types --- .../Models/OpenApiDocument.cs | 62 ++++++++++++++++--- .../Services/OpenApiWorkspace.cs | 38 ++++++++---- 2 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index d680495c8..8cc1fda6c 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -588,19 +588,65 @@ public static ReadResult Parse(string input, return OpenApiModelFactory.Parse(input, format, settings); } /// - /// Adds a schema to the components object of the current document. + /// Adds a component to the components object of the current document and registers it to the underlying workspace. /// - /// The schema to add + /// The component to add /// The id for the component - /// Whether the schema was added to the components. - public bool AddComponentSchema(string id, OpenApiSchema openApiSchema) + /// The type of the component + /// Whether the component was added to the components. + /// Thrown when the component is null. + /// Thrown when the id is null or empty. + public bool AddComponent(string id, T componentToRegister) { - Utils.CheckArgumentNull(openApiSchema); + Utils.CheckArgumentNull(componentToRegister); Utils.CheckArgumentNullOrEmpty(id); Components ??= new(); - Components.Schemas ??= new Dictionary(); - Components.Schemas.Add(id, openApiSchema); - return Workspace?.RegisterSchemaForDocument(this, openApiSchema, id) ?? false; + switch (componentToRegister) + { + case OpenApiSchema openApiSchema: + Components.Schemas ??= new Dictionary(); + Components.Schemas.Add(id, openApiSchema); + break; + case OpenApiParameter openApiParameter: + Components.Parameters ??= new Dictionary(); + Components.Parameters.Add(id, openApiParameter); + break; + case OpenApiResponse openApiResponse: + Components.Responses ??= new Dictionary(); + Components.Responses.Add(id, openApiResponse); + break; + case OpenApiRequestBody openApiRequestBody: + Components.RequestBodies ??= new Dictionary(); + Components.RequestBodies.Add(id, openApiRequestBody); + break; + case OpenApiLink openApiLink: + Components.Links ??= new Dictionary(); + Components.Links.Add(id, openApiLink); + break; + case OpenApiCallback openApiCallback: + Components.Callbacks ??= new Dictionary(); + Components.Callbacks.Add(id, openApiCallback); + break; + case OpenApiPathItem openApiPathItem: + Components.PathItems ??= new Dictionary(); + Components.PathItems.Add(id, openApiPathItem); + break; + case OpenApiExample openApiExample: + Components.Examples ??= new Dictionary(); + Components.Examples.Add(id, openApiExample); + break; + case OpenApiHeader openApiHeader: + Components.Headers ??= new Dictionary(); + Components.Headers.Add(id, openApiHeader); + break; + case OpenApiSecurityScheme openApiSecurityScheme: + Components.SecuritySchemes ??= new Dictionary(); + Components.SecuritySchemes.Add(id, openApiSecurityScheme); + break; + default: + throw new ArgumentException($"Component type {componentToRegister!.GetType().Name} is not supported."); + } + return Workspace?.RegisterComponentForDocument(this, componentToRegister, id) ?? false; } } diff --git a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs index 1d8aa2799..f92e6f322 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs @@ -145,26 +145,40 @@ private string getBaseUri(OpenApiDocument openApiDocument) } /// - /// Registers a schema for a document in the workspace + /// Registers a component for a document in the workspace /// - /// The document to register the schema for. - /// The schema to register. - /// The id of the schema. - /// true if the schema is successfully registered; otherwise false. + /// The document to register the component for. + /// The component to register. + /// The id of the component. + /// The type of the component to register. + /// true if the component is successfully registered; otherwise false. /// openApiDocument is null - /// openApiSchema is null + /// componentToRegister is null /// id is null or empty - public bool RegisterSchemaForDocument(OpenApiDocument openApiDocument, OpenApiSchema openApiSchema, string id) + public bool RegisterComponentForDocument(OpenApiDocument openApiDocument, T componentToRegister, string id) { Utils.CheckArgumentNull(openApiDocument); - Utils.CheckArgumentNull(openApiSchema); + Utils.CheckArgumentNull(componentToRegister); Utils.CheckArgumentNullOrEmpty(id); var baseUri = getBaseUri(openApiDocument); - var location = baseUri + ReferenceType.Schema.GetDisplayName() + ComponentSegmentSeparator + id; - - return RegisterComponent(location, openApiSchema); + var location = componentToRegister switch + { + OpenApiSchema => baseUri + ReferenceType.Schema.GetDisplayName() + ComponentSegmentSeparator + id, + OpenApiParameter => baseUri + ReferenceType.Parameter.GetDisplayName() + ComponentSegmentSeparator + id, + OpenApiResponse => baseUri + ReferenceType.Response.GetDisplayName() + ComponentSegmentSeparator + id, + OpenApiRequestBody => baseUri + ReferenceType.RequestBody.GetDisplayName() + ComponentSegmentSeparator + id, + OpenApiLink => baseUri + ReferenceType.Link.GetDisplayName() + ComponentSegmentSeparator + id, + OpenApiCallback => baseUri + ReferenceType.Callback.GetDisplayName() + ComponentSegmentSeparator + id, + OpenApiPathItem => baseUri + ReferenceType.PathItem.GetDisplayName() + ComponentSegmentSeparator + id, + OpenApiExample => baseUri + ReferenceType.Example.GetDisplayName() + ComponentSegmentSeparator + id, + OpenApiHeader => baseUri + ReferenceType.Header.GetDisplayName() + ComponentSegmentSeparator + id, + OpenApiSecurityScheme => baseUri + ReferenceType.SecurityScheme.GetDisplayName() + ComponentSegmentSeparator + id, + _ => throw new ArgumentException($"Invalid component type {componentToRegister.GetType().Name}"), + }; + + return RegisterComponent(location, componentToRegister); } /// @@ -173,7 +187,7 @@ public bool RegisterSchemaForDocument(OpenApiDocument openApiDocument, OpenApiSc /// /// /// true if the component is successfully registered; otherwise false. - public bool RegisterComponent(string location, T component) + internal bool RegisterComponent(string location, T component) { var uri = ToLocationUrl(location); if (component is IOpenApiReferenceable referenceable) From 45d49f1a9dbac0d83839fc557933585e25e120b4 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 6 Jan 2025 12:56:32 -0500 Subject: [PATCH 15/15] chore: updates public api test Signed-off-by: Vincent Biret --- .../Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index f18916ab0..3fee85452 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -571,7 +571,7 @@ namespace Microsoft.OpenApi.Models public System.Collections.Generic.IList? Tags { get; set; } public System.Collections.Generic.IDictionary? Webhooks { get; set; } public Microsoft.OpenApi.Services.OpenApiWorkspace? Workspace { get; set; } - public bool AddComponentSchema(string id, Microsoft.OpenApi.Models.OpenApiSchema openApiSchema) { } + public bool AddComponent(string id, T componentToRegister) { } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } @@ -1533,9 +1533,8 @@ namespace Microsoft.OpenApi.Services public int ComponentsCount() { } public bool Contains(string location) { } public System.Uri GetDocumentId(string key) { } - public bool RegisterComponent(string location, T component) { } + public bool RegisterComponentForDocument(Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, T componentToRegister, string id) { } public void RegisterComponents(Microsoft.OpenApi.Models.OpenApiDocument document) { } - public bool RegisterSchemaForDocument(Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Microsoft.OpenApi.Models.OpenApiSchema openApiSchema, string id) { } public T? ResolveReference(string location) { } } public class OperationSearch : Microsoft.OpenApi.Services.OpenApiVisitorBase