Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added Nullability markup for some classes #1710

Merged
merged 11 commits into from
Oct 8, 2024
9 changes: 5 additions & 4 deletions src/Microsoft.OpenApi.Hidi/Formatters/PowerShellFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ public override void Visit(OpenApiSchema 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);
Expand All @@ -67,14 +68,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<OpenApiParameter>());

// 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<OpenApiParameter>());
operationId = SingularizeAndDeduplicateOperationId(operationId.SplitByChar('.'));
operationId = ResolveODataCastOperationId(operationId);
operationId = ResolveByRefOperationId(operationId);
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi.Hidi/OpenApiService.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
25 changes: 13 additions & 12 deletions src/Microsoft.OpenApi/Models/OpenApiComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Writers;

#nullable enable

namespace Microsoft.OpenApi.Models
{
Expand All @@ -19,60 +20,60 @@ public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible
/// <summary>
/// An object to hold reusable <see cref="OpenApiSchema"/> Objects.
/// </summary>
public IDictionary<string, OpenApiSchema> Schemas { get; set; } = new Dictionary<string, OpenApiSchema>();
public IDictionary<string, OpenApiSchema>? Schemas { get; set; } = new Dictionary<string, OpenApiSchema>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiResponse"/> Objects.
/// </summary>
public virtual IDictionary<string, OpenApiResponse> Responses { get; set; } = new Dictionary<string, OpenApiResponse>();
public virtual IDictionary<string, OpenApiResponse>? Responses { get; set; } = new Dictionary<string, OpenApiResponse>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiParameter"/> Objects.
/// </summary>
public virtual IDictionary<string, OpenApiParameter> Parameters { get; set; } =
public virtual IDictionary<string, OpenApiParameter>? Parameters { get; set; } =
new Dictionary<string, OpenApiParameter>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiExample"/> Objects.
/// </summary>
public virtual IDictionary<string, OpenApiExample> Examples { get; set; } = new Dictionary<string, OpenApiExample>();
public virtual IDictionary<string, OpenApiExample>? Examples { get; set; } = new Dictionary<string, OpenApiExample>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiRequestBody"/> Objects.
/// </summary>
public virtual IDictionary<string, OpenApiRequestBody> RequestBodies { get; set; } =
public virtual IDictionary<string, OpenApiRequestBody>? RequestBodies { get; set; } =
new Dictionary<string, OpenApiRequestBody>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiHeader"/> Objects.
/// </summary>
public virtual IDictionary<string, OpenApiHeader> Headers { get; set; } = new Dictionary<string, OpenApiHeader>();
public virtual IDictionary<string, OpenApiHeader>? Headers { get; set; } = new Dictionary<string, OpenApiHeader>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiSecurityScheme"/> Objects.
/// </summary>
public virtual IDictionary<string, OpenApiSecurityScheme> SecuritySchemes { get; set; } =
public virtual IDictionary<string, OpenApiSecurityScheme>? SecuritySchemes { get; set; } =
new Dictionary<string, OpenApiSecurityScheme>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiLink"/> Objects.
/// </summary>
public virtual IDictionary<string, OpenApiLink> Links { get; set; } = new Dictionary<string, OpenApiLink>();
public virtual IDictionary<string, OpenApiLink>? Links { get; set; } = new Dictionary<string, OpenApiLink>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiCallback"/> Objects.
/// </summary>
public virtual IDictionary<string, OpenApiCallback> Callbacks { get; set; } = new Dictionary<string, OpenApiCallback>();
public virtual IDictionary<string, OpenApiCallback>? Callbacks { get; set; } = new Dictionary<string, OpenApiCallback>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiPathItem"/> Object.
/// </summary>
public virtual IDictionary<string, OpenApiPathItem> PathItems { get; set; } = new Dictionary<string, OpenApiPathItem>();
public virtual IDictionary<string, OpenApiPathItem>? PathItems { get; set; } = new Dictionary<string, OpenApiPathItem>();

/// <summary>
/// This object MAY be extended with Specification Extensions.
/// </summary>
public virtual IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
public virtual IDictionary<string, IOpenApiExtension>? Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();

/// <summary>
/// Parameter-less constructor
Expand All @@ -82,7 +83,7 @@ public OpenApiComponents() { }
/// <summary>
/// Initializes a copy of an <see cref="OpenApiComponents"/> object
/// </summary>
public OpenApiComponents(OpenApiComponents components)
public OpenApiComponents(OpenApiComponents? components)
{
Schemas = components?.Schemas != null ? new Dictionary<string, OpenApiSchema>(components.Schemas) : null;
Responses = components?.Responses != null ? new Dictionary<string, OpenApiResponse>(components.Responses) : null;
Expand Down
63 changes: 34 additions & 29 deletions src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Writers;

#nullable enable

namespace Microsoft.OpenApi.Models
{
/// <summary>
Expand All @@ -26,7 +28,7 @@
/// <summary>
/// Related workspace containing OpenApiDocuments that are referenced in this document
/// </summary>
public OpenApiWorkspace Workspace { get; set; }
public OpenApiWorkspace? Workspace { get; set; }

/// <summary>
/// REQUIRED. Provides metadata about the API. The metadata MAY be used by tooling as required.
Expand All @@ -36,12 +38,12 @@
/// <summary>
/// The default value for the $schema keyword within Schema Objects contained within this OAS document. This MUST be in the form of a URI.
/// </summary>
public string JsonSchemaDialect { get; set; }
public string? JsonSchemaDialect { get; set; }

/// <summary>
/// An array of Server Objects, which provide connectivity information to a target server.
/// </summary>
public IList<OpenApiServer> Servers { get; set; } = new List<OpenApiServer>();
public IList<OpenApiServer>? Servers { get; set; } = new List<OpenApiServer>();

/// <summary>
/// REQUIRED. The available paths and operations for the API.
Expand All @@ -53,33 +55,33 @@
/// 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
/// </summary>
public IDictionary<string, OpenApiPathItem> Webhooks { get; set; } = new Dictionary<string, OpenApiPathItem>();
public IDictionary<string, OpenApiPathItem>? Webhooks { get; set; } = new Dictionary<string, OpenApiPathItem>();

/// <summary>
/// An element to hold various schemas for the specification.
/// </summary>
public OpenApiComponents Components { get; set; }
public OpenApiComponents? Components { get; set; }

/// <summary>
/// A declaration of which security mechanisms can be used across the API.
/// </summary>
public IList<OpenApiSecurityRequirement> SecurityRequirements { get; set; } =
public IList<OpenApiSecurityRequirement>? SecurityRequirements { get; set; } =
new List<OpenApiSecurityRequirement>();

/// <summary>
/// A list of tags used by the specification with additional metadata.
/// </summary>
public IList<OpenApiTag> Tags { get; set; } = new List<OpenApiTag>();
public IList<OpenApiTag>? Tags { get; set; } = new List<OpenApiTag>();

/// <summary>
/// Additional external documentation.
/// </summary>
public OpenApiExternalDocs ExternalDocs { get; set; }
public OpenApiExternalDocs? ExternalDocs { get; set; }

/// <summary>
/// This object MAY be extended with Specification Extensions.
/// </summary>
public IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
public IDictionary<string, IOpenApiExtension>? Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();

/// <summary>
/// The unique hash code of the generated OpenAPI document
Expand All @@ -97,25 +99,28 @@
public OpenApiDocument()
{
Workspace = new OpenApiWorkspace();
BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid().ToString());
BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid());
Info = new OpenApiInfo();
Paths = new OpenApiPaths();
}

/// <summary>
/// Initializes a copy of an an <see cref="OpenApiDocument"/> object
/// </summary>
public OpenApiDocument(OpenApiDocument document)
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<OpenApiServer>(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<string, OpenApiPathItem>(document.Webhooks) : null;
Components = document?.Components != null ? new(document?.Components) : null;
SecurityRequirements = document?.SecurityRequirements != null ? new List<OpenApiSecurityRequirement>(document.SecurityRequirements) : null;
Tags = document?.Tags != null ? new List<OpenApiTag>(document.Tags) : null;
ExternalDocs = document?.ExternalDocs != null ? new(document?.ExternalDocs) : null;
Extensions = document?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(document.Extensions) : null;
BaseUri = document?.BaseUri != null ? document.BaseUri : new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid());
}

