From c06a9f1858a435851c87418a1cc78da91490e0eb Mon Sep 17 00:00:00 2001 From: HavenDV Date: Fri, 28 Jun 2024 00:21:33 +0400 Subject: [PATCH 1/9] feat: Added nullable enable to OpenApiComponents. --- .../Models/OpenApiComponents.cs | 25 ++++++++++--------- .../Formatters/PowerShellFormatterTests.cs | 8 +++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiComponents.cs b/src/Microsoft.OpenApi/Models/OpenApiComponents.cs index 4af4248ab..890dbb36f 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiComponents.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiComponents.cs @@ -8,6 +8,7 @@ using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Writers; +#nullable enable namespace Microsoft.OpenApi.Models { @@ -19,60 +20,60 @@ public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible /// /// An object to hold reusable Objects. /// - public IDictionary Schemas { get; set; } = new Dictionary(); + public IDictionary? Schemas { get; set; } = new Dictionary(); /// /// An object to hold reusable Objects. /// - public virtual IDictionary Responses { get; set; } = new Dictionary(); + public virtual IDictionary? Responses { get; set; } = new Dictionary(); /// /// An object to hold reusable Objects. /// - public virtual IDictionary Parameters { get; set; } = + public virtual IDictionary? Parameters { get; set; } = new Dictionary(); /// /// An object to hold reusable Objects. /// - public virtual IDictionary Examples { get; set; } = new Dictionary(); + public virtual IDictionary? Examples { get; set; } = new Dictionary(); /// /// An object to hold reusable Objects. /// - public virtual IDictionary RequestBodies { get; set; } = + public virtual IDictionary? RequestBodies { get; set; } = new Dictionary(); /// /// An object to hold reusable Objects. /// - public virtual IDictionary Headers { get; set; } = new Dictionary(); + public virtual IDictionary? Headers { get; set; } = new Dictionary(); /// /// An object to hold reusable Objects. /// - public virtual IDictionary SecuritySchemes { get; set; } = + public virtual IDictionary? SecuritySchemes { get; set; } = new Dictionary(); /// /// An object to hold reusable Objects. /// - public virtual IDictionary Links { get; set; } = new Dictionary(); + public virtual IDictionary? Links { get; set; } = new Dictionary(); /// /// An object to hold reusable Objects. /// - public virtual IDictionary Callbacks { get; set; } = new Dictionary(); + public virtual IDictionary? Callbacks { get; set; } = new Dictionary(); /// /// An object to hold reusable Object. /// - public virtual IDictionary PathItems { get; set; } = new Dictionary(); + public virtual IDictionary? PathItems { get; set; } = new Dictionary(); /// /// This object MAY be extended with Specification Extensions. /// - public virtual IDictionary Extensions { get; set; } = new Dictionary(); + public virtual IDictionary? Extensions { get; set; } = new Dictionary(); /// /// Parameter-less constructor @@ -82,7 +83,7 @@ public OpenApiComponents() { } /// /// Initializes a copy of an object /// - public OpenApiComponents(OpenApiComponents components) + public OpenApiComponents(OpenApiComponents? components) { Schemas = components?.Schemas != null ? new Dictionary(components.Schemas) : null; Responses = components?.Responses != null ? new Dictionary(components.Responses) : null; diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs index 6bd55a4aa..33996f044 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs @@ -59,9 +59,9 @@ public void RemoveAnyOfAndOneOfFromSchema() var walker = new OpenApiWalker(powerShellFormatter); walker.Walk(openApiDocument); - var testSchema = openApiDocument.Components.Schemas["TestSchema"]; - var averageAudioDegradationProperty = testSchema.GetProperties()?.GetValueOrDefault("averageAudioDegradation"); - var defaultPriceProperty = testSchema.GetProperties()?.GetValueOrDefault("defaultPrice"); + var testSchema = openApiDocument.Components.Schemas?["TestSchema"]; + var averageAudioDegradationProperty = testSchema?.GetProperties()?.GetValueOrDefault("averageAudioDegradation"); + var defaultPriceProperty = testSchema?.GetProperties()?.GetValueOrDefault("defaultPrice"); // Assert Assert.Null(averageAudioDegradationProperty?.GetAnyOf()); @@ -71,7 +71,7 @@ public void RemoveAnyOfAndOneOfFromSchema() Assert.Null(defaultPriceProperty?.GetOneOf()); Assert.Equal(SchemaValueType.Number, defaultPriceProperty?.GetJsonType()); Assert.Equal("double", defaultPriceProperty?.GetFormat()?.Key); - Assert.NotNull(testSchema.GetAdditionalProperties()); + Assert.NotNull(testSchema?.GetAdditionalProperties()); } [Fact] From 2deb4aa30b67354ad887c27edb675d976967216c Mon Sep 17 00:00:00 2001 From: HavenDV Date: Fri, 28 Jun 2024 00:38:28 +0400 Subject: [PATCH 2/9] feat: Added nullable enable to OpenApiDocument. --- src/Microsoft.OpenApi.Hidi/OpenApiService.cs | 14 ++-- .../Models/OpenApiDocument.cs | 79 ++++++++++--------- .../Services/OpenApiWalker.cs | 10 +-- .../Services/OpenApiWorkspace.cs | 4 +- .../Writers/OpenApiWriterExtensions.cs | 6 +- .../Formatters/PowerShellFormatterTests.cs | 4 +- .../Services/OpenApiFilterServiceTests.cs | 6 +- 7 files changed, 66 insertions(+), 57 deletions(-) diff --git a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs index c4d34d4cf..ce4055df2 100644 --- a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -185,7 +185,7 @@ private static OpenApiDocument ApplyFilters(HidiOptions options, ILogger logger, stopwatch.Start(); document = OpenApiFilterService.CreateFilteredDocument(document, predicate); stopwatch.Stop(); - logger.LogTrace("{Timestamp}ms: Creating filtered OpenApi document with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths.Count); + logger.LogTrace("{Timestamp}ms: Creating filtered OpenApi document with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths?.Count); } return document; @@ -248,7 +248,7 @@ private static async Task GetOpenApi(HidiOptions options, strin document = await ConvertCsdlToOpenApi(filteredStream ?? stream, format, metadataVersion, options.SettingsConfig, cancellationToken).ConfigureAwait(false); stopwatch.Stop(); - logger.LogTrace("{Timestamp}ms: Generated OpenAPI with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths.Count); + logger.LogTrace("{Timestamp}ms: Generated OpenAPI with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths?.Count); } } else if (!string.IsNullOrEmpty(options.OpenApi)) @@ -659,7 +659,7 @@ internal static void WriteTreeDocumentAsMarkdown(string openapiUrl, OpenApiDocum { var rootNode = OpenApiUrlTreeNode.Create(document, "main"); - writer.WriteLine("# " + document.Info.Title); + writer.WriteLine("# " + document.Info?.Title); writer.WriteLine(); writer.WriteLine("API Description: " + openapiUrl); @@ -695,7 +695,7 @@ internal static void WriteTreeDocumentAsHtml(string sourceUrl, OpenApiDocument d """); - writer.WriteLine("

" + document.Info.Title + "

"); + writer.WriteLine("

" + document.Info?.Title + "

"); writer.WriteLine(); writer.WriteLine($"

API Description: {sourceUrl}

