Skip to content

Commit 2deb4aa

Browse files
committed
feat: Added nullable enable to OpenApiDocument.
1 parent c06a9f1 commit 2deb4aa

File tree

7 files changed

+66
-57
lines changed

7 files changed

+66
-57
lines changed

src/Microsoft.OpenApi.Hidi/OpenApiService.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation. All rights reserved.
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

44
using System;
@@ -185,7 +185,7 @@ private static OpenApiDocument ApplyFilters(HidiOptions options, ILogger logger,
185185
stopwatch.Start();
186186
document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
187187
stopwatch.Stop();
188-
logger.LogTrace("{Timestamp}ms: Creating filtered OpenApi document with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths.Count);
188+
logger.LogTrace("{Timestamp}ms: Creating filtered OpenApi document with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths?.Count);
189189
}
190190

191191
return document;
@@ -248,7 +248,7 @@ private static async Task<OpenApiDocument> GetOpenApi(HidiOptions options, strin
248248

249249
document = await ConvertCsdlToOpenApi(filteredStream ?? stream, format, metadataVersion, options.SettingsConfig, cancellationToken).ConfigureAwait(false);
250250
stopwatch.Stop();
251-
logger.LogTrace("{Timestamp}ms: Generated OpenAPI with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths.Count);
251+
logger.LogTrace("{Timestamp}ms: Generated OpenAPI with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths?.Count);
252252
}
253253
}
254254
else if (!string.IsNullOrEmpty(options.OpenApi))
@@ -659,7 +659,7 @@ internal static void WriteTreeDocumentAsMarkdown(string openapiUrl, OpenApiDocum
659659
{
660660
var rootNode = OpenApiUrlTreeNode.Create(document, "main");
661661

662-
writer.WriteLine("# " + document.Info.Title);
662+
writer.WriteLine("# " + document.Info?.Title);
663663
writer.WriteLine();
664664
writer.WriteLine("API Description: " + openapiUrl);
665665

@@ -695,7 +695,7 @@ internal static void WriteTreeDocumentAsHtml(string sourceUrl, OpenApiDocument d
695695
</style>
696696
<body>
697697
""");
698-
writer.WriteLine("<h1>" + document.Info.Title + "</h1>");
698+
writer.WriteLine("<h1>" + document.Info?.Title + "</h1>");
699699
writer.WriteLine();
700700
writer.WriteLine($"<h3> API Description: <a href='{sourceUrl}'>{sourceUrl}</a></h3>");
701701

@@ -766,8 +766,8 @@ internal static async Task PluginManifest(HidiOptions options, ILogger logger, C
766766
// Create OpenAIPluginManifest from ApiDependency and OpenAPI document
767767
var manifest = new OpenAIPluginManifest
768768
{
769-
NameForHuman = document.Info.Title,
770-
DescriptionForHuman = document.Info.Description,
769+
NameForHuman = document.Info?.Title,
770+
DescriptionForHuman = document.Info?.Description,
771771
Api = new()
772772
{
773773
Type = "openapi",

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
using Microsoft.OpenApi.Services;
1717
using Microsoft.OpenApi.Writers;
1818

19+
#nullable enable
20+
1921
namespace Microsoft.OpenApi.Models
2022
{
2123
/// <summary>
@@ -26,60 +28,60 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IBaseDo
2628
/// <summary>
2729
/// Related workspace containing OpenApiDocuments that are referenced in this document
2830
/// </summary>
29-
public OpenApiWorkspace Workspace { get; set; }
31+
public OpenApiWorkspace? Workspace { get; set; }
3032

3133
/// <summary>
3234
/// REQUIRED. Provides metadata about the API. The metadata MAY be used by tooling as required.
3335
/// </summary>
34-
public OpenApiInfo Info { get; set; }
36+
public OpenApiInfo? Info { get; set; }
3537

3638
/// <summary>
3739
/// The default value for the $schema keyword within Schema Objects contained within this OAS document. This MUST be in the form of a URI.
3840
/// </summary>
39-
public string JsonSchemaDialect { get; set; }
41+
public string? JsonSchemaDialect { get; set; }
4042

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

4648
/// <summary>
4749
/// REQUIRED. The available paths and operations for the API.
4850
/// </summary>
49-
public OpenApiPaths Paths { get; set; }
51+
public OpenApiPaths? Paths { get; set; }
5052

5153
/// <summary>
5254
/// The incoming webhooks that MAY be received as part of this API and that the API consumer MAY choose to implement.
5355
/// A map of requests initiated other than by an API call, for example by an out of band registration.
5456
/// 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
5557
/// </summary>
56-
public IDictionary<string, OpenApiPathItem> Webhooks { get; set; } = new Dictionary<string, OpenApiPathItem>();
58+
public IDictionary<string, OpenApiPathItem>? Webhooks { get; set; } = new Dictionary<string, OpenApiPathItem>();
5759

5860
/// <summary>
5961
/// An element to hold various schemas for the specification.
6062
/// </summary>
61-
public OpenApiComponents Components { get; set; }
63+
public OpenApiComponents? Components { get; set; }
6264

6365
/// <summary>
6466
/// A declaration of which security mechanisms can be used across the API.
6567
/// </summary>
66-
public IList<OpenApiSecurityRequirement> SecurityRequirements { get; set; } =
68+
public IList<OpenApiSecurityRequirement>? SecurityRequirements { get; set; } =
6769
new List<OpenApiSecurityRequirement>();
6870

6971
/// <summary>
7072
/// A list of tags used by the specification with additional metadata.
7173
/// </summary>
72-
public IList<OpenApiTag> Tags { get; set; } = new List<OpenApiTag>();
74+
public IList<OpenApiTag>? Tags { get; set; } = new List<OpenApiTag>();
7375

7476
/// <summary>
7577
/// Additional external documentation.
7678
/// </summary>
77-
public OpenApiExternalDocs ExternalDocs { get; set; }
79+
public OpenApiExternalDocs? ExternalDocs { get; set; }
7880

7981
/// <summary>
8082
/// This object MAY be extended with Specification Extensions.
8183
/// </summary>
82-
public IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
84+
public IDictionary<string, IOpenApiExtension>? Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
8385

8486
/// <summary>
8587
/// The unique hash code of the generated OpenAPI document
@@ -97,13 +99,13 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IBaseDo
9799
public OpenApiDocument()
98100
{
99101
Workspace = new OpenApiWorkspace();
100-
BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid().ToString());
102+
BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid().ToString());
101103
}
102104

103105
/// <summary>
104106
/// Initializes a copy of an an <see cref="OpenApiDocument"/> object
105107
/// </summary>
106-
public OpenApiDocument(OpenApiDocument document)
108+
public OpenApiDocument(OpenApiDocument? document)
107109
{
108110
Workspace = document?.Workspace != null ? new(document?.Workspace) : null;
109111
Info = document?.Info != null ? new(document?.Info) : null;
@@ -116,6 +118,7 @@ public OpenApiDocument(OpenApiDocument document)
116118
Tags = document?.Tags != null ? new List<OpenApiTag>(document.Tags) : null;
117119
ExternalDocs = document?.ExternalDocs != null ? new(document?.ExternalDocs) : null;
118120
Extensions = document?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(document.Extensions) : null;
121+
BaseUri = document?.BaseUri != null ? document.BaseUri : new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid().ToString());
119122
}
120123

121124
/// <summary>
@@ -242,8 +245,10 @@ public void SerializeAsV2(IOpenApiWriter writer)
242245

243246
if (loops.TryGetValue(typeof(JsonSchema), out List<object> schemas))
244247
{
245-
var openApiSchemas = schemas.Cast<JsonSchema>().Distinct()
246-
.ToDictionary(k => k.GetRef().ToString());
248+
var openApiSchemas = schemas.Cast<JsonSchema>()
249+
.Distinct()
250+
.Where(s => s.GetRef() != null)
251+
.ToDictionary(k => k.GetRef()!.ToString());
247252

248253
foreach (var schema in openApiSchemas.Values.ToList())
249254
{
@@ -377,7 +382,7 @@ private static string ParseServerUrl(OpenApiServer server)
377382
return parsedUrl;
378383
}
379384

380-
private static void WriteHostInfoV2(IOpenApiWriter writer, IList<OpenApiServer> servers)
385+
private static void WriteHostInfoV2(IOpenApiWriter writer, IList<OpenApiServer>? servers)
381386
{
382387
if (servers == null || !servers.Any())
383388
{
@@ -457,7 +462,7 @@ public void SetReferenceHostDocument()
457462
/// <summary>
458463
/// Load the referenced <see cref="IOpenApiReferenceable"/> object from a <see cref="OpenApiReference"/> object
459464
/// </summary>
460-
internal T ResolveReferenceTo<T>(OpenApiReference reference) where T : class, IOpenApiReferenceable
465+
internal T? ResolveReferenceTo<T>(OpenApiReference reference) where T : class, IOpenApiReferenceable
461466
{
462467
if (reference.IsExternal)
463468
{
@@ -472,7 +477,7 @@ internal T ResolveReferenceTo<T>(OpenApiReference reference) where T : class, IO
472477
/// <summary>
473478
/// Load the referenced <see cref="IOpenApiReferenceable"/> object from a <see cref="OpenApiReference"/> object
474479
/// </summary>
475-
public IOpenApiReferenceable ResolveReference(OpenApiReference reference)
480+
public IOpenApiReferenceable? ResolveReference(OpenApiReference reference)
476481
{
477482
return ResolveReference(reference, false);
478483
}
@@ -482,7 +487,7 @@ public IOpenApiReferenceable ResolveReference(OpenApiReference reference)
482487
/// </summary>
483488
/// <param name="referenceUri"></param>
484489
/// <returns>A JsonSchema ref.</returns>
485-
public JsonSchema ResolveJsonSchemaReference(Uri referenceUri)
490+
public JsonSchema? ResolveJsonSchemaReference(Uri referenceUri)
486491
{
487492
const char pound = '#';
488493
string uriLocation;
@@ -492,7 +497,7 @@ public JsonSchema ResolveJsonSchemaReference(Uri referenceUri)
492497
{
493498
// External reference, ex: ./TodoReference.yaml#/components/schemas/todo
494499
string externalUri = referenceUri.OriginalString.Split(pound).First();
495-
Uri externalDocId = Workspace.GetDocumentId(externalUri);
500+
Uri? externalDocId = Workspace?.GetDocumentId(externalUri);
496501
string relativePath = referenceUri.OriginalString.Split(pound).Last();
497502
uriLocation = externalDocId + relativePath;
498503
}
@@ -501,7 +506,7 @@ public JsonSchema ResolveJsonSchemaReference(Uri referenceUri)
501506
uriLocation = BaseUri + referenceUri.ToString().TrimStart(pound);
502507
}
503508

504-
return (JsonSchema)Workspace.ResolveReference<IBaseDocument>(uriLocation);
509+
return Workspace?.ResolveReference<IBaseDocument>(uriLocation) as JsonSchema;
505510
}
506511

507512
/// <summary>
@@ -541,7 +546,7 @@ private static string ConvertByteArrayToString(byte[] hash)
541546
/// <summary>
542547
/// Load the referenced <see cref="IOpenApiReferenceable"/> object from a <see cref="OpenApiReference"/> object
543548
/// </summary>
544-
internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool useExternal)
549+
internal IOpenApiReferenceable? ResolveReference(OpenApiReference? reference, bool useExternal)
545550
{
546551
if (reference == null)
547552
{
@@ -556,7 +561,7 @@ internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool
556561
// Special case for Tag
557562
if (reference.Type == ReferenceType.Tag)
558563
{
559-
foreach (var tag in this.Tags)
564+
foreach (var tag in this.Tags ?? Enumerable.Empty<OpenApiTag>())
560565
{
561566
if (tag.Name == reference.Id)
562567
{
@@ -572,10 +577,10 @@ internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool
572577
string relativePath = OpenApiConstants.ComponentsSegment + reference.Type.GetDisplayName() + "/" + reference.Id;
573578

574579
uriLocation = useExternal
575-
? Workspace.GetDocumentId(reference.ExternalResource)?.OriginalString + relativePath
580+
? Workspace?.GetDocumentId(reference.ExternalResource)?.OriginalString + relativePath
576581
: BaseUri + relativePath;
577582

578-
return Workspace.ResolveReference<IOpenApiReferenceable>(uriLocation);
583+
return Workspace?.ResolveReference<IOpenApiReferenceable>(uriLocation);
579584
}
580585

581586
/// <summary>
@@ -584,7 +589,7 @@ internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool
584589
/// <param name="url"> The path to the OpenAPI file.</param>
585590
/// <param name="settings"></param>
586591
/// <returns></returns>
587-
public static ReadResult Load(string url, OpenApiReaderSettings settings = null)
592+
public static ReadResult Load(string url, OpenApiReaderSettings? settings = null)
588593
{
589594
return OpenApiModelFactory.Load(url, settings);
590595
}
@@ -598,7 +603,7 @@ public static ReadResult Load(string url, OpenApiReaderSettings settings = null)
598603
/// <returns></returns>
599604
public static ReadResult Load(Stream stream,
600605
string format,
601-
OpenApiReaderSettings settings = null)
606+
OpenApiReaderSettings? settings = null)
602607
{
603608
return OpenApiModelFactory.Load(stream, format, settings);
604609
}
@@ -612,7 +617,7 @@ public static ReadResult Load(Stream stream,
612617
/// <returns></returns>
613618
public static ReadResult Load(TextReader input,
614619
string format,
615-
OpenApiReaderSettings settings = null)
620+
OpenApiReaderSettings? settings = null)
616621
{
617622
return OpenApiModelFactory.Load(input, format, settings);
618623
}
@@ -623,7 +628,7 @@ public static ReadResult Load(TextReader input,
623628
/// <param name="url"> The path to the OpenAPI file.</param>
624629
/// <param name="settings">The OpenApi reader settings.</param>
625630
/// <returns></returns>
626-
public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings settings = null)
631+
public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings? settings = null)
627632
{
628633
return await OpenApiModelFactory.LoadAsync(url, settings);
629634
}
@@ -636,7 +641,7 @@ public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings
636641
/// <param name="settings">The OpenApi reader settings.</param>
637642
/// <param name="cancellationToken">Propagates information about operation cancelling.</param>
638643
/// <returns></returns>
639-
public static async Task<ReadResult> LoadAsync(Stream stream, string format, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default)
644+
public static async Task<ReadResult> LoadAsync(Stream stream, string format, OpenApiReaderSettings? settings = null, CancellationToken cancellationToken = default)
640645
{
641646
return await OpenApiModelFactory.LoadAsync(stream, format, settings, cancellationToken);
642647
}
@@ -648,7 +653,7 @@ public static async Task<ReadResult> LoadAsync(Stream stream, string format, Ope
648653
/// <param name="format"> The OpenAPI format to use during parsing.</param>
649654
/// <param name="settings">The OpenApi reader settings.</param>
650655
/// <returns></returns>
651-
public static async Task<ReadResult> LoadAsync(TextReader input, string format, OpenApiReaderSettings settings = null)
656+
public static async Task<ReadResult> LoadAsync(TextReader input, string format, OpenApiReaderSettings? settings = null)
652657
{
653658
return await OpenApiModelFactory.LoadAsync(input, format, settings);
654659
}
@@ -661,8 +666,8 @@ public static async Task<ReadResult> LoadAsync(TextReader input, string format,
661666
/// <param name="settings"></param>
662667
/// <returns></returns>
663668
public static ReadResult Parse(string input,
664-
string format = null,
665-
OpenApiReaderSettings settings = null)
669+
string? format = null,
670+
OpenApiReaderSettings? settings = null)
666671
{
667672
return OpenApiModelFactory.Parse(input, format, settings);
668673
}
@@ -674,18 +679,18 @@ public static ReadResult Parse(string input,
674679
/// <param name="options"></param>
675680
/// <returns></returns>
676681
/// <exception cref="NotImplementedException"></exception>
677-
public JsonSchema FindSubschema(Json.Pointer.JsonPointer pointer, EvaluationOptions options)
682+
public JsonSchema? FindSubschema(Json.Pointer.JsonPointer pointer, EvaluationOptions options)
678683
{
679684
var locationUri = string.Concat(BaseUri, pointer);
680-
return (JsonSchema)Workspace.ResolveReference<IBaseDocument>(locationUri);
685+
return Workspace?.ResolveReference<IBaseDocument>(locationUri) as JsonSchema;
681686
}
682687
}
683688

684689
internal class FindSchemaReferences : OpenApiVisitorBase
685690
{
686-
private Dictionary<string, JsonSchema> Schemas;
691+
private Dictionary<string, JsonSchema>? Schemas;
687692

688-
public static void ResolveSchemas(OpenApiComponents components, Dictionary<string, JsonSchema> schemas)
693+
public static void ResolveSchemas(OpenApiComponents? components, Dictionary<string, JsonSchema> schemas)
689694
{
690695
var visitor = new FindSchemaReferences();
691696
visitor.Schemas = schemas;

src/Microsoft.OpenApi/Services/OpenApiWalker.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,11 @@ internal void Walk(OpenApiExternalDocs externalDocs)
106106

107107
_visitor.Visit(externalDocs);
108108
}
109-
109+
#nullable enable
110110
/// <summary>
111111
/// Visits <see cref="OpenApiComponents"/> and child objects
112112
/// </summary>
113-
internal void Walk(OpenApiComponents components)
113+
internal void Walk(OpenApiComponents? components)
114114
{
115115
if (components == null)
116116
{
@@ -119,11 +119,6 @@ internal void Walk(OpenApiComponents components)
119119

120120
_visitor.Visit(components);
121121

122-
if (components == null)
123-
{
124-
return;
125-
}
126-
127122
Walk(OpenApiConstants.Schemas, () =>
128123
{
129124
if (components.Schemas != null)
@@ -237,6 +232,7 @@ internal void Walk(OpenApiComponents components)
237232
Walk(components as IOpenApiExtensible);
238233
}
239234

235+
#nullable restore
240236
/// <summary>
241237
/// Visits <see cref="OpenApiPaths"/> and child objects
242238
/// </summary>

0 commit comments

Comments
 (0)