/// <summary>
Expand Down Expand Up @@ -360,7 +365,7 @@
return parsedUrl;
}

private static void WriteHostInfoV2(IOpenApiWriter writer, IList<OpenApiServer> servers)
private static void WriteHostInfoV2(IOpenApiWriter writer, IList<OpenApiServer>? servers)
{
if (servers == null || !servers.Any())
{
Expand Down Expand Up @@ -440,7 +445,7 @@
/// <summary>
/// Load the referenced <see cref="IOpenApiReferenceable"/> object from a <see cref="OpenApiReference"/> object
/// </summary>
internal T ResolveReferenceTo<T>(OpenApiReference reference) where T : class, IOpenApiReferenceable
internal T? ResolveReferenceTo<T>(OpenApiReference reference) where T : class, IOpenApiReferenceable
{
if (reference.IsExternal)
{
Expand Down Expand Up @@ -489,7 +494,7 @@
/// <summary>
/// Load the referenced <see cref="IOpenApiReferenceable"/> object from a <see cref="OpenApiReference"/> object
/// </summary>
internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool useExternal)
internal IOpenApiReferenceable? ResolveReference(OpenApiReference? reference, bool useExternal)
{
if (reference == null)
{
Expand All @@ -504,14 +509,14 @@
// Special case for Tag
if (reference.Type == ReferenceType.Tag)
{
foreach (var tag in this.Tags)
foreach (var tag in this.Tags ?? Enumerable.Empty<OpenApiTag>())
{
if (tag.Name == reference.Id)
{
tag.Reference = reference;
return tag;
}
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Where Note

This foreach loop
implicitly filters its target sequence
- consider filtering the sequence explicitly using '.Where(...)'.

return null;
}
Expand All @@ -526,11 +531,11 @@
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<IOpenApiReferenceable>(uriLocation);
return Workspace?.ResolveReference<IOpenApiReferenceable>(uriLocation);
}

/// <summary>
Expand All @@ -539,7 +544,7 @@
/// <param name="url"> The path to the OpenAPI file.</param>
/// <param name="settings"></param>
/// <returns></returns>
public static ReadResult Load(string url, OpenApiReaderSettings settings = null)
public static ReadResult Load(string url, OpenApiReaderSettings? settings = null)
{
return OpenApiModelFactory.Load(url, settings);
}
Expand All @@ -553,7 +558,7 @@
/// <returns></returns>
public static ReadResult Load(Stream stream,
string format,
OpenApiReaderSettings settings = null)
OpenApiReaderSettings? settings = null)
{
return OpenApiModelFactory.Load(stream, format, settings);
}
Expand All @@ -567,7 +572,7 @@
/// <returns></returns>
public static ReadResult Load(TextReader input,
string format,
OpenApiReaderSettings settings = null)
OpenApiReaderSettings? settings = null)
{
return OpenApiModelFactory.Load(input, format, settings);
}
Expand All @@ -578,7 +583,7 @@
/// <param name="url"> The path to the OpenAPI file.</param>
/// <param name="settings">The OpenApi reader settings.</param>
/// <returns></returns>
public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings settings = null)
public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings? settings = null)
{
return await OpenApiModelFactory.LoadAsync(url, settings);
}
Expand All @@ -591,7 +596,7 @@
/// <param name="settings">The OpenApi reader settings.</param>
/// <param name="cancellationToken">Propagates information about operation cancelling.</param>
/// <returns></returns>
public static async Task<ReadResult> LoadAsync(Stream stream, string format, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default)
public static async Task<ReadResult> LoadAsync(Stream stream, string format, OpenApiReaderSettings? settings = null, CancellationToken cancellationToken = default)
{
return await OpenApiModelFactory.LoadAsync(stream, format, settings, cancellationToken);
}
Expand All @@ -603,7 +608,7 @@
/// <param name="format"> The OpenAPI format to use during parsing.</param>
/// <param name="settings">The OpenApi reader settings.</param>
/// <returns></returns>
public static async Task<ReadResult> LoadAsync(TextReader input, string format, OpenApiReaderSettings settings = null)
public static async Task<ReadResult> LoadAsync(TextReader input, string format, OpenApiReaderSettings? settings = null)
{
return await OpenApiModelFactory.LoadAsync(input, format, settings);
}
Expand All @@ -616,18 +621,18 @@
/// <param name="settings"></param>
/// <returns></returns>
public static ReadResult Parse(string input,
string format = null,
OpenApiReaderSettings settings = null)
string? format = null,
OpenApiReaderSettings? settings = null)
{
return OpenApiModelFactory.Parse(input, format, settings);
}
}

internal class FindSchemaReferences : OpenApiVisitorBase
{
private Dictionary<string, OpenApiSchema> Schemas;
private Dictionary<string, OpenApiSchema> Schemas = new();

public static void ResolveSchemas(OpenApiComponents components, Dictionary<string, OpenApiSchema> schemas)
public static void ResolveSchemas(OpenApiComponents? components, Dictionary<string, OpenApiSchema> schemas)
{
var visitor = new FindSchemaReferences();
visitor.Schemas = schemas;
Expand Down
Loading
Loading