"); @@ -766,8 +766,8 @@ internal static async Task PluginManifest(HidiOptions options, ILogger logger, C // Create OpenAIPluginManifest from ApiDependency and OpenAPI document var manifest = new OpenAIPluginManifest { - NameForHuman = document.Info.Title, - DescriptionForHuman = document.Info.Description, + NameForHuman = document.Info?.Title, + DescriptionForHuman = document.Info?.Description, Api = new() { Type = "openapi", diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 64dc1d2d4..58cf153b4 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -16,6 +16,8 @@ using Microsoft.OpenApi.Services; using Microsoft.OpenApi.Writers; +#nullable enable + namespace Microsoft.OpenApi.Models { /// @@ -26,60 +28,60 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IBaseDo /// /// Related workspace containing OpenApiDocuments that are referenced in this document /// - public OpenApiWorkspace Workspace { get; set; } + public OpenApiWorkspace? Workspace { get; set; } /// /// REQUIRED. Provides metadata about the API. The metadata MAY be used by tooling as required. /// - public OpenApiInfo Info { get; set; } + public OpenApiInfo? Info { get; set; } /// /// The default value for the $schema keyword within Schema Objects contained within this OAS document. This MUST be in the form of a URI. /// - public string JsonSchemaDialect { get; set; } + public string? JsonSchemaDialect { get; set; } /// /// An array of Server Objects, which provide connectivity information to a target server. /// - public IList Servers { get; set; } = new List(); + public IList? Servers { get; set; } = new List(); /// /// REQUIRED. The available paths and operations for the API. /// - public OpenApiPaths Paths { get; set; } + public OpenApiPaths? Paths { get; set; } /// /// The incoming webhooks that MAY be received as part of this API and that the API consumer MAY choose to implement. /// A map of requests initiated other than by an API call, for example by an out of band registration. /// The key name is a unique string to refer to each webhook, while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider and the expected responses /// - public IDictionary Webhooks { get; set; } = new Dictionary(); + public IDictionary? Webhooks { get; set; } = new Dictionary(); /// /// An element to hold various schemas for the specification. /// - public OpenApiComponents Components { get; set; } + public OpenApiComponents? Components { get; set; } /// /// A declaration of which security mechanisms can be used across the API. /// - public IList SecurityRequirements { get; set; } = + public IList? SecurityRequirements { get; set; } = new List(); /// /// A list of tags used by the specification with additional metadata. /// - public IList Tags { get; set; } = new List(); + public IList? Tags { get; set; } = new List(); /// /// Additional external documentation. /// - public OpenApiExternalDocs ExternalDocs { get; set; } + public OpenApiExternalDocs? ExternalDocs { get; set; } /// /// This object MAY be extended with Specification Extensions. /// - public IDictionary Extensions { get; set; } = new Dictionary(); + public IDictionary? Extensions { get; set; } = new Dictionary(); /// /// The unique hash code of the generated OpenAPI document @@ -97,13 +99,13 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IBaseDo public OpenApiDocument() { Workspace = new OpenApiWorkspace(); - BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid().ToString()); + BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid().ToString()); } /// /// Initializes a copy of an an object /// - public OpenApiDocument(OpenApiDocument document) + public OpenApiDocument(OpenApiDocument? document) { Workspace = document?.Workspace != null ? new(document?.Workspace) : null; Info = document?.Info != null ? new(document?.Info) : null; @@ -116,6 +118,7 @@ public OpenApiDocument(OpenApiDocument document) Tags = document?.Tags != null ? new List(document.Tags) : null; ExternalDocs = document?.ExternalDocs != null ? new(document?.ExternalDocs) : null; Extensions = document?.Extensions != null ? new Dictionary(document.Extensions) : null; + BaseUri = document?.BaseUri != null ? document.BaseUri : new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid().ToString()); } /// @@ -242,8 +245,10 @@ public void SerializeAsV2(IOpenApiWriter writer) if (loops.TryGetValue(typeof(JsonSchema), out List schemas)) { - var openApiSchemas = schemas.Cast().Distinct() - .ToDictionary(k => k.GetRef().ToString()); + var openApiSchemas = schemas.Cast() + .Distinct() + .Where(s => s.GetRef() != null) + .ToDictionary(k => k.GetRef()!.ToString()); foreach (var schema in openApiSchemas.Values.ToList()) { @@ -377,7 +382,7 @@ private static string ParseServerUrl(OpenApiServer server) return parsedUrl; } - private static void WriteHostInfoV2(IOpenApiWriter writer, IList servers) + private static void WriteHostInfoV2(IOpenApiWriter writer, IList? servers) { if (servers == null || !servers.Any()) { @@ -457,7 +462,7 @@ public void SetReferenceHostDocument() /// /// Load the referenced object from a object /// - internal T ResolveReferenceTo(OpenApiReference reference) where T : class, IOpenApiReferenceable + internal T? ResolveReferenceTo(OpenApiReference reference) where T : class, IOpenApiReferenceable { if (reference.IsExternal) { @@ -472,7 +477,7 @@ internal T ResolveReferenceTo(OpenApiReference reference) where T : class, IO /// /// Load the referenced object from a object /// - public IOpenApiReferenceable ResolveReference(OpenApiReference reference) + public IOpenApiReferenceable? ResolveReference(OpenApiReference reference) { return ResolveReference(reference, false); } @@ -482,7 +487,7 @@ public IOpenApiReferenceable ResolveReference(OpenApiReference reference) /// /// /// A JsonSchema ref. - public JsonSchema ResolveJsonSchemaReference(Uri referenceUri) + public JsonSchema? ResolveJsonSchemaReference(Uri referenceUri) { const char pound = '#'; string uriLocation; @@ -492,7 +497,7 @@ public JsonSchema ResolveJsonSchemaReference(Uri referenceUri) { // External reference, ex: ./TodoReference.yaml#/components/schemas/todo string externalUri = referenceUri.OriginalString.Split(pound).First(); - Uri externalDocId = Workspace.GetDocumentId(externalUri); + Uri? externalDocId = Workspace?.GetDocumentId(externalUri); string relativePath = referenceUri.OriginalString.Split(pound).Last(); uriLocation = externalDocId + relativePath; } @@ -501,7 +506,7 @@ public JsonSchema ResolveJsonSchemaReference(Uri referenceUri) uriLocation = BaseUri + referenceUri.ToString().TrimStart(pound); } - return (JsonSchema)Workspace.ResolveReference(uriLocation); + return Workspace?.ResolveReference(uriLocation) as JsonSchema; } /// @@ -541,7 +546,7 @@ private static string ConvertByteArrayToString(byte[] hash) /// /// Load the referenced object from a object /// - internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool useExternal) + internal IOpenApiReferenceable? ResolveReference(OpenApiReference? reference, bool useExternal) { if (reference == null) { @@ -556,7 +561,7 @@ internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool // Special case for Tag if (reference.Type == ReferenceType.Tag) { - foreach (var tag in this.Tags) + foreach (var tag in this.Tags ?? Enumerable.Empty()) { if (tag.Name == reference.Id) { @@ -572,10 +577,10 @@ internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool string relativePath = OpenApiConstants.ComponentsSegment + reference.Type.GetDisplayName() + "/" + reference.Id; uriLocation = useExternal - ? Workspace.GetDocumentId(reference.ExternalResource)?.OriginalString + relativePath + ? Workspace?.GetDocumentId(reference.ExternalResource)?.OriginalString + relativePath : BaseUri + relativePath; - return Workspace.ResolveReference(uriLocation); + return Workspace?.ResolveReference(uriLocation); } /// @@ -584,7 +589,7 @@ internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool /// The path to the OpenAPI file. /// /// - public static ReadResult Load(string url, OpenApiReaderSettings settings = null) + public static ReadResult Load(string url, OpenApiReaderSettings? settings = null) { return OpenApiModelFactory.Load(url, settings); } @@ -598,7 +603,7 @@ public static ReadResult Load(string url, OpenApiReaderSettings settings = null) /// public static ReadResult Load(Stream stream, string format, - OpenApiReaderSettings settings = null) + OpenApiReaderSettings? settings = null) { return OpenApiModelFactory.Load(stream, format, settings); } @@ -612,7 +617,7 @@ public static ReadResult Load(Stream stream, /// public static ReadResult Load(TextReader input, string format, - OpenApiReaderSettings settings = null) + OpenApiReaderSettings? settings = null) { return OpenApiModelFactory.Load(input, format, settings); } @@ -623,7 +628,7 @@ public static ReadResult Load(TextReader input, /// The path to the OpenAPI file. /// The OpenApi reader settings. /// - public static async Task LoadAsync(string url, OpenApiReaderSettings settings = null) + public static async Task LoadAsync(string url, OpenApiReaderSettings? settings = null) { return await OpenApiModelFactory.LoadAsync(url, settings); } @@ -636,7 +641,7 @@ public static async Task LoadAsync(string url, OpenApiReaderSettings /// The OpenApi reader settings. /// Propagates information about operation cancelling. /// - public static async Task LoadAsync(Stream stream, string format, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default) + public static async Task LoadAsync(Stream stream, string format, OpenApiReaderSettings? settings = null, CancellationToken cancellationToken = default) { return await OpenApiModelFactory.LoadAsync(stream, format, settings, cancellationToken); } @@ -648,7 +653,7 @@ public static async Task LoadAsync(Stream stream, string format, Ope /// The OpenAPI format to use during parsing. /// The OpenApi reader settings. /// - public static async Task LoadAsync(TextReader input, string format, OpenApiReaderSettings settings = null) + public static async Task LoadAsync(TextReader input, string format, OpenApiReaderSettings? settings = null) { return await OpenApiModelFactory.LoadAsync(input, format, settings); } @@ -661,8 +666,8 @@ public static async Task LoadAsync(TextReader input, string format, /// /// public static ReadResult Parse(string input, - string format = null, - OpenApiReaderSettings settings = null) + string? format = null, + OpenApiReaderSettings? settings = null) { return OpenApiModelFactory.Parse(input, format, settings); } @@ -674,18 +679,18 @@ public static ReadResult Parse(string input, /// /// /// - public JsonSchema FindSubschema(Json.Pointer.JsonPointer pointer, EvaluationOptions options) + public JsonSchema? FindSubschema(Json.Pointer.JsonPointer pointer, EvaluationOptions options) { var locationUri = string.Concat(BaseUri, pointer); - return (JsonSchema)Workspace.ResolveReference(locationUri); + return Workspace?.ResolveReference(locationUri) as JsonSchema; } } internal class FindSchemaReferences : OpenApiVisitorBase { - private Dictionary Schemas; + private Dictionary? Schemas; - public static void ResolveSchemas(OpenApiComponents components, Dictionary schemas) + public static void ResolveSchemas(OpenApiComponents? components, Dictionary schemas) { var visitor = new FindSchemaReferences(); visitor.Schemas = schemas; diff --git a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs index b934074f9..81a646787 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs @@ -106,11 +106,11 @@ internal void Walk(OpenApiExternalDocs externalDocs) _visitor.Visit(externalDocs); } - +#nullable enable /// /// Visits and child objects /// - internal void Walk(OpenApiComponents components) + internal void Walk(OpenApiComponents? components) { if (components == null) { @@ -119,11 +119,6 @@ internal void Walk(OpenApiComponents components) _visitor.Visit(components); - if (components == null) - { - return; - } - Walk(OpenApiConstants.Schemas, () => { if (components.Schemas != null) @@ -237,6 +232,7 @@ internal void Walk(OpenApiComponents components) Walk(components as IOpenApiExtensible); } +#nullable restore /// /// Visits and child objects /// diff --git a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs index f8ca95a13..b7697cd8e 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs @@ -131,13 +131,14 @@ public bool Contains(string location) return _IOpenApiReferenceableRegistry.ContainsKey(key) || _jsonSchemaRegistry.ContainsKey(key) || _artifactsRegistry.ContainsKey(key); } +#nullable enable /// /// Resolves a reference given a key. /// /// /// /// The resolved reference. - public T ResolveReference(string location) + public T? ResolveReference(string location) { if (string.IsNullOrEmpty(location)) return default; @@ -157,6 +158,7 @@ public T ResolveReference(string location) return default; } +#nullable restore private Uri ToLocationUrl(string location) { diff --git a/src/Microsoft.OpenApi/Writers/OpenApiWriterExtensions.cs b/src/Microsoft.OpenApi/Writers/OpenApiWriterExtensions.cs index 0ab285c93..13212b599 100644 --- a/src/Microsoft.OpenApi/Writers/OpenApiWriterExtensions.cs +++ b/src/Microsoft.OpenApi/Writers/OpenApiWriterExtensions.cs @@ -126,6 +126,7 @@ public static void WriteProperty(this IOpenApiWriter writer, string name, T v writer.WriteValue(value); } +#nullable enable /// /// Write the optional Open API object/element. /// @@ -137,7 +138,7 @@ public static void WriteProperty(this IOpenApiWriter writer, string name, T v public static void WriteOptionalObject( this IOpenApiWriter writer, string name, - T value, + T? value, Action action) { if (value != null) @@ -162,7 +163,7 @@ public static void WriteOptionalObject( public static void WriteRequiredObject( this IOpenApiWriter writer, string name, - T value, + T? value, Action action) { Utils.CheckArgumentNull(action); @@ -178,6 +179,7 @@ public static void WriteRequiredObject( writer.WriteEndObject(); } } +#nullable restore /// /// Write the optional of collection string. diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs index 33996f044..4a662be67 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs @@ -59,7 +59,7 @@ public void RemoveAnyOfAndOneOfFromSchema() var walker = new OpenApiWalker(powerShellFormatter); walker.Walk(openApiDocument); - var testSchema = openApiDocument.Components.Schemas?["TestSchema"]; + var testSchema = openApiDocument.Components?.Schemas?["TestSchema"]; var averageAudioDegradationProperty = testSchema?.GetProperties()?.GetValueOrDefault("averageAudioDegradation"); var defaultPriceProperty = testSchema?.GetProperties()?.GetValueOrDefault("defaultPrice"); @@ -85,7 +85,7 @@ public void ResolveFunctionParameters() var walker = new OpenApiWalker(powerShellFormatter); walker.Walk(openApiDocument); - var idsParameter = openApiDocument.Paths["/foo"].Operations[OperationType.Get].Parameters.Where(static p => p.Name == "ids").FirstOrDefault(); + var idsParameter = openApiDocument.Paths?["/foo"].Operations[OperationType.Get].Parameters.Where(static p => p.Name == "ids").FirstOrDefault(); // Assert Assert.Null(idsParameter?.Content); diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs index 5fb1b15f9..02e6cedb0 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs @@ -43,6 +43,7 @@ public void ReturnFilteredOpenApiDocumentBasedOnOperationIdsAndTags(string? oper // Assert Assert.NotNull(subsetOpenApiDocument); + Assert.NotNull(subsetOpenApiDocument.Paths); Assert.NotEmpty(subsetOpenApiDocument.Paths); Assert.Equal(expectedPathCount, subsetOpenApiDocument.Paths.Count); } @@ -62,6 +63,7 @@ public void ReturnFilteredOpenApiDocumentBasedOnPostmanCollection() // Assert Assert.NotNull(subsetOpenApiDocument); + Assert.NotNull(subsetOpenApiDocument.Paths); Assert.NotEmpty(subsetOpenApiDocument.Paths); Assert.Equal(3, subsetOpenApiDocument.Paths.Count); } @@ -150,10 +152,11 @@ public void ContinueProcessingWhenUrlsInCollectionAreMissingFromSourceDocument() var pathCount = requestUrls.Count; var predicate = OpenApiFilterService.CreatePredicate(requestUrls: requestUrls, source: _openApiDocumentMock); var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(_openApiDocumentMock, predicate); - var subsetPathCount = subsetOpenApiDocument.Paths.Count; + var subsetPathCount = subsetOpenApiDocument.Paths?.Count; // Assert Assert.NotNull(subsetOpenApiDocument); + Assert.NotNull(subsetOpenApiDocument.Paths); Assert.NotEmpty(subsetOpenApiDocument.Paths); Assert.Equal(2, subsetPathCount); Assert.NotEqual(pathCount, subsetPathCount); @@ -180,6 +183,7 @@ public void ReturnsPathParametersOnSlicingBasedOnOperationIdsOrTags(string? oper var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(_openApiDocumentMock, predicate); // Assert + Assert.NotNull(subsetOpenApiDocument.Paths); foreach (var pathItem in subsetOpenApiDocument.Paths) { Assert.True(pathItem.Value.Parameters.Any()); From 91462693f765eb71421ea2c9bbb48cbd64563369 Mon Sep 17 00:00:00 2001 From: HavenDV Date: Fri, 28 Jun 2024 00:40:11 +0400 Subject: [PATCH 3/9] feat: Added nullable enable to OpenApiMediaType. --- src/Microsoft.OpenApi/Models/OpenApiMediaType.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs index cb97f3185..04839675e 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs @@ -9,6 +9,8 @@ using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Writers; +#nullable enable + namespace Microsoft.OpenApi.Models { /// @@ -16,12 +18,12 @@ namespace Microsoft.OpenApi.Models /// public class OpenApiMediaType : IOpenApiSerializable, IOpenApiExtensible { - private JsonSchema _schema; + private JsonSchema? _schema; /// /// The schema defining the type used for the request body. /// - public virtual JsonSchema Schema + public virtual JsonSchema? Schema { get => _schema; set => _schema = value; @@ -31,13 +33,13 @@ public virtual JsonSchema Schema /// Example of the media type. /// The example object SHOULD be in the correct format as specified by the media type. /// - public OpenApiAny Example { get; set; } + public OpenApiAny? Example { get; set; } /// /// Examples of the media type. /// Each example object SHOULD match the media type and specified schema if present. /// - public IDictionary Examples { get; set; } = new Dictionary(); + public IDictionary? Examples { get; set; } = new Dictionary(); /// /// A map between a property name and its encoding information. @@ -45,12 +47,12 @@ public virtual JsonSchema Schema /// The encoding object SHALL only apply to requestBody objects /// when the media type is multipart or application/x-www-form-urlencoded. /// - public IDictionary Encoding { get; set; } = new Dictionary(); + public IDictionary? Encoding { get; set; } = new Dictionary(); /// /// Serialize to Open Api v3.0. /// - public IDictionary Extensions { get; set; } = new Dictionary(); + public IDictionary? Extensions { get; set; } = new Dictionary(); /// /// Parameterless constructor @@ -60,7 +62,7 @@ public OpenApiMediaType() { } /// /// Initializes a copy of an object /// - public OpenApiMediaType(OpenApiMediaType mediaType) + public OpenApiMediaType(OpenApiMediaType? mediaType) { Schema = mediaType?.Schema != null ? JsonNodeCloneHelper.CloneJsonSchema(mediaType.Schema) : null; Example = mediaType?.Example != null ? JsonNodeCloneHelper.Clone(mediaType.Example) : null; From 38f35c2ea7304855a06632155e4781cfc9a6c368 Mon Sep 17 00:00:00 2001 From: HavenDV Date: Fri, 28 Jun 2024 00:45:15 +0400 Subject: [PATCH 4/9] feat: Added nullable enable to OpenApiOperation. --- .../Formatters/PowerShellFormatter.cs | 9 +++--- .../Models/OpenApiOperation.cs | 30 ++++++++++--------- .../Formatters/PowerShellFormatterTests.cs | 2 +- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.OpenApi.Hidi/Formatters/PowerShellFormatter.cs b/src/Microsoft.OpenApi.Hidi/Formatters/PowerShellFormatter.cs index d8b19f916..5daa0c9b5 100644 --- a/src/Microsoft.OpenApi.Hidi/Formatters/PowerShellFormatter.cs +++ b/src/Microsoft.OpenApi.Hidi/Formatters/PowerShellFormatter.cs @@ -54,7 +54,8 @@ public override void Visit(ref JsonSchema schema) public override void Visit(OpenApiPathItem pathItem) { - if (pathItem.Operations.TryGetValue(OperationType.Put, out var value)) + if (pathItem.Operations.TryGetValue(OperationType.Put, out var value) && + value.OperationId != null) { var operationId = value.OperationId; pathItem.Operations[OperationType.Put].OperationId = ResolvePutOperationId(operationId); @@ -69,14 +70,14 @@ public override void Visit(OpenApiOperation operation) throw new ArgumentException($"OperationId is required {PathString}", nameof(operation)); var operationId = operation.OperationId; - var operationTypeExtension = operation.Extensions.GetExtension("x-ms-docs-operation-type"); + var operationTypeExtension = operation.Extensions?.GetExtension("x-ms-docs-operation-type"); if (operationTypeExtension.IsEquals("function")) - operation.Parameters = ResolveFunctionParameters(operation.Parameters); + operation.Parameters = ResolveFunctionParameters(operation.Parameters ?? new List()); // Order matters. Resolve operationId. operationId = RemoveHashSuffix(operationId); if (operationTypeExtension.IsEquals("action") || operationTypeExtension.IsEquals("function")) - operationId = RemoveKeyTypeSegment(operationId, operation.Parameters); + operationId = RemoveKeyTypeSegment(operationId, operation.Parameters ?? new List()); operationId = SingularizeAndDeduplicateOperationId(operationId.SplitByChar('.')); operationId = ResolveODataCastOperationId(operationId); operationId = ResolveByRefOperationId(operationId); diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs index 498e93306..4da68d082 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs @@ -8,6 +8,8 @@ using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Writers; +#nullable enable + namespace Microsoft.OpenApi.Models { /// @@ -24,30 +26,30 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible /// A list of tags for API documentation control. /// Tags can be used for logical grouping of operations by resources or any other qualifier. /// - public IList Tags { get; set; } = new List(); + public IList? Tags { get; set; } = new List(); /// /// A short summary of what the operation does. /// - public string Summary { get; set; } + public string? Summary { get; set; } /// /// A verbose explanation of the operation behavior. /// CommonMark syntax MAY be used for rich text representation. /// - public string Description { get; set; } + public string? Description { get; set; } /// /// Additional external documentation for this operation. /// - public OpenApiExternalDocs ExternalDocs { get; set; } + public OpenApiExternalDocs? ExternalDocs { get; set; } /// /// Unique string used to identify the operation. The id MUST be unique among all operations described in the API. /// Tools and libraries MAY use the operationId to uniquely identify an operation, therefore, /// it is RECOMMENDED to follow common programming naming conventions. /// - public string OperationId { get; set; } + public string? OperationId { get; set; } /// /// A list of parameters that are applicable for this operation. @@ -55,7 +57,7 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible /// The list MUST NOT include duplicated parameters. A unique parameter is defined by a combination of a name and location. /// The list can use the Reference Object to link to parameters that are defined at the OpenAPI Object's components/parameters. /// - public IList Parameters { get; set; } = new List(); + public IList? Parameters { get; set; } = new List(); /// /// The request body applicable for this operation. @@ -63,12 +65,12 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible /// has explicitly defined semantics for request bodies. /// In other cases where the HTTP spec is vague, requestBody SHALL be ignored by consumers. /// - public OpenApiRequestBody RequestBody { get; set; } + public OpenApiRequestBody? RequestBody { get; set; } /// /// REQUIRED. The list of possible responses as they are returned from executing this operation. /// - public OpenApiResponses Responses { get; set; } = new(); + public OpenApiResponses? Responses { get; set; } = new(); /// /// A map of possible out-of band callbacks related to the parent operation. @@ -78,7 +80,7 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible /// The key value used to identify the callback object is an expression, evaluated at runtime, /// that identifies a URL to use for the callback operation. /// - public IDictionary Callbacks { get; set; } = new Dictionary(); + public IDictionary? Callbacks { get; set; } = new Dictionary(); /// /// Declares this operation to be deprecated. Consumers SHOULD refrain from usage of the declared operation. @@ -92,19 +94,19 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible /// This definition overrides any declared top-level security. /// To remove a top-level security declaration, an empty array can be used. /// - public IList Security { get; set; } = new List(); + public IList? Security { get; set; } = new List(); /// /// An alternative server array to service this operation. /// If an alternative server object is specified at the Path Item Object or Root level, /// it will be overridden by this value. /// - public IList Servers { get; set; } = new List(); + public IList? Servers { get; set; } = new List(); /// /// This object MAY be extended with Specification Extensions. /// - public IDictionary Extensions { get; set; } = new Dictionary(); + public IDictionary? Extensions { get; set; } = new Dictionary(); /// /// Parameterless constructor @@ -114,9 +116,9 @@ public OpenApiOperation() { } /// /// Initializes a copy of an object /// - public OpenApiOperation(OpenApiOperation operation) + public OpenApiOperation(OpenApiOperation? operation) { - Tags = operation?.Tags != null ? new List(operation?.Tags) : null; + Tags = operation?.Tags != null ? new List(operation.Tags) : null; Summary = operation?.Summary ?? Summary; Description = operation?.Description ?? Description; ExternalDocs = operation?.ExternalDocs != null ? new(operation?.ExternalDocs) : null; diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs index 4a662be67..81c1ca7a2 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs @@ -85,7 +85,7 @@ public void ResolveFunctionParameters() var walker = new OpenApiWalker(powerShellFormatter); walker.Walk(openApiDocument); - var idsParameter = openApiDocument.Paths?["/foo"].Operations[OperationType.Get].Parameters.Where(static p => p.Name == "ids").FirstOrDefault(); + var idsParameter = openApiDocument.Paths?["/foo"].Operations[OperationType.Get].Parameters?.Where(static p => p.Name == "ids").FirstOrDefault(); // Assert Assert.Null(idsParameter?.Content); From ead99b1f4e3c421ae2f1b0160edf92f3a00067d5 Mon Sep 17 00:00:00 2001 From: HavenDV Date: Fri, 28 Jun 2024 00:47:52 +0400 Subject: [PATCH 5/9] feat: Updated PublicApi.approved.txt. --- .../PublicApi/PublicApi.approved.txt | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 7e0730600..9c810732f 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -423,18 +423,18 @@ namespace Microsoft.OpenApi.Models public class OpenApiComponents : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { public OpenApiComponents() { } - public OpenApiComponents(Microsoft.OpenApi.Models.OpenApiComponents components) { } - public System.Collections.Generic.IDictionary Schemas { get; set; } - public virtual System.Collections.Generic.IDictionary Callbacks { get; set; } - public virtual System.Collections.Generic.IDictionary Examples { get; set; } - public virtual System.Collections.Generic.IDictionary Extensions { get; set; } - public virtual System.Collections.Generic.IDictionary Headers { get; set; } - public virtual System.Collections.Generic.IDictionary Links { get; set; } - public virtual System.Collections.Generic.IDictionary Parameters { get; set; } - public virtual System.Collections.Generic.IDictionary PathItems { get; set; } - public virtual System.Collections.Generic.IDictionary RequestBodies { get; set; } - public virtual System.Collections.Generic.IDictionary Responses { get; set; } - public virtual System.Collections.Generic.IDictionary SecuritySchemes { get; set; } + public OpenApiComponents(Microsoft.OpenApi.Models.OpenApiComponents? components) { } + public System.Collections.Generic.IDictionary? Schemas { get; set; } + public virtual System.Collections.Generic.IDictionary? Callbacks { get; set; } + public virtual System.Collections.Generic.IDictionary? Examples { get; set; } + public virtual System.Collections.Generic.IDictionary? Extensions { get; set; } + public virtual System.Collections.Generic.IDictionary? Headers { get; set; } + public virtual System.Collections.Generic.IDictionary? Links { get; set; } + public virtual System.Collections.Generic.IDictionary? Parameters { get; set; } + public virtual System.Collections.Generic.IDictionary? PathItems { get; set; } + public virtual System.Collections.Generic.IDictionary? RequestBodies { get; set; } + public virtual System.Collections.Generic.IDictionary? Responses { get; set; } + public virtual System.Collections.Generic.IDictionary? SecuritySchemes { get; set; } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } @@ -606,35 +606,35 @@ namespace Microsoft.OpenApi.Models public class OpenApiDocument : Json.Schema.IBaseDocument, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { public OpenApiDocument() { } - public OpenApiDocument(Microsoft.OpenApi.Models.OpenApiDocument document) { } + public OpenApiDocument(Microsoft.OpenApi.Models.OpenApiDocument? document) { } public System.Uri BaseUri { get; } - public Microsoft.OpenApi.Models.OpenApiComponents Components { get; set; } - public System.Collections.Generic.IDictionary Extensions { get; set; } - public Microsoft.OpenApi.Models.OpenApiExternalDocs ExternalDocs { get; set; } + public Microsoft.OpenApi.Models.OpenApiComponents? Components { get; set; } + public System.Collections.Generic.IDictionary? Extensions { get; set; } + public Microsoft.OpenApi.Models.OpenApiExternalDocs? ExternalDocs { get; set; } public string HashCode { get; } - public Microsoft.OpenApi.Models.OpenApiInfo Info { get; set; } - public string JsonSchemaDialect { get; set; } - public Microsoft.OpenApi.Models.OpenApiPaths Paths { get; set; } - public System.Collections.Generic.IList SecurityRequirements { get; set; } - public System.Collections.Generic.IList Servers { get; set; } - 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 Json.Schema.JsonSchema FindSubschema(Json.Pointer.JsonPointer pointer, Json.Schema.EvaluationOptions options) { } - public Json.Schema.JsonSchema ResolveJsonSchemaReference(System.Uri referenceUri) { } - public Microsoft.OpenApi.Interfaces.IOpenApiReferenceable ResolveReference(Microsoft.OpenApi.Models.OpenApiReference reference) { } + public Microsoft.OpenApi.Models.OpenApiInfo? Info { get; set; } + public string? JsonSchemaDialect { get; set; } + public Microsoft.OpenApi.Models.OpenApiPaths? Paths { get; set; } + public System.Collections.Generic.IList? SecurityRequirements { get; set; } + public System.Collections.Generic.IList? Servers { get; set; } + 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 Json.Schema.JsonSchema? FindSubschema(Json.Pointer.JsonPointer pointer, Json.Schema.EvaluationOptions options) { } + public Json.Schema.JsonSchema? ResolveJsonSchemaReference(System.Uri referenceUri) { } + public Microsoft.OpenApi.Interfaces.IOpenApiReferenceable? ResolveReference(Microsoft.OpenApi.Models.OpenApiReference reference) { } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SetReferenceHostDocument() { } public static string GenerateHashValue(Microsoft.OpenApi.Models.OpenApiDocument doc) { } - public static Microsoft.OpenApi.Reader.ReadResult Load(string url, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } - public static Microsoft.OpenApi.Reader.ReadResult Load(System.IO.Stream stream, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } - public static Microsoft.OpenApi.Reader.ReadResult Load(System.IO.TextReader input, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } - public static System.Threading.Tasks.Task LoadAsync(string url, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } - public static System.Threading.Tasks.Task LoadAsync(System.IO.TextReader input, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } - public static System.Threading.Tasks.Task LoadAsync(System.IO.Stream stream, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null, System.Threading.CancellationToken cancellationToken = default) { } - public static Microsoft.OpenApi.Reader.ReadResult Parse(string input, string format = null, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } + public static Microsoft.OpenApi.Reader.ReadResult Load(string url, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } + public static Microsoft.OpenApi.Reader.ReadResult Load(System.IO.Stream stream, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } + public static Microsoft.OpenApi.Reader.ReadResult Load(System.IO.TextReader input, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } + public static System.Threading.Tasks.Task LoadAsync(string url, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } + public static System.Threading.Tasks.Task LoadAsync(System.IO.TextReader input, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } + public static System.Threading.Tasks.Task LoadAsync(System.IO.Stream stream, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null, System.Threading.CancellationToken cancellationToken = default) { } + public static Microsoft.OpenApi.Reader.ReadResult Parse(string input, string? format = null, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } } public class OpenApiEncoding : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { @@ -775,12 +775,12 @@ namespace Microsoft.OpenApi.Models public class OpenApiMediaType : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { public OpenApiMediaType() { } - public OpenApiMediaType(Microsoft.OpenApi.Models.OpenApiMediaType mediaType) { } - public System.Collections.Generic.IDictionary Encoding { get; set; } - public Microsoft.OpenApi.Any.OpenApiAny Example { get; set; } - public System.Collections.Generic.IDictionary Examples { get; set; } - public System.Collections.Generic.IDictionary Extensions { get; set; } - public virtual Json.Schema.JsonSchema Schema { get; set; } + public OpenApiMediaType(Microsoft.OpenApi.Models.OpenApiMediaType? mediaType) { } + public System.Collections.Generic.IDictionary? Encoding { get; set; } + public Microsoft.OpenApi.Any.OpenApiAny? Example { get; set; } + public System.Collections.Generic.IDictionary? Examples { get; set; } + public System.Collections.Generic.IDictionary? Extensions { get; set; } + public virtual Json.Schema.JsonSchema? Schema { get; set; } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } @@ -815,20 +815,20 @@ namespace Microsoft.OpenApi.Models { public const bool DeprecatedDefault = false; public OpenApiOperation() { } - public OpenApiOperation(Microsoft.OpenApi.Models.OpenApiOperation operation) { } - public System.Collections.Generic.IDictionary Callbacks { get; set; } + public OpenApiOperation(Microsoft.OpenApi.Models.OpenApiOperation? operation) { } + public System.Collections.Generic.IDictionary? Callbacks { get; set; } public bool Deprecated { get; set; } - public string Description { get; set; } - public System.Collections.Generic.IDictionary Extensions { get; set; } - public Microsoft.OpenApi.Models.OpenApiExternalDocs ExternalDocs { get; set; } - public string OperationId { get; set; } - public System.Collections.Generic.IList Parameters { get; set; } - public Microsoft.OpenApi.Models.OpenApiRequestBody RequestBody { get; set; } - public Microsoft.OpenApi.Models.OpenApiResponses Responses { get; set; } - public System.Collections.Generic.IList Security { get; set; } - public System.Collections.Generic.IList Servers { get; set; } - public string Summary { get; set; } - public System.Collections.Generic.IList Tags { get; set; } + public string? Description { get; set; } + public System.Collections.Generic.IDictionary? Extensions { get; set; } + public Microsoft.OpenApi.Models.OpenApiExternalDocs? ExternalDocs { get; set; } + public string? OperationId { get; set; } + public System.Collections.Generic.IList? Parameters { get; set; } + public Microsoft.OpenApi.Models.OpenApiRequestBody? RequestBody { get; set; } + public Microsoft.OpenApi.Models.OpenApiResponses? Responses { get; set; } + public System.Collections.Generic.IList? Security { get; set; } + public System.Collections.Generic.IList? Servers { get; set; } + public string? Summary { get; set; } + public System.Collections.Generic.IList? Tags { get; set; } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } @@ -1495,7 +1495,7 @@ namespace Microsoft.OpenApi.Services public bool Contains(string location) { } public System.Uri GetDocumentId(string key) { } public bool RegisterComponent(string location, T component) { } - public T ResolveReference(string location) { } + public T? ResolveReference(string location) { } } public class OperationSearch : Microsoft.OpenApi.Services.OpenApiVisitorBase { @@ -1827,7 +1827,7 @@ namespace Microsoft.OpenApi.Writers where T : Microsoft.OpenApi.Interfaces.IOpenApiElement { } public static void WriteOptionalMap(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, System.Collections.Generic.IDictionary elements, System.Action action) where T : Microsoft.OpenApi.Interfaces.IOpenApiElement { } - public static void WriteOptionalObject(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, T value, System.Action action) { } + public static void WriteOptionalObject(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, T? value, System.Action action) { } public static void WriteProperty(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, string value) { } public static void WriteProperty(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, bool value, bool defaultValue = false) { } public static void WriteProperty(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, bool? value, bool defaultValue = false) { } @@ -1840,7 +1840,7 @@ namespace Microsoft.OpenApi.Writers public static void WriteRequiredMap(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, System.Collections.Generic.IDictionary elements, System.Action action) { } public static void WriteRequiredMap(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, System.Collections.Generic.IDictionary elements, System.Action action) where T : Microsoft.OpenApi.Interfaces.IOpenApiElement { } - public static void WriteRequiredObject(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, T value, System.Action action) { } + public static void WriteRequiredObject(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, T? value, System.Action action) { } public static void WriteRequiredProperty(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, string value) { } } public class OpenApiWriterSettings From e9430686aeaaee8c4bb77455a401ba981330091b Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 30 Jul 2024 10:36:49 -0400 Subject: [PATCH 6/9] Update src/Microsoft.OpenApi/Models/OpenApiDocument.cs --- src/Microsoft.OpenApi/Models/OpenApiDocument.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 58cf153b4..19727885b 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -118,7 +118,7 @@ public OpenApiDocument(OpenApiDocument? document) Tags = document?.Tags != null ? new List(document.Tags) : null; ExternalDocs = document?.ExternalDocs != null ? new(document?.ExternalDocs) : null; Extensions = document?.Extensions != null ? new Dictionary(document.Extensions) : null; - BaseUri = document?.BaseUri != null ? document.BaseUri : new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid().ToString()); + BaseUri = document?.BaseUri != null ? document.BaseUri : new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid()); } /// From 5727db514d3754112ec4173bafb9824f56b3dd82 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 30 Jul 2024 10:37:07 -0400 Subject: [PATCH 7/9] Update src/Microsoft.OpenApi/Models/OpenApiDocument.cs --- src/Microsoft.OpenApi/Models/OpenApiDocument.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 19727885b..9afda29f4 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -99,7 +99,7 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IBaseDo public OpenApiDocument() { Workspace = new OpenApiWorkspace(); - BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid().ToString()); + BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid()); } /// From 7e0f1e0245ccdf81b207b98529b02c9bad12e3f1 Mon Sep 17 00:00:00 2001 From: HavenDV Date: Fri, 6 Sep 2024 02:34:08 +0400 Subject: [PATCH 8/9] fix: Resolved conflicts. --- .../Models/OpenApiDocument.cs | 8 +- .../Formatters/PowerShellFormatterTests.cs | 27 +++-- .../PublicApi/PublicApi.approved.txt | 106 +++++++++--------- 3 files changed, 72 insertions(+), 69 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 3dbe09e0b..b05fbbbd3 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -529,11 +529,11 @@ private static string ConvertByteArrayToString(byte[] hash) string relativePath = OpenApiConstants.ComponentsSegment + reference.Type.GetDisplayName() + "/" + reference.Id; uriLocation = useExternal - ? Workspace.GetDocumentId(reference.ExternalResource)?.OriginalString + relativePath + ? Workspace?.GetDocumentId(reference.ExternalResource)?.OriginalString + relativePath : BaseUri + relativePath; } - return Workspace.ResolveReference(uriLocation); + return Workspace?.ResolveReference(uriLocation); } /// @@ -628,9 +628,9 @@ public static ReadResult Parse(string input, internal class FindSchemaReferences : OpenApiVisitorBase { - private Dictionary Schemas; + private Dictionary Schemas = new(); - public static void ResolveSchemas(OpenApiComponents components, Dictionary schemas) + public static void ResolveSchemas(OpenApiComponents? components, Dictionary schemas) { var visitor = new FindSchemaReferences(); visitor.Schemas = schemas; diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs index 94f99a1d2..f047ecdc7 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs @@ -57,18 +57,21 @@ public void RemoveAnyOfAndOneOfFromSchema() var walker = new OpenApiWalker(powerShellFormatter); walker.Walk(openApiDocument); - var testSchema = openApiDocument.Components.Schemas["TestSchema"]; - var averageAudioDegradationProperty = testSchema.Properties["averageAudioDegradation"]; - var defaultPriceProperty = testSchema.Properties["defaultPrice"]; + var testSchema = openApiDocument.Components?.Schemas?["TestSchema"]; + var averageAudioDegradationProperty = testSchema?.Properties["averageAudioDegradation"]; + var defaultPriceProperty = testSchema?.Properties["defaultPrice"]; // Assert - Assert.Null(averageAudioDegradationProperty.AnyOf); - Assert.Equal("number", averageAudioDegradationProperty.Type); - Assert.Equal("float", averageAudioDegradationProperty.Format); - Assert.True(averageAudioDegradationProperty.Nullable); - Assert.Null(defaultPriceProperty.OneOf); - Assert.Equal("number", defaultPriceProperty.Type); - Assert.Equal("double", defaultPriceProperty.Format); + Assert.NotNull(openApiDocument.Components); + Assert.NotNull(openApiDocument.Components.Schemas); + Assert.NotNull(testSchema); + Assert.Null(averageAudioDegradationProperty?.AnyOf); + Assert.Equal("number", averageAudioDegradationProperty?.Type); + Assert.Equal("float", averageAudioDegradationProperty?.Format); + Assert.True(averageAudioDegradationProperty?.Nullable); + Assert.Null(defaultPriceProperty?.OneOf); + Assert.Equal("number", defaultPriceProperty?.Type); + Assert.Equal("double", defaultPriceProperty?.Format); Assert.NotNull(testSchema.AdditionalProperties); } @@ -83,12 +86,12 @@ public void ResolveFunctionParameters() var walker = new OpenApiWalker(powerShellFormatter); walker.Walk(openApiDocument); - var idsParameter = openApiDocument.Paths["/foo"].Operations[OperationType.Get].Parameters.Where(static p => p.Name == "ids").FirstOrDefault(); + var idsParameter = openApiDocument.Paths?["/foo"].Operations[OperationType.Get].Parameters?.Where(static p => p.Name == "ids").FirstOrDefault(); // Assert Assert.Null(idsParameter?.Content); Assert.NotNull(idsParameter?.Schema); - Assert.Equal("array", idsParameter?.Schema.Type); + Assert.Equal("array", idsParameter.Schema.Type); } private static OpenApiDocument GetSampleOpenApiDocument() diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 7eb01a70c..a71bde20a 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -336,18 +336,18 @@ namespace Microsoft.OpenApi.Models public class OpenApiComponents : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { public OpenApiComponents() { } - public OpenApiComponents(Microsoft.OpenApi.Models.OpenApiComponents components) { } - public System.Collections.Generic.IDictionary Schemas { get; set; } - public virtual System.Collections.Generic.IDictionary Callbacks { get; set; } - public virtual System.Collections.Generic.IDictionary Examples { get; set; } - public virtual System.Collections.Generic.IDictionary Extensions { get; set; } - public virtual System.Collections.Generic.IDictionary Headers { get; set; } - public virtual System.Collections.Generic.IDictionary Links { get; set; } - public virtual System.Collections.Generic.IDictionary Parameters { get; set; } - public virtual System.Collections.Generic.IDictionary PathItems { get; set; } - public virtual System.Collections.Generic.IDictionary RequestBodies { get; set; } - public virtual System.Collections.Generic.IDictionary Responses { get; set; } - public virtual System.Collections.Generic.IDictionary SecuritySchemes { get; set; } + public OpenApiComponents(Microsoft.OpenApi.Models.OpenApiComponents? components) { } + public System.Collections.Generic.IDictionary? Schemas { get; set; } + public virtual System.Collections.Generic.IDictionary? Callbacks { get; set; } + public virtual System.Collections.Generic.IDictionary? Examples { get; set; } + public virtual System.Collections.Generic.IDictionary? Extensions { get; set; } + public virtual System.Collections.Generic.IDictionary? Headers { get; set; } + public virtual System.Collections.Generic.IDictionary? Links { get; set; } + public virtual System.Collections.Generic.IDictionary? Parameters { get; set; } + public virtual System.Collections.Generic.IDictionary? PathItems { get; set; } + public virtual System.Collections.Generic.IDictionary? RequestBodies { get; set; } + public virtual System.Collections.Generic.IDictionary? Responses { get; set; } + public virtual System.Collections.Generic.IDictionary? SecuritySchemes { get; set; } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } @@ -531,32 +531,32 @@ namespace Microsoft.OpenApi.Models public class OpenApiDocument : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { public OpenApiDocument() { } - public OpenApiDocument(Microsoft.OpenApi.Models.OpenApiDocument document) { } + public OpenApiDocument(Microsoft.OpenApi.Models.OpenApiDocument? document) { } public System.Uri BaseUri { get; } - public Microsoft.OpenApi.Models.OpenApiComponents Components { get; set; } - public System.Collections.Generic.IDictionary Extensions { get; set; } - public Microsoft.OpenApi.Models.OpenApiExternalDocs ExternalDocs { get; set; } + public Microsoft.OpenApi.Models.OpenApiComponents? Components { get; set; } + public System.Collections.Generic.IDictionary? Extensions { get; set; } + public Microsoft.OpenApi.Models.OpenApiExternalDocs? ExternalDocs { get; set; } public string HashCode { get; } - public Microsoft.OpenApi.Models.OpenApiInfo Info { get; set; } - public string JsonSchemaDialect { get; set; } - public Microsoft.OpenApi.Models.OpenApiPaths Paths { get; set; } - public System.Collections.Generic.IList SecurityRequirements { get; set; } - public System.Collections.Generic.IList Servers { get; set; } - 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 Microsoft.OpenApi.Models.OpenApiInfo? Info { get; set; } + public string? JsonSchemaDialect { get; set; } + public Microsoft.OpenApi.Models.OpenApiPaths? Paths { get; set; } + public System.Collections.Generic.IList? SecurityRequirements { get; set; } + public System.Collections.Generic.IList? Servers { get; set; } + 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 void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SetReferenceHostDocument() { } public static string GenerateHashValue(Microsoft.OpenApi.Models.OpenApiDocument doc) { } - public static Microsoft.OpenApi.Reader.ReadResult Load(string url, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } - public static Microsoft.OpenApi.Reader.ReadResult Load(System.IO.Stream stream, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } - public static Microsoft.OpenApi.Reader.ReadResult Load(System.IO.TextReader input, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } - public static System.Threading.Tasks.Task LoadAsync(string url, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } - public static System.Threading.Tasks.Task LoadAsync(System.IO.TextReader input, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } - public static System.Threading.Tasks.Task LoadAsync(System.IO.Stream stream, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null, System.Threading.CancellationToken cancellationToken = default) { } - public static Microsoft.OpenApi.Reader.ReadResult Parse(string input, string format = null, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } + public static Microsoft.OpenApi.Reader.ReadResult Load(string url, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } + public static Microsoft.OpenApi.Reader.ReadResult Load(System.IO.Stream stream, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } + public static Microsoft.OpenApi.Reader.ReadResult Load(System.IO.TextReader input, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } + public static System.Threading.Tasks.Task LoadAsync(string url, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } + public static System.Threading.Tasks.Task LoadAsync(System.IO.TextReader input, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } + public static System.Threading.Tasks.Task LoadAsync(System.IO.Stream stream, string format, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null, System.Threading.CancellationToken cancellationToken = default) { } + public static Microsoft.OpenApi.Reader.ReadResult Parse(string input, string? format = null, Microsoft.OpenApi.Reader.OpenApiReaderSettings? settings = null) { } } public class OpenApiEncoding : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { @@ -688,12 +688,12 @@ namespace Microsoft.OpenApi.Models public class OpenApiMediaType : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { public OpenApiMediaType() { } - public OpenApiMediaType(Microsoft.OpenApi.Models.OpenApiMediaType mediaType) { } - public System.Collections.Generic.IDictionary Encoding { get; set; } - public System.Text.Json.Nodes.JsonNode Example { get; set; } - public System.Collections.Generic.IDictionary Examples { get; set; } - public System.Collections.Generic.IDictionary Extensions { get; set; } - public virtual Microsoft.OpenApi.Models.OpenApiSchema Schema { get; set; } + public OpenApiMediaType(Microsoft.OpenApi.Models.OpenApiMediaType? mediaType) { } + public System.Collections.Generic.IDictionary? Encoding { get; set; } + public System.Text.Json.Nodes.JsonNode? Example { get; set; } + public System.Collections.Generic.IDictionary? Examples { get; set; } + public System.Collections.Generic.IDictionary? Extensions { get; set; } + public virtual Microsoft.OpenApi.Models.OpenApiSchema? Schema { get; set; } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } @@ -728,20 +728,20 @@ namespace Microsoft.OpenApi.Models { public const bool DeprecatedDefault = false; public OpenApiOperation() { } - public OpenApiOperation(Microsoft.OpenApi.Models.OpenApiOperation operation) { } - public System.Collections.Generic.IDictionary Callbacks { get; set; } + public OpenApiOperation(Microsoft.OpenApi.Models.OpenApiOperation? operation) { } + public System.Collections.Generic.IDictionary? Callbacks { get; set; } public bool Deprecated { get; set; } - public string Description { get; set; } - public System.Collections.Generic.IDictionary Extensions { get; set; } - public Microsoft.OpenApi.Models.OpenApiExternalDocs ExternalDocs { get; set; } - public string OperationId { get; set; } - public System.Collections.Generic.IList Parameters { get; set; } - public Microsoft.OpenApi.Models.OpenApiRequestBody RequestBody { get; set; } - public Microsoft.OpenApi.Models.OpenApiResponses Responses { get; set; } - public System.Collections.Generic.IList Security { get; set; } - public System.Collections.Generic.IList Servers { get; set; } - public string Summary { get; set; } - public System.Collections.Generic.IList Tags { get; set; } + public string? Description { get; set; } + public System.Collections.Generic.IDictionary? Extensions { get; set; } + public Microsoft.OpenApi.Models.OpenApiExternalDocs? ExternalDocs { get; set; } + public string? OperationId { get; set; } + public System.Collections.Generic.IList? Parameters { get; set; } + public Microsoft.OpenApi.Models.OpenApiRequestBody? RequestBody { get; set; } + public Microsoft.OpenApi.Models.OpenApiResponses? Responses { get; set; } + public System.Collections.Generic.IList? Security { get; set; } + public System.Collections.Generic.IList? Servers { get; set; } + public string? Summary { get; set; } + public System.Collections.Generic.IList? Tags { get; set; } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } @@ -1512,7 +1512,7 @@ namespace Microsoft.OpenApi.Services public bool Contains(string location) { } public System.Uri GetDocumentId(string key) { } public bool RegisterComponent(string location, T component) { } - public T ResolveReference(string location) { } + public T? ResolveReference(string location) { } } public class OperationSearch : Microsoft.OpenApi.Services.OpenApiVisitorBase { @@ -1837,7 +1837,7 @@ namespace Microsoft.OpenApi.Writers where T : Microsoft.OpenApi.Interfaces.IOpenApiElement { } public static void WriteOptionalMap(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, System.Collections.Generic.IDictionary elements, System.Action action) where T : Microsoft.OpenApi.Interfaces.IOpenApiElement { } - public static void WriteOptionalObject(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, T value, System.Action action) { } + public static void WriteOptionalObject(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, T? value, System.Action action) { } public static void WriteProperty(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, string value) { } public static void WriteProperty(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, bool value, bool defaultValue = false) { } public static void WriteProperty(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, bool? value, bool defaultValue = false) { } @@ -1850,7 +1850,7 @@ namespace Microsoft.OpenApi.Writers public static void WriteRequiredMap(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, System.Collections.Generic.IDictionary elements, System.Action action) { } public static void WriteRequiredMap(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, System.Collections.Generic.IDictionary elements, System.Action action) where T : Microsoft.OpenApi.Interfaces.IOpenApiElement { } - public static void WriteRequiredObject(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, T value, System.Action action) { } + public static void WriteRequiredObject(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, T? value, System.Action action) { } public static void WriteRequiredProperty(this Microsoft.OpenApi.Writers.IOpenApiWriter writer, string name, string value) { } } public class OpenApiWriterSettings From 45b0e5ea73885a74f07d725bf49ab94c2fc5f7e9 Mon Sep 17 00:00:00 2001 From: HavenDV Date: Fri, 6 Sep 2024 02:52:24 +0400 Subject: [PATCH 9/9] feat: Make REQUIRED properties as non-nullable and revert some changes according this. --- src/Microsoft.OpenApi.Hidi/OpenApiService.cs | 12 ++++++------ src/Microsoft.OpenApi/Models/OpenApiDocument.cs | 10 ++++++---- .../Formatters/PowerShellFormatterTests.cs | 4 ++-- .../Services/OpenApiFilterServiceTests.cs | 6 +----- .../PublicApi/PublicApi.approved.txt | 4 ++-- .../Walkers/WalkerLocationTests.cs | 12 ++++++------ 6 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs index c0ff17aa7..df3bf0e67 100644 --- a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs @@ -185,7 +185,7 @@ private static OpenApiDocument ApplyFilters(HidiOptions options, ILogger logger, stopwatch.Start(); document = OpenApiFilterService.CreateFilteredDocument(document, predicate); stopwatch.Stop(); - logger.LogTrace("{Timestamp}ms: Creating filtered OpenApi document with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths?.Count); + logger.LogTrace("{Timestamp}ms: Creating filtered OpenApi document with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths.Count); } return document; @@ -248,7 +248,7 @@ private static async Task GetOpenApi(HidiOptions options, strin document = await ConvertCsdlToOpenApi(filteredStream ?? stream, format, metadataVersion, options.SettingsConfig, cancellationToken).ConfigureAwait(false); stopwatch.Stop(); - logger.LogTrace("{Timestamp}ms: Generated OpenAPI with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths?.Count); + logger.LogTrace("{Timestamp}ms: Generated OpenAPI with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths.Count); } } else if (!string.IsNullOrEmpty(options.OpenApi)) @@ -666,7 +666,7 @@ internal static void WriteTreeDocumentAsMarkdown(string openapiUrl, OpenApiDocum { var rootNode = OpenApiUrlTreeNode.Create(document, "main"); - writer.WriteLine("# " + document.Info?.Title); + writer.WriteLine("# " + document.Info.Title); writer.WriteLine(); writer.WriteLine("API Description: " + openapiUrl); @@ -702,7 +702,7 @@ internal static void WriteTreeDocumentAsHtml(string sourceUrl, OpenApiDocument d """); - writer.WriteLine("

" + document.Info?.Title + "

"); + writer.WriteLine("

" + document.Info.Title + "

"); writer.WriteLine(); writer.WriteLine($"

API Description: {sourceUrl}

"); @@ -773,8 +773,8 @@ internal static async Task PluginManifest(HidiOptions options, ILogger logger, C // Create OpenAIPluginManifest from ApiDependency and OpenAPI document var manifest = new OpenAIPluginManifest { - NameForHuman = document.Info?.Title, - DescriptionForHuman = document.Info?.Description, + NameForHuman = document.Info.Title, + DescriptionForHuman = document.Info.Description, Api = new() { Type = "openapi", diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index b05fbbbd3..8b80fe958 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -33,7 +33,7 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible /// /// REQUIRED. Provides metadata about the API. The metadata MAY be used by tooling as required. /// - public OpenApiInfo? Info { get; set; } + public OpenApiInfo Info { get; set; } /// /// The default value for the $schema keyword within Schema Objects contained within this OAS document. This MUST be in the form of a URI. @@ -48,7 +48,7 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible /// /// REQUIRED. The available paths and operations for the API. /// - public OpenApiPaths? Paths { get; set; } + public OpenApiPaths Paths { get; set; } /// /// The incoming webhooks that MAY be received as part of this API and that the API consumer MAY choose to implement. @@ -100,6 +100,8 @@ public OpenApiDocument() { Workspace = new OpenApiWorkspace(); BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid()); + Info = new OpenApiInfo(); + Paths = new OpenApiPaths(); } /// @@ -108,10 +110,10 @@ public OpenApiDocument() public OpenApiDocument(OpenApiDocument? document) { Workspace = document?.Workspace != null ? new(document?.Workspace) : null; - Info = document?.Info != null ? new(document?.Info) : null; + Info = document?.Info != null ? new(document?.Info) : new OpenApiInfo(); JsonSchemaDialect = document?.JsonSchemaDialect ?? JsonSchemaDialect; Servers = document?.Servers != null ? new List(document.Servers) : null; - Paths = document?.Paths != null ? new(document?.Paths) : null; + Paths = document?.Paths != null ? new(document?.Paths) : new OpenApiPaths(); Webhooks = document?.Webhooks != null ? new Dictionary(document.Webhooks) : null; Components = document?.Components != null ? new(document?.Components) : null; SecurityRequirements = document?.SecurityRequirements != null ? new List(document.SecurityRequirements) : null; diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs index f047ecdc7..214bd47ff 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Formatters/PowerShellFormatterTests.cs @@ -86,12 +86,12 @@ public void ResolveFunctionParameters() var walker = new OpenApiWalker(powerShellFormatter); walker.Walk(openApiDocument); - var idsParameter = openApiDocument.Paths?["/foo"].Operations[OperationType.Get].Parameters?.Where(static p => p.Name == "ids").FirstOrDefault(); + var idsParameter = openApiDocument.Paths["/foo"].Operations[OperationType.Get].Parameters?.Where(static p => p.Name == "ids").FirstOrDefault(); // Assert Assert.Null(idsParameter?.Content); Assert.NotNull(idsParameter?.Schema); - Assert.Equal("array", idsParameter.Schema.Type); + Assert.Equal("array", idsParameter?.Schema.Type); } private static OpenApiDocument GetSampleOpenApiDocument() diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs index 02e6cedb0..5fb1b15f9 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs @@ -43,7 +43,6 @@ public void ReturnFilteredOpenApiDocumentBasedOnOperationIdsAndTags(string? oper // Assert Assert.NotNull(subsetOpenApiDocument); - Assert.NotNull(subsetOpenApiDocument.Paths); Assert.NotEmpty(subsetOpenApiDocument.Paths); Assert.Equal(expectedPathCount, subsetOpenApiDocument.Paths.Count); } @@ -63,7 +62,6 @@ public void ReturnFilteredOpenApiDocumentBasedOnPostmanCollection() // Assert Assert.NotNull(subsetOpenApiDocument); - Assert.NotNull(subsetOpenApiDocument.Paths); Assert.NotEmpty(subsetOpenApiDocument.Paths); Assert.Equal(3, subsetOpenApiDocument.Paths.Count); } @@ -152,11 +150,10 @@ public void ContinueProcessingWhenUrlsInCollectionAreMissingFromSourceDocument() var pathCount = requestUrls.Count; var predicate = OpenApiFilterService.CreatePredicate(requestUrls: requestUrls, source: _openApiDocumentMock); var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(_openApiDocumentMock, predicate); - var subsetPathCount = subsetOpenApiDocument.Paths?.Count; + var subsetPathCount = subsetOpenApiDocument.Paths.Count; // Assert Assert.NotNull(subsetOpenApiDocument); - Assert.NotNull(subsetOpenApiDocument.Paths); Assert.NotEmpty(subsetOpenApiDocument.Paths); Assert.Equal(2, subsetPathCount); Assert.NotEqual(pathCount, subsetPathCount); @@ -183,7 +180,6 @@ public void ReturnsPathParametersOnSlicingBasedOnOperationIdsOrTags(string? oper var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(_openApiDocumentMock, predicate); // Assert - Assert.NotNull(subsetOpenApiDocument.Paths); foreach (var pathItem in subsetOpenApiDocument.Paths) { Assert.True(pathItem.Value.Parameters.Any()); diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index a71bde20a..33c61f484 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -537,9 +537,9 @@ namespace Microsoft.OpenApi.Models public System.Collections.Generic.IDictionary? Extensions { get; set; } public Microsoft.OpenApi.Models.OpenApiExternalDocs? ExternalDocs { get; set; } public string HashCode { get; } - public Microsoft.OpenApi.Models.OpenApiInfo? Info { get; set; } + public Microsoft.OpenApi.Models.OpenApiInfo Info { get; set; } public string? JsonSchemaDialect { get; set; } - public Microsoft.OpenApi.Models.OpenApiPaths? Paths { get; set; } + public Microsoft.OpenApi.Models.OpenApiPaths Paths { get; set; } public System.Collections.Generic.IList? SecurityRequirements { get; set; } public System.Collections.Generic.IList? Servers { get; set; } public System.Collections.Generic.IList? Tags { get; set; } diff --git a/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs b/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs index 924364ccd..698d3fc5c 100644 --- a/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs @@ -24,7 +24,9 @@ public void LocateTopLevelObjects() walker.Walk(doc); locator.Locations.Should().BeEquivalentTo(new List { + "#/info", "#/servers", + "#/paths", "#/tags" }); } @@ -39,7 +41,6 @@ public void LocateTopLevelArrayItems() new(), new() }, - Paths = new(), Tags = new List { new() @@ -51,6 +52,7 @@ public void LocateTopLevelArrayItems() walker.Walk(doc); locator.Locations.Should().BeEquivalentTo(new List { + "#/info", "#/servers", "#/servers/0", "#/servers/1", @@ -63,10 +65,7 @@ public void LocateTopLevelArrayItems() [Fact] public void LocatePathOperationContentSchema() { - var doc = new OpenApiDocument - { - Paths = new() - }; + var doc = new OpenApiDocument(); doc.Paths.Add("/test", new() { Operations = new Dictionary @@ -98,6 +97,7 @@ public void LocatePathOperationContentSchema() walker.Walk(doc); locator.Locations.Should().BeEquivalentTo(new List { + "#/info", "#/servers", "#/paths", "#/paths/~1test", @@ -131,7 +131,6 @@ public void WalkDOMWithCycles() var doc = new OpenApiDocument { - Paths = new(), Components = new() { Schemas = new Dictionary @@ -146,6 +145,7 @@ public void WalkDOMWithCycles() walker.Walk(doc); locator.Locations.Should().BeEquivalentTo(new List { + "#/info", "#/servers", "#/paths", "#/components",