diff --git a/CHANGELOG.md b/CHANGELOG.md index f65217252f..f2ea809c88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added support for OpenAPI 3.1. [#3914](https://github.com/microsoft/kiota/issues/3914) + ### Changed - Fixed mapping of Binary Types to array buffer in TypeScript. [#6124](https://github.com/microsoft/kiota/issues/6124) @@ -35,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed a bug in the VS Code extension deeplink with the API Center extension [#6004](https://github.com/microsoft/kiota/issues/6004) - Drops Python 3.8 support by removing deprecated type aliases from generated code. [microsoft/kiota-python#349](https://github.com/microsoft/kiota-python/issues/349) -- Removes superfluous inline imports in serializer methods in Python Generation. +- Removes superfluous inline imports in serializer methods in Python Generation. ## [1.22.2] @@ -1570,5 +1572,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Initial GitHub release - - diff --git a/src/Kiota.Builder/Configuration/LanguagesInformation.cs b/src/Kiota.Builder/Configuration/LanguagesInformation.cs index 5dcb41dc59..c96c854d59 100644 --- a/src/Kiota.Builder/Configuration/LanguagesInformation.cs +++ b/src/Kiota.Builder/Configuration/LanguagesInformation.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.OpenApi.Any; +using System.Text.Json.Nodes; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Writers; @@ -9,23 +9,16 @@ namespace Kiota.Builder.Configuration; public class LanguagesInformation : Dictionary, IOpenApiSerializable, ICloneable { - public void SerializeAsV2(IOpenApiWriter writer) => SerializeAsV3(writer); - public void SerializeAsV3(IOpenApiWriter writer) + public void SerializeAsV2(IOpenApiWriter writer) => SerializeAsV31(writer); + public void SerializeAsV3(IOpenApiWriter writer) => SerializeAsV31(writer); + public static LanguagesInformation Parse(JsonObject jsonNode) { - ArgumentNullException.ThrowIfNull(writer); - writer.WriteStartObject(); - foreach (var entry in this.OrderBy(static x => x.Key, StringComparer.OrdinalIgnoreCase)) + var extension = new LanguagesInformation(); + foreach (var property in jsonNode.Where(static property => property.Value is JsonObject)) { - writer.WriteRequiredObject(entry.Key, entry.Value, (w, x) => x.SerializeAsV3(w)); + extension.Add(property.Key, LanguageInformation.Parse(property.Value!)); } - writer.WriteEndObject(); - } - public static LanguagesInformation Parse(IOpenApiAny source) - { - if (source is not OpenApiObject rawObject) throw new ArgumentOutOfRangeException(nameof(source)); - var extension = new LanguagesInformation(); - foreach (var property in rawObject) - extension.Add(property.Key, LanguageInformation.Parse(property.Value)); + return extension; } @@ -36,4 +29,15 @@ public object Clone() result.Add(entry.Key, entry.Value);// records don't need to be cloned as they are immutable return result; } + + public void SerializeAsV31(IOpenApiWriter writer) + { + ArgumentNullException.ThrowIfNull(writer); + writer.WriteStartObject(); + foreach (var entry in this.OrderBy(static x => x.Key, StringComparer.OrdinalIgnoreCase)) + { + writer.WriteRequiredObject(entry.Key, entry.Value, (w, x) => x.SerializeAsV3(w)); + } + writer.WriteEndObject(); + } } diff --git a/src/Kiota.Builder/EqualityComparers/OpenApiSchemaReferenceComparer.cs b/src/Kiota.Builder/EqualityComparers/OpenApiSchemaReferenceComparer.cs index 588bfde78c..ceb2097b2c 100644 --- a/src/Kiota.Builder/EqualityComparers/OpenApiSchemaReferenceComparer.cs +++ b/src/Kiota.Builder/EqualityComparers/OpenApiSchemaReferenceComparer.cs @@ -1,28 +1,30 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Models.References; namespace Kiota.Builder.EqualityComparers; -internal class OpenApiSchemaReferenceComparer(StringComparer? stringComparer = null) : IEqualityComparer +internal class OpenApiSchemaReferenceComparer(StringComparer? stringComparer = null) : IEqualityComparer { private readonly StringComparer _stringComparer = stringComparer ?? StringComparer.OrdinalIgnoreCase; - public bool Equals(OpenApiSchema? x, OpenApiSchema? y) + public bool Equals(IOpenApiSchema? x, IOpenApiSchema? y) { - if (string.IsNullOrEmpty(x?.Reference?.Id) || string.IsNullOrEmpty(y?.Reference?.Id)) return object.Equals(x, y); - return _stringComparer.Equals(x.Reference.Id, y.Reference.Id); + if (x is not OpenApiSchemaReference xRef || y is not OpenApiSchemaReference yRef) return object.Equals(x, y); + return _stringComparer.Equals(xRef.Reference.Id, yRef.Reference.Id); } - public int GetHashCode([DisallowNull] OpenApiSchema obj) + public int GetHashCode([DisallowNull] IOpenApiSchema obj) { + if (obj is not OpenApiSchemaReference objRef) return obj.GetHashCode(); var hash = new HashCode(); - if (!string.IsNullOrEmpty(obj.Reference?.Id)) + if (!string.IsNullOrEmpty(objRef.Reference?.Id)) { - hash.Add(obj.Reference.Id, _stringComparer); + hash.Add(objRef.Reference.Id, _stringComparer); } else { - hash.Add(obj); + hash.Add(objRef); } return hash.ToHashCode(); diff --git a/src/Kiota.Builder/Extensions/OpenApiDeprecationExtensionExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiDeprecationExtensionExtensions.cs index 75a2b5921d..afafdc03a1 100644 --- a/src/Kiota.Builder/Extensions/OpenApiDeprecationExtensionExtensions.cs +++ b/src/Kiota.Builder/Extensions/OpenApiDeprecationExtensionExtensions.cs @@ -3,6 +3,7 @@ using Kiota.Builder.CodeDOM; using Microsoft.OpenApi.MicrosoftExtensions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Services; namespace Kiota.Builder.Extensions; @@ -12,38 +13,36 @@ internal static DeprecationInformation ToDeprecationInformation(this OpenApiDepr { return new DeprecationInformation(value.Description.CleanupDescription().CleanupXMLString(), value.Date, value.RemovalDate, value.Version.CleanupDescription().CleanupXMLString(), true); } - internal static DeprecationInformation GetDeprecationInformation(this OpenApiSchema schema) + internal static DeprecationInformation GetDeprecationInformation(this IOpenApiSchema schema) { if (schema.Deprecated && schema.Extensions.TryGetValue(OpenApiDeprecationExtension.Name, out var deprecatedExtension) && deprecatedExtension is OpenApiDeprecationExtension deprecatedValue) return deprecatedValue.ToDeprecationInformation(); return new(null, null, null, null, schema.Deprecated); } - internal static DeprecationInformation GetDeprecationInformation(this OpenApiParameter parameter) + internal static DeprecationInformation GetDeprecationInformation(this IOpenApiParameter parameter) { if (parameter.Deprecated && parameter.Extensions.TryGetValue(OpenApiDeprecationExtension.Name, out var deprecatedExtension) && deprecatedExtension is OpenApiDeprecationExtension deprecatedValue) return deprecatedValue.ToDeprecationInformation(); else if (parameter.Schema != null && !parameter.Schema.IsReferencedSchema() && parameter.Schema.Deprecated) return parameter.Schema.GetDeprecationInformation(); - else if (parameter.Content.Values.Select(static x => x.Schema).Where(static x => x != null && !x.IsReferencedSchema() && x.Deprecated).Select(static x => x.GetDeprecationInformation()).FirstOrDefault(static x => x.IsDeprecated) is DeprecationInformation contentDeprecationInformation) + else if (parameter.Content.Values.Select(static x => x.Schema).Where(static x => x != null && !x.IsReferencedSchema() && x.Deprecated).Select(static x => x!.GetDeprecationInformation()).FirstOrDefault(static x => x.IsDeprecated) is DeprecationInformation contentDeprecationInformation) return contentDeprecationInformation; return new(null, null, null, null, parameter.Deprecated); } internal static DeprecationInformation GetDeprecationInformation(this OpenApiOperation operation) { - if (operation.Deprecated && operation.Extensions.TryGetValue(OpenApiDeprecationExtension.Name, out var deprecatedExtension) && deprecatedExtension is OpenApiDeprecationExtension deprecatedValue) + if (operation.Deprecated && operation.Extensions is not null && operation.Extensions.TryGetValue(OpenApiDeprecationExtension.Name, out var deprecatedExtension) && deprecatedExtension is OpenApiDeprecationExtension deprecatedValue) return deprecatedValue.ToDeprecationInformation(); - else if (operation.Responses.Values + else if (operation.Responses?.Values .SelectMany(static x => x.Content.Values) .Select(static x => x?.Schema) .OfType() - .Where(static x => !x.IsReferencedSchema()) .Select(static x => x.GetDeprecationInformation()) .FirstOrDefault(static x => x.IsDeprecated) is DeprecationInformation responseDeprecationInformation) return responseDeprecationInformation; else if (operation.RequestBody?.Content.Values .Select(static x => x?.Schema) .OfType() - .Where(static x => !x.IsReferencedSchema()) .Select(static x => x.GetDeprecationInformation()) .FirstOrDefault(static x => x.IsDeprecated) is DeprecationInformation requestDeprecationInformation) return requestDeprecationInformation; diff --git a/src/Kiota.Builder/Extensions/OpenApiDocumentExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiDocumentExtensions.cs index dc46ca6807..04fd3d6e9f 100644 --- a/src/Kiota.Builder/Extensions/OpenApiDocumentExtensions.cs +++ b/src/Kiota.Builder/Extensions/OpenApiDocumentExtensions.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Kiota.Builder.EqualityComparers; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.References; namespace Kiota.Builder.Extensions; @@ -19,7 +20,7 @@ internal static void InitializeInheritanceIndex(this OpenApiDocument openApiDocu { inheritanceIndex.TryAdd(entry.Key, new(StringComparer.OrdinalIgnoreCase)); if (entry.Value.AllOf != null) - foreach (var allOfEntry in entry.Value.AllOf.Where(static x => !string.IsNullOrEmpty(x.Reference?.Id))) + foreach (var allOfEntry in entry.Value.AllOf.OfType()) { var dependents = inheritanceIndex.GetOrAdd(allOfEntry.Reference.Id, new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase)); dependents.TryAdd(entry.Key, false); @@ -31,7 +32,7 @@ internal static void InitializeInheritanceIndex(this OpenApiDocument openApiDocu { ArgumentNullException.ThrowIfNull(openApiDocument); var candidateUrl = openApiDocument.Servers - .GroupBy(static x => x, new OpenApiServerComparer()) //group by protocol relative urls + ?.GroupBy(static x => x, new OpenApiServerComparer()) //group by protocol relative urls .FirstOrDefault() ?.OrderByDescending(static x => x.Url, StringComparer.OrdinalIgnoreCase) // prefer https over http ?.FirstOrDefault() diff --git a/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs index 2ca1fd3009..ec8dd8fa8c 100644 --- a/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs +++ b/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs @@ -3,6 +3,7 @@ using System.Linq; using Kiota.Builder.Configuration; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; namespace Kiota.Builder.Extensions; public static class OpenApiOperationExtensions @@ -21,21 +22,22 @@ private static string vendorSpecificCleanup(string input) /// /// cleans application/vnd.github.mercy-preview+json to application/json /// - internal static OpenApiSchema? GetResponseSchema(this OpenApiOperation operation, StructuredMimeTypesCollection structuredMimeTypes) + internal static IOpenApiSchema? GetResponseSchema(this OpenApiOperation operation, StructuredMimeTypesCollection structuredMimeTypes) { ArgumentNullException.ThrowIfNull(operation); // Return Schema that represents all the possible success responses! return operation.GetResponseSchemas(SuccessCodes, structuredMimeTypes) .FirstOrDefault(); } - internal static IEnumerable GetResponseSchemas(this OpenApiOperation operation, HashSet successCodesToUse, StructuredMimeTypesCollection structuredMimeTypes) + internal static IEnumerable GetResponseSchemas(this OpenApiOperation operation, HashSet successCodesToUse, StructuredMimeTypesCollection structuredMimeTypes) { + if (operation.Responses is null) return []; // Return Schema that represents all the possible success responses! return operation.Responses.Where(r => successCodesToUse.Contains(r.Key)) .OrderBy(static x => x.Key, StringComparer.OrdinalIgnoreCase) .SelectMany(re => re.Value.Content.GetValidSchemas(structuredMimeTypes)); } - internal static OpenApiSchema? GetRequestSchema(this OpenApiOperation operation, StructuredMimeTypesCollection structuredMimeTypes) + internal static IOpenApiSchema? GetRequestSchema(this OpenApiOperation operation, StructuredMimeTypesCollection structuredMimeTypes) { ArgumentNullException.ThrowIfNull(operation); return operation.RequestBody?.Content @@ -44,7 +46,7 @@ internal static IEnumerable GetResponseSchemas(this OpenApiOperat private static readonly StructuredMimeTypesCollection multipartMimeTypes = new(["multipart/form-data"]); internal static bool IsMultipartFormDataSchema(this IDictionary source, StructuredMimeTypesCollection structuredMimeTypes) { - return source.GetValidSchemas(structuredMimeTypes).FirstOrDefault() is OpenApiSchema schema && + return source.GetValidSchemas(structuredMimeTypes).FirstOrDefault() is IOpenApiSchema schema && source.GetValidSchemas(multipartMimeTypes).FirstOrDefault() == schema; } internal static bool IsMultipartTopMimeType(this IDictionary source, StructuredMimeTypesCollection structuredMimeTypes) @@ -56,7 +58,7 @@ internal static bool IsMultipartTopMimeType(this IDictionary !multipartMimeTypes.Contains(x)).Any(structuredMimeTypes.Contains)) return true; return structuredMimeTypes.First() == multipartMimeTypes.First(); } - internal static IEnumerable GetValidSchemas(this IDictionary source, StructuredMimeTypesCollection structuredMimeTypes) + internal static IEnumerable GetValidSchemas(this IDictionary source, StructuredMimeTypesCollection structuredMimeTypes) { ArgumentNullException.ThrowIfNull(source); ArgumentNullException.ThrowIfNull(structuredMimeTypes); @@ -67,9 +69,9 @@ internal static IEnumerable GetValidSchemas(this IDictionary (Key: c.Key.Split(';', StringSplitOptions.RemoveEmptyEntries)[0], c.Value)) .Where(c => structuredMimeTypes.Contains(c.Key) || structuredMimeTypes.Contains(vendorSpecificCleanup(c.Key))) .Select(static co => co.Value.Schema) - .Where(static s => s is not null); + .OfType(); } - internal static OpenApiSchema? GetResponseSchema(this OpenApiResponse response, StructuredMimeTypesCollection structuredMimeTypes) + internal static IOpenApiSchema? GetResponseSchema(this IOpenApiResponse response, StructuredMimeTypesCollection structuredMimeTypes) { ArgumentNullException.ThrowIfNull(response); return response.Content.GetValidSchemas(structuredMimeTypes).FirstOrDefault(); diff --git a/src/Kiota.Builder/Extensions/OpenApiReferenceExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiReferenceExtensions.cs deleted file mode 100644 index c1c556ee35..0000000000 --- a/src/Kiota.Builder/Extensions/OpenApiReferenceExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.OpenApi.Models; - -namespace Kiota.Builder.Extensions; -public static class OpenApiReferenceExtensions -{ - public static string GetClassName(this OpenApiReference? reference) - { - if (reference?.Id is string referenceId && !string.IsNullOrEmpty(referenceId)) - return referenceId[(referenceId.LastIndexOf('.') + 1)..]; - return string.Empty; - } -} diff --git a/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs index ec9d9d2f6a..0a537d17f0 100644 --- a/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs +++ b/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs @@ -2,22 +2,25 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using Microsoft.OpenApi.Any; +using System.Text.Json; +using Kiota.Builder.OpenApiExtensions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Models.References; namespace Kiota.Builder.Extensions; public static class OpenApiSchemaExtensions { - private static readonly Func> classNamesFlattener = x => - (x.AnyOf ?? Enumerable.Empty()).Union(x.AllOf).Union(x.OneOf).ToList(); - public static IEnumerable GetSchemaNames(this OpenApiSchema schema, bool directOnly = false) + private static readonly Func> classNamesFlattener = x => + (x.AnyOf ?? Enumerable.Empty()).Union(x.AllOf).Union(x.OneOf).ToList(); + public static IEnumerable GetSchemaNames(this IOpenApiSchema schema, bool directOnly = false) { if (schema == null) return []; if (!directOnly && schema.Items != null) return schema.Items.GetSchemaNames(); - if (!string.IsNullOrEmpty(schema.Reference?.Id)) - return [schema.Reference.Id.Split('/')[^1].Split('.')[^1]]; + if (schema.GetReferenceId() is string refId && !string.IsNullOrEmpty(refId)) + return [refId.Split('/')[^1].Split('.')[^1]]; if (!directOnly && schema.AnyOf.Any()) return schema.AnyOf.FlattenIfRequired(classNamesFlattener); if (!directOnly && schema.AllOf.Any()) @@ -26,152 +29,203 @@ public static IEnumerable GetSchemaNames(this OpenApiSchema schema, bool return schema.OneOf.FlattenIfRequired(classNamesFlattener); return []; } - internal static IEnumerable FlattenSchemaIfRequired(this IList schemas, Func> subsequentGetter) + internal static string? GetReferenceId(this IOpenApiSchema schema) + { + return schema switch + { + OpenApiSchemaReference reference => reference.Reference?.Id, + OpenApiSchema s when s.GetMergedSchemaOriginalReferenceId() is string originalReferenceId => originalReferenceId, + _ => null, + }; + } + internal static IEnumerable FlattenSchemaIfRequired(this IList schemas, Func> subsequentGetter) { if (schemas is null) return []; - return schemas.Count == 1 && !schemas[0].HasAnyProperty() && string.IsNullOrEmpty(schemas[0].Reference?.Id) ? + return schemas.Count == 1 && !schemas[0].HasAnyProperty() && string.IsNullOrEmpty(schemas[0].GetReferenceId()) ? schemas.FlattenEmptyEntries(subsequentGetter, 1) : schemas; } - private static IEnumerable FlattenIfRequired(this IList schemas, Func> subsequentGetter) + private static IEnumerable FlattenIfRequired(this IList schemas, Func> subsequentGetter) { return schemas.FlattenSchemaIfRequired(subsequentGetter).SelectMany(static x => x.GetSchemaNames()); } - public static string GetSchemaName(this OpenApiSchema schema, bool directOnly = false) + public static string GetSchemaName(this IOpenApiSchema schema, bool directOnly = false) { return schema.GetSchemaNames(directOnly).LastOrDefault()?.TrimStart('$') ?? string.Empty;// OData $ref } - public static bool IsReferencedSchema(this OpenApiSchema schema) + public static bool IsReferencedSchema(this IOpenApiSchema schema) { - var isReference = schema?.Reference != null; - if (isReference && schema!.Reference.IsExternal) - throw new NotSupportedException("External references are not supported in this version of Kiota. While Kiota awaits on OpenAPI.Net to support inlining external references, you can use https://www.nuget.org/packages/Microsoft.OpenApi.Hidi to generate an OpenAPI description with inlined external references and then use this new reference with Kiota."); - return isReference; + return schema switch + { + OpenApiSchemaReference => true, + _ => false, + }; } - public static bool IsArray(this OpenApiSchema? schema) + public static bool IsArray(this IOpenApiSchema? schema) { - return "array".Equals(schema?.Type, StringComparison.OrdinalIgnoreCase) && schema.Items != null && + return schema is { Type: JsonSchemaType.Array or (JsonSchemaType.Array | JsonSchemaType.Null) } && schema.Items is not null && (schema.Items.IsComposedEnum() || schema.Items.IsEnum() || schema.Items.IsSemanticallyMeaningful() || - FlattenEmptyEntries([schema.Items], static x => x.AnyOf.Union(x.AllOf).Union(x.OneOf).ToList(), 1).FirstOrDefault() is OpenApiSchema flat && flat.IsSemanticallyMeaningful()); + FlattenEmptyEntries([schema.Items], static x => x.AnyOf.Union(x.AllOf).Union(x.OneOf).ToList(), 1).FirstOrDefault() is IOpenApiSchema flat && flat.IsSemanticallyMeaningful()); } - public static bool IsObjectType(this OpenApiSchema? schema) + public static bool IsObjectType(this IOpenApiSchema? schema) { - return "object".Equals(schema?.Type, StringComparison.OrdinalIgnoreCase); + return schema is { Type: JsonSchemaType.Object or (JsonSchemaType.Object | JsonSchemaType.Null) }; } - public static bool HasAnyProperty(this OpenApiSchema? schema) + public static bool HasAnyProperty(this IOpenApiSchema? schema) { return schema?.Properties is { Count: > 0 }; } - public static bool IsInclusiveUnion(this OpenApiSchema? schema, uint exclusiveMinimumNumberOfEntries = 1) + public static bool IsInclusiveUnion(this IOpenApiSchema? schema, uint exclusiveMinimumNumberOfEntries = 1) { return schema?.AnyOf?.Count(static x => IsSemanticallyMeaningful(x, true)) > exclusiveMinimumNumberOfEntries; // so we don't consider any of object/nullable as a union type } - public static bool IsInherited(this OpenApiSchema? schema) + public static bool IsInherited(this IOpenApiSchema? schema) { if (schema is null) return false; var meaningfulMemberSchemas = schema.AllOf.FlattenSchemaIfRequired(static x => x.AllOf) .Where(static x => x.IsSemanticallyMeaningful(ignoreEnums: true, ignoreArrays: true, ignoreType: true)) // the next line ensures the meaningful schema are objects as it won't make sense inheriting from a primitive despite it being meaningful. - .Where(static x => string.IsNullOrEmpty(x.Reference?.Id) || string.IsNullOrEmpty(x.Type) || "object".Equals(x.Type, StringComparison.OrdinalIgnoreCase)) + .Where(static x => string.IsNullOrEmpty(x.GetReferenceId()) || x.Type is null || !x.Type.HasValue || (x.Type.Value & JsonSchemaType.Object) is JsonSchemaType.Object) .ToArray(); var isRootSchemaMeaningful = schema.IsSemanticallyMeaningful(ignoreEnums: true, ignoreArrays: true, ignoreType: true); - return meaningfulMemberSchemas.Count(static x => !string.IsNullOrEmpty(x.Reference?.Id)) == 1 && - (meaningfulMemberSchemas.Count(static x => string.IsNullOrEmpty(x.Reference?.Id)) == 1 || + return meaningfulMemberSchemas.Count(static x => !string.IsNullOrEmpty(x.GetReferenceId())) == 1 && + (meaningfulMemberSchemas.Count(static x => string.IsNullOrEmpty(x.GetReferenceId())) == 1 || isRootSchemaMeaningful); } - internal static OpenApiSchema? MergeAllOfSchemaEntries(this OpenApiSchema? schema, HashSet? schemasToExclude = default, Func? filter = default) + internal static IOpenApiSchema? MergeAllOfSchemaEntries(this IOpenApiSchema? schema, HashSet? schemasToExclude = default, Func? filter = default) { return schema.MergeIntersectionSchemaEntries(schemasToExclude, true, filter); } - internal static OpenApiSchema? MergeInclusiveUnionSchemaEntries(this OpenApiSchema? schema) + internal static IOpenApiSchema? MergeInclusiveUnionSchemaEntries(this IOpenApiSchema? schema) { if (schema is null || !schema.IsInclusiveUnion(0)) return null; - var result = new OpenApiSchema(schema); + var result = schema.GetSchemaOrTargetShallowCopy(); result.AnyOf.Clear(); result.TryAddProperties(schema.AnyOf.SelectMany(static x => x.Properties)); + schema.AddOriginalReferenceIdExtension(result); return result; } - internal static OpenApiSchema? MergeExclusiveUnionSchemaEntries(this OpenApiSchema? schema) + internal static IOpenApiSchema? MergeExclusiveUnionSchemaEntries(this IOpenApiSchema? schema) { if (schema is null || !schema.IsExclusiveUnion(0)) return null; - var result = new OpenApiSchema(schema); + var result = schema.GetSchemaOrTargetShallowCopy(); result.OneOf.Clear(); result.TryAddProperties(schema.OneOf.SelectMany(static x => x.Properties)); + schema.AddOriginalReferenceIdExtension(result); return result; } - internal static OpenApiSchema? MergeSingleInclusiveUnionInheritanceOrIntersectionSchemaEntries(this OpenApiSchema? schema) + internal static IOpenApiSchema? MergeSingleInclusiveUnionInheritanceOrIntersectionSchemaEntries(this IOpenApiSchema? schema) { if (schema is not null && schema.IsInclusiveUnion(0) - && schema.AnyOf.OnlyOneOrDefault() is OpenApiSchema subSchema + && schema.AnyOf.OnlyOneOrDefault() is IOpenApiSchema subSchema && (subSchema.IsInherited() || subSchema.IsIntersection())) { - var result = new OpenApiSchema(schema); + var result = schema.GetSchemaOrTargetShallowCopy(); result.AnyOf.Clear(); result.TryAddProperties(subSchema.Properties); result.AllOf.AddRange(subSchema.AllOf); + schema.AddOriginalReferenceIdExtension(result); return result; } return null; } - internal static OpenApiSchema? MergeSingleExclusiveUnionInheritanceOrIntersectionSchemaEntries(this OpenApiSchema? schema) + internal static IOpenApiSchema? MergeSingleExclusiveUnionInheritanceOrIntersectionSchemaEntries(this IOpenApiSchema? schema) { if (schema is not null && schema.IsExclusiveUnion(0) - && schema.OneOf.OnlyOneOrDefault() is OpenApiSchema subSchema + && schema.OneOf.OnlyOneOrDefault() is IOpenApiSchema subSchema && (subSchema.IsInherited() || subSchema.IsIntersection())) { - var result = new OpenApiSchema(schema); + var result = schema.GetSchemaOrTargetShallowCopy(); result.OneOf.Clear(); result.TryAddProperties(subSchema.Properties); result.AllOf.AddRange(subSchema.AllOf); + schema.AddOriginalReferenceIdExtension(result); return result; } return null; } - internal static OpenApiSchema? MergeIntersectionSchemaEntries(this OpenApiSchema? schema, HashSet? schemasToExclude = default, bool overrideIntersection = false, Func? filter = default) + private static OpenApiSchema GetSchemaOrTargetShallowCopy(this IOpenApiSchema? schema) + { + return schema switch + { + OpenApiSchema oas => (OpenApiSchema)oas.CreateShallowCopy(), + OpenApiSchemaReference oasr when oasr.Target is OpenApiSchema target => (OpenApiSchema)target.CreateShallowCopy(), + OpenApiSchemaReference => throw new InvalidOperationException("The schema reference is not resolved"), + _ => throw new InvalidOperationException("The schema type is not supported") + }; + } + + internal static IOpenApiSchema? MergeIntersectionSchemaEntries(this IOpenApiSchema? schema, HashSet? schemasToExclude = default, bool overrideIntersection = false, Func? filter = default) { if (schema is null) return null; if (!schema.IsIntersection() && !overrideIntersection) return schema; - var result = new OpenApiSchema(schema); + + var result = schema.GetSchemaOrTargetShallowCopy(); result.AllOf.Clear(); var meaningfulSchemas = schema.AllOf + .Where(x => x is not null && (schemasToExclude is null || !schemasToExclude.Contains(x))) .Where(x => (x.IsSemanticallyMeaningful() || x.AllOf.Any()) && (filter == null || filter(x))) .Select(x => MergeIntersectionSchemaEntries(x, schemasToExclude, overrideIntersection, filter)) - .Where(x => x is not null && (schemasToExclude is null || !schemasToExclude.Contains(x))) - .OfType() + .OfType() .ToArray(); var entriesToMerge = meaningfulSchemas.FlattenEmptyEntries(static x => x.AllOf).Union(meaningfulSchemas).ToArray(); if (entriesToMerge.Select(static x => x.Discriminator).OfType().FirstOrDefault() is OpenApiDiscriminator discriminator) if (result.Discriminator is null) result.Discriminator = discriminator; - else if (string.IsNullOrEmpty(result.Discriminator.PropertyName) && !string.IsNullOrEmpty(discriminator.PropertyName)) - result.Discriminator.PropertyName = discriminator.PropertyName; - else if (discriminator.Mapping?.Any() ?? false) - result.Discriminator.Mapping = discriminator.Mapping.ToDictionary(static x => x.Key, static x => x.Value); + else + { + if (string.IsNullOrEmpty(result.Discriminator.PropertyName) && !string.IsNullOrEmpty(discriminator.PropertyName)) + result.Discriminator.PropertyName = discriminator.PropertyName; + if (discriminator.Mapping?.Any() ?? false) + result.Discriminator.Mapping = discriminator.Mapping.ToDictionary(static x => x.Key, static x => x.Value); + } + + schema.AddOriginalReferenceIdExtension(result); result.TryAddProperties(entriesToMerge.SelectMany(static x => x.Properties)); return result; } - internal static void TryAddProperties(this OpenApiSchema schema, IEnumerable> properties) + /// + /// Adds a temporary extension to the schema to store the original reference id of the schema being merged. + /// This is used to keep track of the original reference id of the schema being merged when the schema is a reference. + /// The reference id is used to generate the class name of the schema. + /// + /// Original schema that was merged. + /// Resulting merged schema. + private static void AddOriginalReferenceIdExtension(this IOpenApiSchema schema, OpenApiSchema result) + { + if (schema is not OpenApiSchemaReference schemaReference) return; + result.Extensions.TryAdd(OpenApiKiotaMergedExtension.Name, new OpenApiKiotaMergedExtension(schemaReference.Reference.Id)); + } + + internal static string? GetMergedSchemaOriginalReferenceId(this IOpenApiSchema schema) + { + return schema.Extensions.TryGetValue(OpenApiKiotaMergedExtension.Name, out var extension) && extension is OpenApiKiotaMergedExtension mergedExtension ? + mergedExtension.OriginalName : + null; + } + + internal static void TryAddProperties(this OpenApiSchema schema, IEnumerable> properties) { foreach (var property in properties) { @@ -179,94 +233,111 @@ internal static void TryAddProperties(this OpenApiSchema schema, IEnumerable x.IsSemanticallyMeaningful()).ToArray(); - return meaningfulSchemas?.Count(static x => !string.IsNullOrEmpty(x.Reference?.Id)) > 1 || meaningfulSchemas?.Count(static x => string.IsNullOrEmpty(x.Reference?.Id)) > 1; + return meaningfulSchemas?.Count(static x => !string.IsNullOrEmpty(x.GetReferenceId())) > 1 || meaningfulSchemas?.Count(static x => string.IsNullOrEmpty(x.GetReferenceId())) > 1; } - public static bool IsExclusiveUnion(this OpenApiSchema? schema, uint exclusiveMinimumNumberOfEntries = 1) + public static bool IsExclusiveUnion(this IOpenApiSchema? schema, uint exclusiveMinimumNumberOfEntries = 1) { - return schema?.OneOf?.Count(static x => IsSemanticallyMeaningful(x, true)) > exclusiveMinimumNumberOfEntries; + if (schema is null) return false; + return schema.OneOf?.Count(static x => IsSemanticallyMeaningful(x, true)) > exclusiveMinimumNumberOfEntries || + schema.IsArrayOfTypes(); // so we don't consider one of object/nullable as a union type } - private static readonly HashSet oDataTypes = new(StringComparer.OrdinalIgnoreCase) { - "number", - "integer", - }; - private static bool IsODataPrimitiveTypeBackwardCompatible(this OpenApiSchema schema) + public static bool IsArrayOfTypes(this IOpenApiSchema? schema) + { + if (schema is null) return false; + return schema.Type.HasValue && !IsPowerOfTwo((uint)(schema.Type.Value & ~JsonSchemaType.Null)); + } + private static bool IsPowerOfTwo(uint x) + { + return (x & (x - 1)) == 0; + } + private static readonly HashSet oDataTypes = [ + JsonSchemaType.Number, + JsonSchemaType.Integer, + JsonSchemaType.Number | JsonSchemaType.Null, + JsonSchemaType.Integer | JsonSchemaType.Null, + ]; + private static readonly Func isODataType = static x => x.Type is not null && oDataTypes.Contains(x.Type.Value); + private static readonly Func isStringType = static x => x is { Type: JsonSchemaType.String or (JsonSchemaType.String | JsonSchemaType.Null) }; + private static bool IsODataPrimitiveTypeBackwardCompatible(this IOpenApiSchema schema) { return schema.IsExclusiveUnion() && schema.OneOf.Count == 3 && schema.OneOf.Count(static x => x.Enum?.Any() ?? false) == 1 && - schema.OneOf.Count(static x => oDataTypes.Contains(x.Type)) == 1 && - schema.OneOf.Count(static x => "string".Equals(x.Type, StringComparison.OrdinalIgnoreCase)) == 1 + schema.OneOf.Count(isODataType) == 1 && + schema.OneOf.Count(isStringType) == 1 || schema.IsInclusiveUnion() && schema.AnyOf.Count == 3 && schema.AnyOf.Count(static x => x.Enum?.Any() ?? false) == 1 && - schema.AnyOf.Count(static x => oDataTypes.Contains(x.Type)) == 1 && - schema.AnyOf.Count(static x => "string".Equals(x.Type, StringComparison.OrdinalIgnoreCase)) == 1; + schema.AnyOf.Count(isODataType) == 1 && + schema.AnyOf.Count(isStringType) == 1; } - public static bool IsODataPrimitiveType(this OpenApiSchema schema) + public static bool IsODataPrimitiveType(this IOpenApiSchema schema) { + if (schema is null) return false; return schema.IsExclusiveUnion() && schema.OneOf.Count == 3 && - schema.OneOf.Count(static x => "string".Equals(x.Type, StringComparison.OrdinalIgnoreCase) && (x.Enum?.Any() ?? false)) == 1 && - schema.OneOf.Count(static x => oDataTypes.Contains(x.Type)) == 1 && - schema.OneOf.Count(static x => "string".Equals(x.Type, StringComparison.OrdinalIgnoreCase)) == 2 + schema.OneOf.Count(static x => isStringType(x) && (x.Enum?.Any() ?? false)) == 1 && + schema.OneOf.Count(isODataType) == 1 && + schema.OneOf.Count(isStringType) == 2 || schema.IsInclusiveUnion() && schema.AnyOf.Count == 3 && - schema.AnyOf.Count(static x => "string".Equals(x.Type, StringComparison.OrdinalIgnoreCase) && (x.Enum?.Any() ?? false)) == 1 && - schema.AnyOf.Count(static x => oDataTypes.Contains(x.Type)) == 1 && - schema.AnyOf.Count(static x => "string".Equals(x.Type, StringComparison.OrdinalIgnoreCase)) == 2 + schema.AnyOf.Count(static x => isStringType(x) && (x.Enum?.Any() ?? false)) == 1 && + schema.AnyOf.Count(isODataType) == 1 && + schema.AnyOf.Count(isStringType) == 2 || schema.IsODataPrimitiveTypeBackwardCompatible(); } - public static bool IsEnum(this OpenApiSchema schema) + public static bool IsEnum(this IOpenApiSchema schema) { if (schema is null) return false; - return schema.Enum.OfType().Any(static x => !string.IsNullOrEmpty(x.Value)) && - (string.IsNullOrEmpty(schema.Type) || "string".Equals(schema.Type, StringComparison.OrdinalIgnoreCase)); // number and boolean enums are not supported + return schema.Enum.Any(static x => x.GetValueKind() is JsonValueKind.String && + x.GetValue() is string value && + !string.IsNullOrEmpty(value)); // number and boolean enums are not supported } - public static bool IsComposedEnum(this OpenApiSchema schema) + public static bool IsComposedEnum(this IOpenApiSchema schema) { if (schema is null) return false; return schema.AnyOf.Count(static x => !x.IsSemanticallyMeaningful(true)) == 1 && schema.AnyOf.Count(static x => x.IsEnum()) == 1 || schema.OneOf.Count(static x => !x.IsSemanticallyMeaningful(true)) == 1 && schema.OneOf.Count(static x => x.IsEnum()) == 1; } - public static bool IsSemanticallyMeaningful(this OpenApiSchema schema, bool ignoreNullableObjects = false, bool ignoreEnums = false, bool ignoreArrays = false, bool ignoreType = false) + public static bool IsSemanticallyMeaningful(this IOpenApiSchema schema, bool ignoreNullableObjects = false, bool ignoreEnums = false, bool ignoreArrays = false, bool ignoreType = false) { if (schema is null) return false; return schema.HasAnyProperty() || (!ignoreEnums && schema.Enum is { Count: > 0 }) || (!ignoreArrays && schema.Items != null) || - (!ignoreType && !string.IsNullOrEmpty(schema.Type) && - ((ignoreNullableObjects && !"object".Equals(schema.Type, StringComparison.OrdinalIgnoreCase)) || + (!ignoreType && schema.Type is not null && + ((ignoreNullableObjects && !schema.IsObjectType()) || !ignoreNullableObjects)) || !string.IsNullOrEmpty(schema.Format) || - !string.IsNullOrEmpty(schema.Reference?.Id); + !string.IsNullOrEmpty(schema.GetReferenceId()); } - public static IEnumerable GetSchemaReferenceIds(this OpenApiSchema schema, HashSet? visitedSchemas = null) + public static IEnumerable GetSchemaReferenceIds(this IOpenApiSchema schema, HashSet? visitedSchemas = null) { visitedSchemas ??= new(); if (schema != null && !visitedSchemas.Contains(schema)) { visitedSchemas.Add(schema); var result = new List(); - if (!string.IsNullOrEmpty(schema.Reference?.Id)) - result.Add(schema.Reference.Id); + if (schema.GetReferenceId() is string refId && !string.IsNullOrEmpty(refId)) + result.Add(refId); if (schema.Items != null) { - if (!string.IsNullOrEmpty(schema.Items.Reference?.Id)) - result.Add(schema.Items.Reference.Id); + if (schema.Items.GetReferenceId() is string itemsRefId && !string.IsNullOrEmpty(itemsRefId)) + result.Add(itemsRefId); result.AddRange(schema.Items.GetSchemaReferenceIds(visitedSchemas)); } - var subSchemaReferences = (schema.Properties?.Values ?? Enumerable.Empty()) - .Union(schema.AnyOf ?? Enumerable.Empty()) - .Union(schema.AllOf ?? Enumerable.Empty()) - .Union(schema.OneOf ?? Enumerable.Empty()) + var subSchemaReferences = (schema.Properties?.Values ?? Enumerable.Empty()) + .Union(schema.AnyOf ?? Enumerable.Empty()) + .Union(schema.AllOf ?? Enumerable.Empty()) + .Union(schema.OneOf ?? Enumerable.Empty()) .SelectMany(x => x.GetSchemaReferenceIds(visitedSchemas)) .ToList();// this to list is important otherwise the any marks the schemas as visited and add range doesn't find anything if (subSchemaReferences.Count != 0) @@ -276,7 +347,7 @@ public static IEnumerable GetSchemaReferenceIds(this OpenApiSchema schem return []; } - private static IEnumerable FlattenEmptyEntries(this IEnumerable schemas, Func> subsequentGetter, int? maxDepth = default) + private static IEnumerable FlattenEmptyEntries(this IEnumerable schemas, Func> subsequentGetter, int? maxDepth = default) { if (schemas == null) return []; ArgumentNullException.ThrowIfNull(subsequentGetter); @@ -285,7 +356,7 @@ private static IEnumerable FlattenEmptyEntries(this IEnumerable>(); + var permutations = new Dictionary>(); foreach (var item in result) { var subsequentItems = subsequentGetter(item); @@ -308,7 +379,7 @@ private static IEnumerable FlattenEmptyEntries(this IEnumerable? visitedSchemas = default) + internal static string GetDiscriminatorPropertyName(this IOpenApiSchema schema, HashSet? visitedSchemas = default) { if (schema == null) @@ -331,10 +402,10 @@ internal static string GetDiscriminatorPropertyName(this OpenApiSchema schema, H return string.Empty; } - internal static IEnumerable> GetDiscriminatorMappings(this OpenApiSchema schema, ConcurrentDictionary> inheritanceIndex) + internal static IEnumerable> GetDiscriminatorMappings(this IOpenApiSchema schema, ConcurrentDictionary> inheritanceIndex) { if (schema == null) - return Enumerable.Empty>(); + return []; if (!(schema.Discriminator?.Mapping?.Any() ?? false)) if (schema.OneOf.Any()) return schema.OneOf.SelectMany(x => GetDiscriminatorMappings(x, inheritanceIndex)); @@ -343,18 +414,18 @@ internal static IEnumerable> GetDiscriminatorMappin else if (schema.AllOf.Any(allOfEvaluatorForMappings) && schema.AllOf[^1].Equals(schema.AllOf.Last(allOfEvaluatorForMappings))) // ensure the matched AllOf entry is the last in the list return GetDiscriminatorMappings(schema.AllOf.Last(allOfEvaluatorForMappings), inheritanceIndex); - else if (!string.IsNullOrEmpty(schema.Reference?.Id)) - return GetAllInheritanceSchemaReferences(schema.Reference.Id, inheritanceIndex) + else if (schema.GetReferenceId() is string refId && !string.IsNullOrEmpty(refId)) + return GetAllInheritanceSchemaReferences(refId, inheritanceIndex) .Where(static x => !string.IsNullOrEmpty(x)) .Select(x => KeyValuePair.Create(x, x)) - .Union(new[] { KeyValuePair.Create(schema.Reference.Id, schema.Reference.Id) }); + .Union([KeyValuePair.Create(refId, refId)]); else - return Enumerable.Empty>(); + return []; return schema.Discriminator .Mapping; } - private static readonly Func allOfEvaluatorForMappings = static x => x.Discriminator?.Mapping.Any() ?? false; + private static readonly Func allOfEvaluatorForMappings = static x => x.Discriminator?.Mapping.Any() ?? false; private static IEnumerable GetAllInheritanceSchemaReferences(string currentReferenceId, ConcurrentDictionary> inheritanceIndex) { ArgumentException.ThrowIfNullOrEmpty(currentReferenceId); @@ -363,5 +434,11 @@ private static IEnumerable GetAllInheritanceSchemaReferences(string curr return dependents.Keys.Union(dependents.Keys.SelectMany(x => GetAllInheritanceSchemaReferences(x, inheritanceIndex))).Distinct(StringComparer.OrdinalIgnoreCase); return []; } + internal static string GetClassName(this IOpenApiSchema? schema) + { + if (schema?.GetReferenceId() is string referenceId && !string.IsNullOrEmpty(referenceId)) + return referenceId[(referenceId.LastIndexOf('.') + 1)..]; + return string.Empty; + } } diff --git a/src/Kiota.Builder/Extensions/OpenApiSettingsExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiSettingsExtensions.cs index 118ac47698..6546b601cb 100644 --- a/src/Kiota.Builder/Extensions/OpenApiSettingsExtensions.cs +++ b/src/Kiota.Builder/Extensions/OpenApiSettingsExtensions.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using Kiota.Builder.OpenApiExtensions; -using Microsoft.OpenApi.Readers; +using Microsoft.OpenApi.Reader; namespace Kiota.Builder.Extensions; public static class OpenApiSettingsExtensions diff --git a/src/Kiota.Builder/Extensions/OpenApiUrlTreeNodeExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiUrlTreeNodeExtensions.cs index a49b82968a..a7869fdf05 100644 --- a/src/Kiota.Builder/Extensions/OpenApiUrlTreeNodeExtensions.cs +++ b/src/Kiota.Builder/Extensions/OpenApiUrlTreeNodeExtensions.cs @@ -6,6 +6,8 @@ using Microsoft.Extensions.Logging; using Microsoft.OpenApi.MicrosoftExtensions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Services; namespace Kiota.Builder.Extensions; @@ -66,14 +68,14 @@ internal static string CleanupParametersFromPath(string pathSegment) .Replace(RequestParametersSectionEndChar, string.Empty, StringComparison.OrdinalIgnoreCase) .Replace(RequestParametersSectionChar, string.Empty, StringComparison.OrdinalIgnoreCase); } - private static IEnumerable GetParametersForPathItem(OpenApiPathItem pathItem, string nodeSegment) + private static IEnumerable GetParametersForPathItem(IOpenApiPathItem pathItem, string nodeSegment) { return pathItem.Parameters - .Union(pathItem.Operations.SelectMany(static x => x.Value.Parameters)) + .Union(pathItem.Operations.SelectMany(static x => x.Value.Parameters ?? Enumerable.Empty())) .Where(static x => x.In == ParameterLocation.Path) .Where(x => nodeSegment.Contains($"{{{x.Name}}}", StringComparison.OrdinalIgnoreCase)); } - public static IEnumerable GetPathParametersForCurrentSegment(this OpenApiUrlTreeNode node) + public static IEnumerable GetPathParametersForCurrentSegment(this OpenApiUrlTreeNode node) { if (node != null && node.IsComplexPathMultipleParameters()) @@ -92,7 +94,7 @@ public static IEnumerable GetPathParametersForCurrentSegment(t /// /// Returns the class name for the node with more or less precision depending on the provided arguments /// - internal static string GetClassName(this OpenApiUrlTreeNode currentNode, StructuredMimeTypesCollection structuredMimeTypes, string? suffix = default, string? prefix = default, OpenApiOperation? operation = default, OpenApiResponse? response = default, OpenApiSchema? schema = default, bool requestBody = false) + internal static string GetClassName(this OpenApiUrlTreeNode currentNode, StructuredMimeTypesCollection structuredMimeTypes, string? suffix = default, string? prefix = default, OpenApiOperation? operation = default, IOpenApiResponse? response = default, IOpenApiSchema? schema = default, bool requestBody = false) { ArgumentNullException.ThrowIfNull(currentNode); return currentNode.GetSegmentName(structuredMimeTypes, suffix, prefix, operation, response, schema, requestBody, static x => x.LastOrDefault() ?? string.Empty); @@ -106,14 +108,14 @@ internal static string GetNavigationPropertyName(this OpenApiUrlTreeNode current return result; } private static readonly HashSet httpVerbs = new(8, StringComparer.OrdinalIgnoreCase) { "get", "post", "put", "patch", "delete", "head", "options", "trace" }; - private static string GetSegmentName(this OpenApiUrlTreeNode currentNode, StructuredMimeTypesCollection structuredMimeTypes, string? suffix, string? prefix, OpenApiOperation? operation, OpenApiResponse? response, OpenApiSchema? schema, bool requestBody, Func, string> segmentsReducer, bool skipExtension = true) + private static string GetSegmentName(this OpenApiUrlTreeNode currentNode, StructuredMimeTypesCollection structuredMimeTypes, string? suffix, string? prefix, OpenApiOperation? operation, IOpenApiResponse? response, IOpenApiSchema? schema, bool requestBody, Func, string> segmentsReducer, bool skipExtension = true) { - var referenceName = schema?.Reference?.GetClassName(); + var referenceName = schema?.GetClassName(); var rawClassName = referenceName is not null && !string.IsNullOrEmpty(referenceName) ? referenceName : - ((requestBody ? null : response?.GetResponseSchema(structuredMimeTypes)?.Reference?.GetClassName()) is string responseClassName && !string.IsNullOrEmpty(responseClassName) ? + ((requestBody ? null : response?.GetResponseSchema(structuredMimeTypes)?.GetClassName()) is string responseClassName && !string.IsNullOrEmpty(responseClassName) ? responseClassName : - ((requestBody ? operation?.GetRequestSchema(structuredMimeTypes) : operation?.GetResponseSchema(structuredMimeTypes))?.Reference?.GetClassName() is string requestClassName && !string.IsNullOrEmpty(requestClassName) ? + ((requestBody ? operation?.GetRequestSchema(structuredMimeTypes) : operation?.GetResponseSchema(structuredMimeTypes))?.GetClassName() is string requestClassName && !string.IsNullOrEmpty(requestClassName) ? requestClassName : CleanupParametersFromPath(currentNode.DeduplicatedSegment())?.ReplaceValueIdentifier())); if (!string.IsNullOrEmpty(rawClassName) && string.IsNullOrEmpty(referenceName)) @@ -193,7 +195,7 @@ public static bool HasRequiredQueryParametersAcrossOperations(this OpenApiUrlTre if (!currentNode.PathItems.TryGetValue(Constants.DefaultOpenApiLabel, out var pathItem)) return false; - var operationQueryParameters = pathItem.Operations.SelectMany(static x => x.Value.Parameters); + var operationQueryParameters = pathItem.Operations.SelectMany(static x => x.Value.Parameters ?? Enumerable.Empty()); return operationQueryParameters.Union(pathItem.Parameters).Where(static x => x.In == ParameterLocation.Query) .Any(static x => x.Required); } @@ -207,9 +209,9 @@ public static string GetUrlTemplate(this OpenApiUrlTreeNode currentNode, Operati var pathItem = currentNode.PathItems[Constants.DefaultOpenApiLabel]; var operationQueryParameters = (operationType, pathItem.Operations.Any()) switch { - (OperationType ot, _) when pathItem.Operations.TryGetValue(ot, out var operation) => operation.Parameters, - (null, true) => pathItem.Operations.SelectMany(static x => x.Value.Parameters).Where(static x => x.In == ParameterLocation.Query), - _ => Enumerable.Empty(), + (OperationType ot, _) when pathItem.Operations.TryGetValue(ot, out var operation) && operation.Parameters is not null => operation.Parameters, + (null, true) => pathItem.Operations.SelectMany(static x => x.Value.Parameters ?? Enumerable.Empty()).Where(static x => x.In == ParameterLocation.Query), + _ => [], }; var parameters = pathItem.Parameters .Union(operationQueryParameters) @@ -234,7 +236,7 @@ public static string GetUrlTemplate(this OpenApiUrlTreeNode currentNode, Operati } var pathReservedPathParametersIds = currentNode.PathItems.TryGetValue(Constants.DefaultOpenApiLabel, out var pItem) ? pItem.Parameters - .Union(pItem.Operations.SelectMany(static x => x.Value.Parameters)) + .Union(pItem.Operations.SelectMany(static x => x.Value.Parameters ?? Enumerable.Empty())) .Where(static x => x.In == ParameterLocation.Path && x.Extensions.TryGetValue(OpenApiReservedParameterExtension.Name, out var ext) && ext is OpenApiReservedParameterExtension reserved && reserved.IsReserved.HasValue && reserved.IsReserved.Value) .Select(static x => x.Name) .ToHashSet(StringComparer.OrdinalIgnoreCase) : @@ -356,10 +358,20 @@ private static void ReplaceParameterInPathForAllChildNodes(OpenApiUrlTreeNode no if (node.PathItems.TryGetValue(Constants.DefaultOpenApiLabel, out var pathItem)) { foreach (var pathParameter in pathItem.Parameters - .Union(pathItem.Operations.SelectMany(static x => x.Value.Parameters)) + .Union(pathItem.Operations.SelectMany(static x => x.Value.Parameters ?? Enumerable.Empty())) .Where(x => x.In == ParameterLocation.Path && oldName.Equals(x.Name, StringComparison.Ordinal))) { - pathParameter.Name = newParameterName; + switch (pathParameter) + { + case OpenApiParameter openApiParameter: + openApiParameter.Name = newParameterName; + break; + case OpenApiParameterReference openApiReference: + openApiReference.Target.Name = newParameterName; + break; + default: + throw new InvalidOperationException("Unexpected parameter type"); + } } } } @@ -386,11 +398,21 @@ private static void CopyNodeIntoOtherNode(OpenApiUrlTreeNode source, OpenApiUrlT pathItem .Value .Operations - .SelectMany(static x => x.Value.Parameters) + .SelectMany(static x => x.Value.Parameters ?? Enumerable.Empty()) .Where(x => x.In == ParameterLocation.Path && pathParameterNameToReplace.Equals(x.Name, StringComparison.Ordinal)) )) { - pathParameter.Name = pathParameterNameReplacement; + switch (pathParameter) + { + case OpenApiParameter openApiParameter: + openApiParameter.Name = pathParameterNameReplacement; + break; + case OpenApiParameterReference openApiReference: + openApiReference.Target.Name = pathParameterNameReplacement; + break; + default: + throw new InvalidOperationException("Unexpected parameter type"); + } } if (source != destination && !destination.PathItems.TryAdd(pathItem.Key, pathItem.Value)) { diff --git a/src/Kiota.Builder/Kiota.Builder.csproj b/src/Kiota.Builder/Kiota.Builder.csproj index 7785059d47..c2ced0a263 100644 --- a/src/Kiota.Builder/Kiota.Builder.csproj +++ b/src/Kiota.Builder/Kiota.Builder.csproj @@ -1,60 +1,60 @@ - - - Latest - enable - true - net8.0;net9.0; - true - http://go.microsoft.com/fwlink/?LinkID=288890 - https://github.com/microsoft/kiota - MIT - true - README.md - Microsoft - Microsoft - Microsoft.OpenApi.Kiota.Builder - Microsoft.OpenApi.Kiota.Builder - ./nupkg - 1.24.0 - $(VersionSuffix) - - https://github.com/microsoft/kiota/releases - - true - https://github.com/microsoft/kiota - The core engine behind the OpenAPI based client generator. - © Microsoft Corporation. All rights reserved. - OpenAPI .NET CSharp TypeScript Java Go PHP Python REST API - true - ..\Microsoft.OpenApi.snk - true - All - - - - $(NoWarn);CS8785;NU5048;NU5104;CA1724;CA1055;CA1848;CA1308;CA1822 - - - - - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - - - - + + + Latest + enable + true + net8.0;net9.0; + true + http://go.microsoft.com/fwlink/?LinkID=288890 + https://github.com/microsoft/kiota + MIT + true + README.md + Microsoft + Microsoft + Microsoft.OpenApi.Kiota.Builder + Microsoft.OpenApi.Kiota.Builder + ./nupkg + 1.24.0 + $(VersionSuffix) + + https://github.com/microsoft/kiota/releases + + true + https://github.com/microsoft/kiota + The core engine behind the OpenAPI based client generator. + © Microsoft Corporation. All rights reserved. + OpenAPI .NET CSharp TypeScript Java Go PHP Python REST API + true + ..\Microsoft.OpenApi.snk + true + All + + + + $(NoWarn);CS8785;NU5048;NU5104;CA1724;CA1055;CA1848;CA1308;CA1822 + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index 8025360676..51f8a6663f 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -9,6 +9,8 @@ using System.Net.Http; using System.Runtime.CompilerServices; using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -32,8 +34,11 @@ using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.ApiManifest; +using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.MicrosoftExtensions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Services; using HttpMethod = Kiota.Builder.CodeDOM.HttpMethod; [assembly: InternalsVisibleTo("Kiota.Builder.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100957cb48387b2a5f54f5ce39255f18f26d32a39990db27cf48737afc6bc62759ba996b8a2bfb675d4e39f3d06ecb55a178b1b4031dcb2a767e29977d88cce864a0d16bfc1b3bebb0edf9fe285f10fffc0a85f93d664fa05af07faa3aad2e545182dbf787e3fd32b56aca95df1a3c4e75dec164a3f1a4c653d971b01ffc39eb3c4")] @@ -189,7 +194,8 @@ private static string NormalizeApiManifestPath(RequestInfo request, string? base // Should Generate sw.Start(); - shouldGenerate &= await workspaceManagementService.ShouldGenerateAsync(config, openApiDocument.HashCode, cancellationToken).ConfigureAwait(false); + var hashCode = await openApiDocument.GetHashCodeAsync(cancellationToken).ConfigureAwait(false); + shouldGenerate &= await workspaceManagementService.ShouldGenerateAsync(config, hashCode, cancellationToken).ConfigureAwait(false); StopLogAndReset(sw, $"step {++stepId} - checking whether the output should be updated - took"); if (shouldGenerate && generating) @@ -227,7 +233,7 @@ private void UpdateConfigurationFromOpenApiDocument() } private LanguagesInformation? GetLanguagesInformationInternal() { - if (openApiDocument == null) + if (openApiDocument is null || openApiDocument.Extensions is null) return null; if (openApiDocument.Extensions.TryGetValue(OpenApiKiotaExtension.Name, out var ext) && ext is OpenApiKiotaExtension kiotaExt) return kiotaExt.LanguagesInformation; @@ -349,7 +355,12 @@ private async Task FinalizeWorkspaceAsync(Stopwatch sw, int stepId, OpenApiUrlTr // Write lock file sw.Start(); using var descriptionStream = !isDescriptionFromWorkspaceCopy ? await LoadStreamAsync(inputPath, cancellationToken).ConfigureAwait(false) : Stream.Null; - await workspaceManagementService.UpdateStateFromConfigurationAsync(config, openApiDocument?.HashCode ?? string.Empty, openApiTree?.GetRequestInfo().ToDictionary(static x => x.Key, static x => x.Value) ?? [], descriptionStream, cancellationToken).ConfigureAwait(false); + var hashCode = openApiDocument switch + { + null => string.Empty, + _ => await openApiDocument.GetHashCodeAsync(cancellationToken).ConfigureAwait(false), + }; + await workspaceManagementService.UpdateStateFromConfigurationAsync(config, hashCode, openApiTree?.GetRequestInfo().ToDictionary(static x => x.Key, static x => x.Value) ?? [], descriptionStream, cancellationToken).ConfigureAwait(false); StopLogAndReset(sw, $"step {++stepId} - writing lock file - took"); } private readonly WorkspaceManagementService workspaceManagementService; @@ -545,18 +556,18 @@ public CodeNamespace CreateSourceModel(OpenApiUrlTreeNode? root) var codeNamespace = rootNamespace.AddNamespace(config.ClientNamespaceName); modelsNamespace = rootNamespace.AddNamespace(config.ModelsNamespaceName); InitializeInheritanceIndex(); - StopLogAndReset(stopwatch, $"{nameof(InitializeInheritanceIndex)}"); + StopLogAndReset(stopwatch, nameof(InitializeInheritanceIndex)); if (root != null) { CreateRequestBuilderClass(codeNamespace, root, root); - StopLogAndReset(stopwatch, $"{nameof(CreateRequestBuilderClass)}"); + StopLogAndReset(stopwatch, nameof(CreateRequestBuilderClass)); stopwatch.Start(); MapTypeDefinitions(codeNamespace); - StopLogAndReset(stopwatch, $"{nameof(MapTypeDefinitions)}"); + StopLogAndReset(stopwatch, nameof(MapTypeDefinitions)); TrimInheritedModels(); - StopLogAndReset(stopwatch, $"{nameof(TrimInheritedModels)}"); + StopLogAndReset(stopwatch, nameof(TrimInheritedModels)); CleanUpInternalState(); - StopLogAndReset(stopwatch, $"{nameof(CleanUpInternalState)}"); + StopLogAndReset(stopwatch, nameof(CleanUpInternalState)); logger.LogTrace("{Timestamp}ms: Created source model with {Count} classes", stopwatch.ElapsedMilliseconds, codeNamespace.GetChildElements(true).Count()); } @@ -572,7 +583,7 @@ private void AddOperationSecurityRequirementToDOM(OpenApiOperation operation, Co return; } - if (operation.Security == null || !operation.Security.Any()) + if (operation.Security == null || !operation.Security.Any() || openApiDocument.Components?.SecuritySchemes is null) return; var securitySchemes = openApiDocument.Components.SecuritySchemes; @@ -588,15 +599,16 @@ private void AddOperationSecurityRequirementToDOM(OpenApiOperation operation, Co } } - private void AddSecurity(CodeClass codeClass, OpenApiSecurityScheme openApiSecurityScheme) + private void AddSecurity(CodeClass codeClass, IOpenApiSecurityScheme openApiSecurityScheme) { - codeClass.AddProperty( - new CodeProperty - { - Type = new CodeType { Name = openApiSecurityScheme.Type.ToString(), IsExternal = true }, - Kind = CodePropertyKind.Headers - } - ); + if (openApiSecurityScheme.Type is not null) + codeClass.AddProperty( + new CodeProperty + { + Type = new CodeType { Name = openApiSecurityScheme.Type.Value.ToString(), IsExternal = true }, + Kind = CodePropertyKind.Headers + } + ); } /// @@ -1063,7 +1075,7 @@ private CodeParameter GetIndexerParameter(OpenApiUrlTreeNode currentNode, OpenAp var pathItems = GetPathItems(currentNode); var parameter = pathItems.TryGetValue(Constants.DefaultOpenApiLabel, out var pathItem) ? pathItem.Parameters .Select(static x => new { Parameter = x, IsPathParameter = true }) - .Union(pathItems[Constants.DefaultOpenApiLabel].Operations.SelectMany(static x => x.Value.Parameters).Select(static x => new { Parameter = x, IsPathParameter = false })) + .Union(pathItems[Constants.DefaultOpenApiLabel].Operations.SelectMany(static x => x.Value.Parameters ?? []).Select(static x => new { Parameter = x, IsPathParameter = false })) .OrderBy(static x => x.IsPathParameter) .Select(static x => x.Parameter) .FirstOrDefault(x => x.Name.Equals(parameterName, StringComparison.OrdinalIgnoreCase) && x.In == ParameterLocation.Path) : @@ -1087,7 +1099,7 @@ private CodeParameter GetIndexerParameter(OpenApiUrlTreeNode currentNode, OpenAp }; return result; } - private static IDictionary GetPathItems(OpenApiUrlTreeNode currentNode, bool validateIsParameterNode = true) + private static IDictionary GetPathItems(OpenApiUrlTreeNode currentNode, bool validateIsParameterNode = true) { if ((!validateIsParameterNode || currentNode.IsParameter) && currentNode.PathItems.Any()) { @@ -1102,7 +1114,7 @@ private static IDictionary GetPathItems(OpenApiUrlTreeN .ToDictionary(static x => x.Key, static x => x.Value, StringComparer.Ordinal); } - return ImmutableDictionary.Empty; + return ImmutableDictionary.Empty; } private CodeIndexer[] CreateIndexer(string childIdentifier, string childType, CodeParameter parameter, OpenApiUrlTreeNode currentNode, OpenApiUrlTreeNode parentNode) { @@ -1130,16 +1142,18 @@ private CodeIndexer[] CreateIndexer(string childIdentifier, string childType, Co result.Add(backCompatibleValue); } - return result.ToArray(); + return [.. result]; } private static readonly StructuralPropertiesReservedNameProvider structuralPropertiesReservedNameProvider = new(); - private CodeProperty? CreateProperty(string childIdentifier, string childType, OpenApiSchema? propertySchema = null, CodeTypeBase? existingType = null, CodePropertyKind kind = CodePropertyKind.Custom) + private CodeProperty? CreateProperty(string childIdentifier, string childType, IOpenApiSchema? propertySchema = null, CodeTypeBase? existingType = null, CodePropertyKind kind = CodePropertyKind.Custom) { var propertyName = childIdentifier.CleanupSymbolName(); if (structuralPropertiesReservedNameProvider.ReservedNames.Contains(propertyName)) propertyName += "Property"; var resultType = existingType ?? GetPrimitiveType(propertySchema, childType); + if ((propertySchema?.Items?.IsEnum() ?? false) && resultType is CodeType codeType) + codeType.Name = childType; if (resultType == null) return null; var prop = new CodeProperty { @@ -1164,9 +1178,11 @@ openApiExtension is OpenApiPrimaryErrorMessageExtension primaryErrorMessageExten !propertyName.Equals(childIdentifier, StringComparison.Ordinal)) prop.SerializationName = childIdentifier; if (kind == CodePropertyKind.Custom && - propertySchema?.Default is OpenApiString stringDefaultValue && - !string.IsNullOrEmpty(stringDefaultValue.Value)) - prop.DefaultValue = $"\"{stringDefaultValue.Value}\""; + propertySchema?.Default is JsonValue stringDefaultJsonValue && + stringDefaultJsonValue.TryGetValue(out var stringDefaultValue) && + !string.IsNullOrEmpty(stringDefaultValue) && + !"null".Equals(stringDefaultValue, StringComparison.OrdinalIgnoreCase)) + prop.DefaultValue = $"\"{stringDefaultValue}\""; if (existingType == null) { @@ -1175,58 +1191,42 @@ openApiExtension is OpenApiPrimaryErrorMessageExtension primaryErrorMessageExten } return prop; } - private static readonly HashSet typeNamesToSkip = new(StringComparer.OrdinalIgnoreCase) { "object", "array" }; - private static CodeType? GetPrimitiveType(OpenApiSchema? typeSchema, string? childType = default) + private static readonly HashSet typeNamesToSkip = [JsonSchemaType.Object, JsonSchemaType.Array, JsonSchemaType.Object | JsonSchemaType.Null, JsonSchemaType.Array | JsonSchemaType.Null]; + private static CodeType? GetPrimitiveType(IOpenApiSchema? typeSchema, string? childType = default) { - var typeNames = new List { typeSchema?.Items?.Type, childType, typeSchema?.Type }; + if (typeSchema?.Items?.IsEnum() ?? false) + return null; + var typeNames = new List { typeSchema?.Items?.Type, typeSchema?.Type }; if (typeSchema?.AnyOf?.Any() ?? false) typeNames.AddRange(typeSchema.AnyOf.Select(x => x.Type)); // double is sometimes an anyof string, number and enum if (typeSchema?.OneOf?.Any() ?? false) typeNames.AddRange(typeSchema.OneOf.Select(x => x.Type)); // double is sometimes an oneof string, number and enum // first value that's not null, and not "object" for primitive collections, the items type matters - var typeName = typeNames.Find(static x => !string.IsNullOrEmpty(x) && !typeNamesToSkip.Contains(x)); - - var isExternal = false; - if (typeSchema?.Items?.IsEnum() ?? false) - typeName = childType; - else - { - var format = typeSchema?.Format ?? typeSchema?.Items?.Format; - var primitiveTypeName = (typeName?.ToLowerInvariant(), format?.ToLowerInvariant()) switch - { - ("string", "base64url") => "base64url", - ("file", _) => "binary", - ("string", "duration") => "TimeSpan", - ("string", "time") => "TimeOnly", - ("string", "date") => "DateOnly", - ("string", "date-time") => "DateTimeOffset", - ("string", "uuid") => "Guid", - ("string", _) => "string", // covers commonmark and html - ("number", "double" or "float" or "decimal") => format.ToLowerInvariant(), - ("number" or "integer", "int8") => "sbyte", - ("number" or "integer", "uint8") => "byte", - ("number" or "integer", "int64") => "int64", - ("number", "int16") => "integer", - ("number", "int32") => "integer", - ("number", _) => "double", - ("integer", _) => "integer", - ("boolean", _) => "boolean", - (_, "byte") => "base64", - (_, "binary") => "binary", - (_, _) => string.Empty, - }; - if (!string.IsNullOrEmpty(primitiveTypeName)) - { - typeName = primitiveTypeName; - isExternal = true; - } - } - if (string.IsNullOrEmpty(typeName)) - return null; - return new CodeType - { - Name = typeName, - IsExternal = isExternal, + var typeName = typeNames.Find(static x => x is not null && !typeNamesToSkip.Contains(x.Value)); + + var format = typeSchema?.Format ?? typeSchema?.Items?.Format; + return (typeName & ~JsonSchemaType.Null, format?.ToLowerInvariant()) switch + { + (_, "byte") => new CodeType { Name = "base64", IsExternal = true }, + (_, "binary") => new CodeType { Name = "binary", IsExternal = true }, + (JsonSchemaType.String, "base64url") => new CodeType { Name = "base64url", IsExternal = true }, + (JsonSchemaType.String, "duration") => new CodeType { Name = "TimeSpan", IsExternal = true }, + (JsonSchemaType.String, "time") => new CodeType { Name = "TimeOnly", IsExternal = true }, + (JsonSchemaType.String, "date") => new CodeType { Name = "DateOnly", IsExternal = true }, + (JsonSchemaType.String, "date-time") => new CodeType { Name = "DateTimeOffset", IsExternal = true }, + (JsonSchemaType.String, "uuid") => new CodeType { Name = "Guid", IsExternal = true }, + (JsonSchemaType.String, _) => new CodeType { Name = "string", IsExternal = true }, // covers commonmark and html + (JsonSchemaType.Number, "double" or "float" or "decimal") => new CodeType { Name = format.ToLowerInvariant(), IsExternal = true }, + (JsonSchemaType.Number or JsonSchemaType.Integer, "int8") => new CodeType { Name = "sbyte", IsExternal = true }, + (JsonSchemaType.Number or JsonSchemaType.Integer, "uint8") => new CodeType { Name = "byte", IsExternal = true }, + (JsonSchemaType.Number or JsonSchemaType.Integer, "int64") => new CodeType { Name = "int64", IsExternal = true }, + (JsonSchemaType.Number, "int16") => new CodeType { Name = "integer", IsExternal = true }, + (JsonSchemaType.Number, "int32") => new CodeType { Name = "integer", IsExternal = true }, + (JsonSchemaType.Number, _) => new CodeType { Name = "double", IsExternal = true }, + (JsonSchemaType.Integer, _) => new CodeType { Name = "integer", IsExternal = true }, + (JsonSchemaType.Boolean, _) => new CodeType { Name = "boolean", IsExternal = true }, + (_, _) when !string.IsNullOrEmpty(childType) => new CodeType { Name = childType, IsExternal = false, }, + (_, _) => null, }; } private const string RequestBodyPlainTextContentType = "text/plain"; @@ -1237,6 +1237,7 @@ openApiExtension is OpenApiPrimaryErrorMessageExtension primaryErrorMessageExten .Concat([CodeMethod.ErrorMappingClientRange, CodeMethod.ErrorMappingServerRange]), StringComparer.OrdinalIgnoreCase); private void AddErrorMappingsForExecutorMethod(OpenApiUrlTreeNode currentNode, OpenApiOperation operation, CodeMethod executorMethod) { + if (operation.Responses is null) return; foreach (var response in operation.Responses.Where(x => errorStatusCodes.Contains(x.Key))) { if (response.Value.GetResponseSchema(config.StructuredMimeTypes) is { } schema) @@ -1252,11 +1253,11 @@ private void AddErrorMappingsForExecutorMethod(OpenApiUrlTreeNode currentNode, O AddErrorMappingToExecutorMethod(currentNode, operation, executorMethod, errorSchema, defaultResponse, CodeMethod.ErrorMappingServerRange); } } - private void AddErrorMappingToExecutorMethod(OpenApiUrlTreeNode currentNode, OpenApiOperation operation, CodeMethod executorMethod, OpenApiSchema errorSchema, OpenApiResponse response, string errorCode) + private void AddErrorMappingToExecutorMethod(OpenApiUrlTreeNode currentNode, OpenApiOperation operation, CodeMethod executorMethod, IOpenApiSchema errorSchema, IOpenApiResponse response, string errorCode) { if (modelsNamespace != null) { - var parentElement = string.IsNullOrEmpty(response.Reference?.Id) && string.IsNullOrEmpty(errorSchema.Reference?.Id) + var parentElement = response is not OpenApiResponseReference && errorSchema is not OpenApiSchemaReference ? (CodeElement)executorMethod : modelsNamespace; var errorType = CreateModelDeclarations(currentNode, errorSchema, operation, parentElement, $"{errorCode}Error", response: response); @@ -1271,7 +1272,7 @@ private void AddErrorMappingToExecutorMethod(OpenApiUrlTreeNode currentNode, Ope logger.LogWarning("Could not create error type for {Error} in {Operation}", errorCode, operation.OperationId); } } - private (CodeTypeBase?, CodeTypeBase?) GetExecutorMethodReturnType(OpenApiUrlTreeNode currentNode, OpenApiSchema? schema, OpenApiOperation operation, CodeClass parentClass, OperationType operationType) + private (CodeTypeBase?, CodeTypeBase?) GetExecutorMethodReturnType(OpenApiUrlTreeNode currentNode, IOpenApiSchema? schema, OpenApiOperation operation, CodeClass parentClass, OperationType operationType) { if (schema != null) { @@ -1333,11 +1334,11 @@ private void AddErrorMappingToExecutorMethod(OpenApiUrlTreeNode currentNode, Ope private static CodeType GetExecutorMethodDefaultReturnType(OpenApiOperation operation) { string returnType; - if (operation.Responses.Any(static x => x.Value.Content.ContainsKey(RequestBodyOctetStreamContentType) && redirectStatusCodes.Contains(x.Key))) + if (operation.Responses?.Any(static x => x.Value.Content.ContainsKey(RequestBodyOctetStreamContentType) && redirectStatusCodes.Contains(x.Key)) is true) returnType = "binary"; - else if (operation.Responses.Any(static x => noContentStatusCodes.Contains(x.Key))) + else if (operation.Responses?.Any(static x => noContentStatusCodes.Contains(x.Key)) is true) returnType = VoidType; - else if (operation.Responses.Any(static x => x.Value.Content.ContainsKey(RequestBodyPlainTextContentType))) + else if (operation.Responses?.Any(static x => x.Value.Content.ContainsKey(RequestBodyPlainTextContentType)) is true) returnType = "string"; else returnType = "binary"; @@ -1381,7 +1382,7 @@ private void CreateOperationMethods(OpenApiUrlTreeNode currentNode, OperationTyp Deprecation = deprecationInformation, }; - if (operation.Extensions.TryGetValue(OpenApiPagingExtension.Name, out var extension) && extension is OpenApiPagingExtension pagingExtension) + if (operation.Extensions is not null && operation.Extensions.TryGetValue(OpenApiPagingExtension.Name, out var extension) && extension is OpenApiPagingExtension pagingExtension) { executorMethod.PagingInformation = new PagingInformation { @@ -1440,19 +1441,20 @@ private void CreateOperationMethods(OpenApiUrlTreeNode currentNode, OperationTyp && currentNode.HasRequiredQueryParametersAcrossOperations())// no need to generate extra strings/templates as optional parameters will have no effect on resolved url. generatorMethod.UrlTemplateOverride = operationUrlTemplate; - var mediaTypes = schema switch + var mediaTypes = (schema, operation.Responses is null) switch { - null => operation.Responses + (_, true) => [], + (null, _) => operation.Responses! .Where(static x => !errorStatusCodes.Contains(x.Key)) .SelectMany(static x => x.Value.Content) .Select(static x => x.Key) //get the successful non structured media types first, with a default 1 priority .Union(config.StructuredMimeTypes.GetAcceptedTypes( - operation.Responses + operation.Responses! .Where(static x => errorStatusCodes.Contains(x.Key)) // get any structured error ones, with the priority from the configuration .SelectMany(static x => x.Value.Content) // we can safely ignore unstructured ones as they won't be used in error mappings anyway and the body won't be read .Select(static x => x.Key))) .Distinct(StringComparer.OrdinalIgnoreCase), - _ => config.StructuredMimeTypes.GetAcceptedTypes(operation.Responses.Values.SelectMany(static x => x.Content).Where(x => schemaReferenceComparer.Equals(schema, x.Value.Schema)).Select(static x => x.Key)), + (_, false) => config.StructuredMimeTypes.GetAcceptedTypes(operation.Responses!.Values.SelectMany(static x => x.Content).Where(x => schemaReferenceComparer.Equals(schema, x.Value.Schema)).Select(static x => x.Key)), }; generatorMethod.AddAcceptedResponsesTypes(mediaTypes); if (config.Language == GenerationLanguage.CLI) @@ -1467,7 +1469,7 @@ private void CreateOperationMethods(OpenApiUrlTreeNode currentNode, OperationTyp } } private static readonly OpenApiSchemaReferenceComparer schemaReferenceComparer = new(); - private static readonly Func GetCodeParameterFromApiParameter = x => + private static readonly Func GetCodeParameterFromApiParameter = x => { var codeName = x.Name.SanitizeParameterNameForCodeSymbols(); return new CodeParameter @@ -1489,7 +1491,7 @@ private void CreateOperationMethods(OpenApiUrlTreeNode currentNode, OperationTyp Optional = !x.Required }; }; - private static readonly Func ParametersFilter = x => x.In == ParameterLocation.Path || x.In == ParameterLocation.Query || x.In == ParameterLocation.Header; + private static readonly Func ParametersFilter = x => x.In == ParameterLocation.Path || x.In == ParameterLocation.Query || x.In == ParameterLocation.Header; private static void SetPathAndQueryParameters(CodeMethod target, OpenApiUrlTreeNode currentNode, OpenApiOperation operation) { var pathAndQueryParameters = currentNode @@ -1499,8 +1501,9 @@ private static void SetPathAndQueryParameters(CodeMethod target, OpenApiUrlTreeN .Select(GetCodeParameterFromApiParameter) .Union(operation .Parameters - .Where(ParametersFilter) - .Select(GetCodeParameterFromApiParameter)) + ?.Where(ParametersFilter) + .Select(GetCodeParameterFromApiParameter) ?? + []) .ToArray(); target.AddPathQueryOrHeaderParameter(pathAndQueryParameters); } @@ -1544,7 +1547,7 @@ private static void AddRequestConfigurationProperties(CodeClass? parameterClass, private readonly ConcurrentDictionary multipartPropertiesModels = new(); - private static bool IsSupportedMultipartDefault(OpenApiSchema openApiSchema, + private static bool IsSupportedMultipartDefault(IOpenApiSchema openApiSchema, StructuredMimeTypesCollection structuredMimeTypes) { // https://spec.openapis.org/oas/v3.0.3.html#special-considerations-for-multipart-content @@ -1562,14 +1565,15 @@ private static bool IsSupportedMultipartDefault(OpenApiSchema openApiSchema, private void AddRequestBuilderMethodParameters(OpenApiUrlTreeNode currentNode, OperationType operationType, OpenApiOperation operation, CodeClass requestConfigClass, CodeMethod method) { - if (operation.GetRequestSchema(config.StructuredMimeTypes) is OpenApiSchema requestBodySchema) + if (operation.RequestBody is not null && + operation.GetRequestSchema(config.StructuredMimeTypes) is IOpenApiSchema requestBodySchema) { CodeTypeBase requestBodyType; if (operation.RequestBody.Content.IsMultipartFormDataSchema(config.StructuredMimeTypes) && operation.RequestBody.Content.IsMultipartTopMimeType(config.StructuredMimeTypes)) { var mediaType = operation.RequestBody.Content.First(x => x.Value.Schema == requestBodySchema).Value; - if (mediaType.Encoding.Any()) + if (mediaType.Encoding is not null && mediaType.Encoding.Any()) { requestBodyType = new CodeType { Name = "MultipartBody", IsExternal = true, }; foreach (var encodingEntry in mediaType.Encoding @@ -1691,7 +1695,7 @@ private string GetModelsNamespaceNameFromReferenceId(string? referenceId) var namespaceSuffix = lastDotIndex != -1 ? $".{referenceId[..lastDotIndex]}" : string.Empty; return $"{modelsNamespace?.Name}{string.Join(NsNameSeparator, namespaceSuffix.Split(NsNameSeparator).Select(static x => x.CleanupSymbolName()))}"; } - private CodeType CreateModelDeclarationAndType(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, OpenApiOperation? operation, CodeNamespace codeNamespace, string classNameSuffix = "", OpenApiResponse? response = default, string typeNameForInlineSchema = "", bool isRequestBody = false) + private CodeType CreateModelDeclarationAndType(OpenApiUrlTreeNode currentNode, IOpenApiSchema schema, OpenApiOperation? operation, CodeNamespace codeNamespace, string classNameSuffix = "", IOpenApiResponse? response = default, string typeNameForInlineSchema = "", bool isRequestBody = false) { var className = string.IsNullOrEmpty(typeNameForInlineSchema) ? currentNode.GetClassName(config.StructuredMimeTypes, operation: operation, suffix: classNameSuffix, response: response, schema: schema, requestBody: isRequestBody).CleanupSymbolName() : typeNameForInlineSchema; var codeDeclaration = AddModelDeclarationIfDoesntExist(currentNode, operation, schema, className, codeNamespace); @@ -1700,17 +1704,17 @@ private CodeType CreateModelDeclarationAndType(OpenApiUrlTreeNode currentNode, O TypeDefinition = codeDeclaration, }; } - private CodeType CreateInheritedModelDeclarationAndType(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, OpenApiOperation? operation, string classNameSuffix, CodeNamespace codeNamespace, bool isRequestBody, string typeNameForInlineSchema, bool isViaDiscriminator = false) + private CodeType CreateInheritedModelDeclarationAndType(OpenApiUrlTreeNode currentNode, IOpenApiSchema schema, OpenApiOperation? operation, string classNameSuffix, CodeNamespace codeNamespace, bool isRequestBody, string typeNameForInlineSchema, bool isViaDiscriminator = false) { return new CodeType { TypeDefinition = CreateInheritedModelDeclaration(currentNode, schema, operation, classNameSuffix, codeNamespace, isRequestBody, typeNameForInlineSchema, isViaDiscriminator), }; } - private CodeClass CreateInheritedModelDeclaration(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, OpenApiOperation? operation, string classNameSuffix, CodeNamespace codeNamespace, bool isRequestBody, string typeNameForInlineSchema, bool isViaDiscriminator = false) + private CodeClass CreateInheritedModelDeclaration(OpenApiUrlTreeNode currentNode, IOpenApiSchema schema, OpenApiOperation? operation, string classNameSuffix, CodeNamespace codeNamespace, bool isRequestBody, string typeNameForInlineSchema, bool isViaDiscriminator = false) { var flattenedAllOfs = schema.AllOf.FlattenSchemaIfRequired(static x => x.AllOf).ToArray(); - var referenceId = schema.Reference?.Id; + var referenceId = schema.GetReferenceId(); var shortestNamespaceName = GetModelsNamespaceNameFromReferenceId(referenceId); var codeNamespaceFromParent = GetShortestNamespace(codeNamespace, schema); if (rootNamespace is null) @@ -1735,7 +1739,7 @@ private CodeClass CreateInheritedModelDeclaration(OpenApiUrlTreeNode currentNode AddModelDeclarationIfDoesntExist(currentNode, operation, schema, className, shortestNamespace), // inline schema + referenced schema (false, { Length: > 0 }, { Length: 1 }, _) => - AddModelDeclarationIfDoesntExist(currentNode, operation, schema.MergeAllOfSchemaEntries([.. referencedSchemas], static x => x.Reference is null)!, className, shortestNamespace, CreateInheritedModelDeclaration(currentNode, referencedSchemas[0], operation, classNameSuffix, codeNamespace, isRequestBody, string.Empty)), + AddModelDeclarationIfDoesntExist(currentNode, operation, schema.MergeAllOfSchemaEntries([.. referencedSchemas], static x => x is not null)!, className, shortestNamespace, CreateInheritedModelDeclaration(currentNode, referencedSchemas[0], operation, classNameSuffix, codeNamespace, isRequestBody, string.Empty)), // properties + referenced schema (true, { Length: 0 }, { Length: 1 }, _) => AddModelDeclarationIfDoesntExist(currentNode, operation, schema, className, shortestNamespace, CreateInheritedModelDeclaration(currentNode, referencedSchemas[0], operation, classNameSuffix, codeNamespace, isRequestBody, string.Empty)), @@ -1777,20 +1781,20 @@ private CodeClass CreateInheritedModelDeclaration(OpenApiUrlTreeNode currentNode if (!currentClass.Documentation.DescriptionAvailable && new string[] { schema.Description } .Union(schema.AllOf - .Where(static x => x.Reference is null) + .OfType() .Select(static x => x.Description)) .FirstOrDefault(static x => !string.IsNullOrEmpty(x)) is string description) currentClass.Documentation.DescriptionTemplate = description.CleanupDescription(); // the last allof entry often is not a reference and doesn't have a description. return currentClass; } - private CodeTypeBase CreateComposedModelDeclaration(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, OpenApiOperation? operation, string suffixForInlineSchema, CodeNamespace codeNamespace, bool isRequestBody, string typeNameForInlineSchema) + private CodeTypeBase CreateComposedModelDeclaration(OpenApiUrlTreeNode currentNode, IOpenApiSchema schema, OpenApiOperation? operation, string suffixForInlineSchema, CodeNamespace codeNamespace, bool isRequestBody, string typeNameForInlineSchema) { var typeName = string.IsNullOrEmpty(typeNameForInlineSchema) ? currentNode.GetClassName(config.StructuredMimeTypes, operation: operation, suffix: suffixForInlineSchema, schema: schema, requestBody: isRequestBody).CleanupSymbolName() : typeNameForInlineSchema; var typesCount = schema.AnyOf?.Count ?? schema.OneOf?.Count ?? 0; - if ((typesCount == 1 && schema.Nullable && schema.IsInclusiveUnion() || // nullable on the root schema outside of anyOf + if ((typesCount == 1 && (schema.Type & JsonSchemaType.Null) is JsonSchemaType.Null && schema.IsInclusiveUnion() || // nullable on the root schema outside of anyOf typesCount == 2 && (schema.AnyOf?.Any(static x => // nullable on a schema in the anyOf - x.Nullable && + (x.Type & JsonSchemaType.Null) is JsonSchemaType.Null && !x.HasAnyProperty() && !x.IsExclusiveUnion() && !x.IsInclusiveUnion() && @@ -1799,7 +1803,7 @@ private CodeTypeBase CreateComposedModelDeclaration(OpenApiUrlTreeNode currentNo !x.IsArray() && !x.IsReferencedSchema()) ?? false)) && schema.AnyOf?.FirstOrDefault(static x => !string.IsNullOrEmpty(x.GetSchemaName())) is { } targetSchema) - {// once openAPI 3.1 is supported, there will be a third case oneOf with Ref and type null. + { var className = targetSchema.GetSchemaName().CleanupSymbolName(); var shortestNamespace = GetShortestNamespace(codeNamespace, targetSchema); return new CodeType @@ -1820,8 +1824,8 @@ private CodeTypeBase CreateComposedModelDeclaration(OpenApiUrlTreeNode currentNo }, schema.AnyOf), (_, _) => throw new InvalidOperationException("Schema is not oneOf nor anyOf"), }; - if (!string.IsNullOrEmpty(schema.Reference?.Id)) - unionType.TargetNamespace = codeNamespace.GetRootNamespace().FindOrAddNamespace(GetModelsNamespaceNameFromReferenceId(schema.Reference.Id)); + if (schema.GetReferenceId() is string refId && !string.IsNullOrEmpty(refId)) + unionType.TargetNamespace = codeNamespace.GetRootNamespace().FindOrAddNamespace(GetModelsNamespaceNameFromReferenceId(refId)); unionType.DiscriminatorInformation.DiscriminatorPropertyName = schema.GetDiscriminatorPropertyName(); GetDiscriminatorMappings(currentNode, schema, codeNamespace, null, operation) ?.ToList() @@ -1850,9 +1854,29 @@ private CodeTypeBase CreateComposedModelDeclaration(OpenApiUrlTreeNode currentNo if (!unionType.ContainsType(declarationType)) unionType.AddType(declarationType); } + if (schema.IsArrayOfTypes()) + { + AddTypeArrayMemberToComposedType(schema, JsonSchemaType.Boolean, unionType); + AddTypeArrayMemberToComposedType(schema, JsonSchemaType.Integer, unionType); + AddTypeArrayMemberToComposedType(schema, JsonSchemaType.Number, unionType); + AddTypeArrayMemberToComposedType(schema, JsonSchemaType.String, unionType); + } return unionType; } - private CodeTypeBase CreateModelDeclarations(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, OpenApiOperation? operation, CodeElement parentElement, string suffixForInlineSchema, OpenApiResponse? response = default, string typeNameForInlineSchema = "", bool isRequestBody = false, bool isViaDiscriminator = false) + private void AddTypeArrayMemberToComposedType(IOpenApiSchema schema, JsonSchemaType typeToScan, CodeComposedTypeBase codeComposedTypeBase) + { + if (!schema.Type.HasValue || (schema.Type.Value & typeToScan) != typeToScan) return; + + var temporarySchema = new OpenApiSchema { Type = typeToScan, Format = schema.Format }; + if (GetPrimitiveType(temporarySchema) is CodeType primitiveType && !string.IsNullOrEmpty(primitiveType.Name)) + { + if (schema.IsArray()) + primitiveType.CollectionKind = CodeTypeBase.CodeTypeCollectionKind.Complex; + if (!codeComposedTypeBase.ContainsType(primitiveType)) + codeComposedTypeBase.AddType(primitiveType); + } + } + private CodeTypeBase CreateModelDeclarations(OpenApiUrlTreeNode currentNode, IOpenApiSchema schema, OpenApiOperation? operation, CodeElement parentElement, string suffixForInlineSchema, IOpenApiResponse? response = default, string typeNameForInlineSchema = "", bool isRequestBody = false, bool isViaDiscriminator = false) { var (codeNamespace, responseValue, suffix) = schema.IsReferencedSchema() switch { @@ -1873,7 +1897,7 @@ private CodeTypeBase CreateModelDeclarations(OpenApiUrlTreeNode currentNode, Ope return CreateInheritedModelDeclarationAndType(currentNode, schema, operation, suffix, codeNamespace, isRequestBody, typeNameForInlineSchema, isViaDiscriminator); } - if (schema.IsIntersection() && schema.MergeIntersectionSchemaEntries() is OpenApiSchema mergedSchema) + if (schema.IsIntersection() && schema.MergeIntersectionSchemaEntries() is IOpenApiSchema mergedSchema) { // multiple allOf entries that do not translate to inheritance return CreateModelDeclarationAndType(currentNode, mergedSchema, operation, codeNamespace, suffix, response: responseValue, typeNameForInlineSchema: typeNameForInlineSchema, isRequestBody); @@ -1886,18 +1910,18 @@ private CodeTypeBase CreateModelDeclarations(OpenApiUrlTreeNode currentNode, Ope } // type: object with single oneOf referring to inheritance or intersection - if (schema.IsObjectType() && schema.MergeSingleExclusiveUnionInheritanceOrIntersectionSchemaEntries() is OpenApiSchema mergedExclusiveUnionSchema) + if (schema.IsObjectType() && schema.MergeSingleExclusiveUnionInheritanceOrIntersectionSchemaEntries() is IOpenApiSchema mergedExclusiveUnionSchema) { return CreateModelDeclarations(currentNode, mergedExclusiveUnionSchema, operation, parentElement, suffixForInlineSchema, response, typeNameForInlineSchema, isRequestBody); } // type: object with single anyOf referring to inheritance or intersection - if (schema.IsObjectType() && schema.MergeSingleInclusiveUnionInheritanceOrIntersectionSchemaEntries() is OpenApiSchema mergedInclusiveUnionSchema) + if (schema.IsObjectType() && schema.MergeSingleInclusiveUnionInheritanceOrIntersectionSchemaEntries() is IOpenApiSchema mergedInclusiveUnionSchema) { return CreateModelDeclarations(currentNode, mergedInclusiveUnionSchema, operation, parentElement, suffixForInlineSchema, response, typeNameForInlineSchema, isRequestBody); } - if (schema.IsObjectType() || schema.HasAnyProperty() || schema.IsEnum() || !string.IsNullOrEmpty(schema.AdditionalProperties?.Type)) + if (schema.IsObjectType() || schema.HasAnyProperty() || schema.IsEnum() || schema.AdditionalProperties?.Type is not null) { // no inheritance or union type, often empty definitions with only additional properties are used as property bags. return CreateModelDeclarationAndType(currentNode, schema, operation, codeNamespace, suffix, response: responseValue, typeNameForInlineSchema: typeNameForInlineSchema, isRequestBody); @@ -1910,16 +1934,16 @@ private CodeTypeBase CreateModelDeclarations(OpenApiUrlTreeNode currentNode, Ope return CreateCollectionModelDeclaration(currentNode, schema, operation, codeNamespace, typeNameForInlineSchema, isRequestBody); } - if (!string.IsNullOrEmpty(schema.Type) || !string.IsNullOrEmpty(schema.Format)) - return GetPrimitiveType(schema, string.Empty) ?? new CodeType { Name = UntypedNodeName, IsExternal = true }; + if (schema.Type is not null && (schema.Type & ~JsonSchemaType.Null) != 0 || !string.IsNullOrEmpty(schema.Format)) + return GetPrimitiveType(schema) ?? new CodeType { Name = UntypedNodeName, IsExternal = true }; if ((schema.AnyOf.Any() || schema.OneOf.Any() || schema.AllOf.Any()) && (schema.AnyOf.FirstOrDefault(static x => x.IsSemanticallyMeaningful(true)) ?? schema.OneOf.FirstOrDefault(static x => x.IsSemanticallyMeaningful(true)) ?? schema.AllOf.FirstOrDefault(static x => x.IsSemanticallyMeaningful(true))) is { } childSchema) // we have an empty node because of some local override for schema properties and need to unwrap it. return CreateModelDeclarations(currentNode, childSchema, operation, parentElement, suffixForInlineSchema, response, typeNameForInlineSchema, isRequestBody); return new CodeType { Name = UntypedNodeName, IsExternal = true }; } - private CodeTypeBase CreateCollectionModelDeclaration(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, OpenApiOperation? operation, CodeNamespace codeNamespace, string typeNameForInlineSchema, bool isRequestBody) + private CodeTypeBase CreateCollectionModelDeclaration(OpenApiUrlTreeNode currentNode, IOpenApiSchema schema, OpenApiOperation? operation, CodeNamespace codeNamespace, string typeNameForInlineSchema, bool isRequestBody) { - CodeTypeBase? type = GetPrimitiveType(schema.Items, string.Empty); + CodeTypeBase? type = GetPrimitiveType(schema.Items); var isEnumOrComposedCollectionType = schema.Items.IsEnum() //the collection could be an enum type so override with strong type instead of string type. || schema.Items.IsComposedEnum() && string.IsNullOrEmpty(schema.Items.Format);//the collection could be a composed type with an enum type so override with strong type instead of string type. if ((string.IsNullOrEmpty(type?.Name) @@ -1946,7 +1970,7 @@ private CodeNamespace GetSearchNamespace(OpenApiUrlTreeNode currentNode, CodeNam return currentNamespace; } private ConcurrentDictionary classLifecycles = new(StringComparer.OrdinalIgnoreCase); - private CodeElement AddModelDeclarationIfDoesntExist(OpenApiUrlTreeNode currentNode, OpenApiOperation? currentOperation, OpenApiSchema schema, string declarationName, CodeNamespace currentNamespace, CodeClass? inheritsFrom = null) + private CodeElement AddModelDeclarationIfDoesntExist(OpenApiUrlTreeNode currentNode, OpenApiOperation? currentOperation, IOpenApiSchema schema, string declarationName, CodeNamespace currentNamespace, CodeClass? inheritsFrom = null) { if (GetExistingDeclaration(currentNamespace, currentNode, declarationName) is not CodeElement existingDeclaration) // we can find it in the components { @@ -1973,7 +1997,7 @@ private CodeElement AddModelDeclarationIfDoesntExist(OpenApiUrlTreeNode currentN } return existingDeclaration; } - private CodeEnum? AddEnumDeclarationIfDoesntExist(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, string declarationName, CodeNamespace currentNamespace) + private CodeEnum? AddEnumDeclarationIfDoesntExist(OpenApiUrlTreeNode currentNode, IOpenApiSchema schema, string declarationName, CodeNamespace currentNamespace) { if (GetExistingDeclaration(currentNamespace, currentNode, declarationName) is not CodeEnum existingDeclaration) // we can find it in the components { @@ -1981,7 +2005,7 @@ private CodeElement AddModelDeclarationIfDoesntExist(OpenApiUrlTreeNode currentN } return existingDeclaration; } - private static CodeEnum? AddEnumDeclaration(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, string declarationName, CodeNamespace currentNamespace) + private static CodeEnum? AddEnumDeclaration(OpenApiUrlTreeNode currentNode, IOpenApiSchema schema, string declarationName, CodeNamespace currentNamespace) { if (schema.IsEnum()) { @@ -1998,7 +2022,7 @@ private CodeElement AddModelDeclarationIfDoesntExist(OpenApiUrlTreeNode currentN Flags = enumFlagsExtension?.IsFlags ?? false, Documentation = new() { - DescriptionTemplate = !string.IsNullOrEmpty(schemaDescription) || !string.IsNullOrEmpty(schema.Reference?.Id) ? + DescriptionTemplate = !string.IsNullOrEmpty(schemaDescription) || !string.IsNullOrEmpty(schema.GetReferenceId()) ? schemaDescription : // if it's a referenced component, we shouldn't use the path item description as it makes it indeterministic currentNode.GetPathItemDescription(Constants.DefaultOpenApiLabel), }, @@ -2009,14 +2033,15 @@ private CodeElement AddModelDeclarationIfDoesntExist(OpenApiUrlTreeNode currentN } return default; } - private static void SetEnumOptions(OpenApiSchema schema, CodeEnum target) + private static void SetEnumOptions(IOpenApiSchema schema, CodeEnum target) { OpenApiEnumValuesDescriptionExtension? extensionInformation = null; if (schema.Extensions.TryGetValue(OpenApiEnumValuesDescriptionExtension.Name, out var rawExtension) && rawExtension is OpenApiEnumValuesDescriptionExtension localExtInfo) extensionInformation = localExtInfo; - target.AddOption(schema.Enum.OfType() - .Where(static x => !x.Value.Equals("null", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(x.Value)) - .Select(static x => x.Value) + target.AddOption(schema.Enum.OfType() + .Where(static x => x.GetValueKind() is JsonValueKind.String or JsonValueKind.Number) + .Select(static x => x.GetValueKind() is JsonValueKind.String ? x.GetValue() : x.GetValue().ToString(CultureInfo.InvariantCulture)) + .Where(static x => !string.IsNullOrEmpty(x)) .Distinct(StringComparer.OrdinalIgnoreCase) .Select((x) => { @@ -2036,18 +2061,18 @@ private static void SetEnumOptions(OpenApiSchema schema, CodeEnum target) .Where(static x => !string.IsNullOrEmpty(x.Name)) .ToArray()); } - private CodeNamespace GetShortestNamespace(CodeNamespace currentNamespace, OpenApiSchema currentSchema) + private CodeNamespace GetShortestNamespace(CodeNamespace currentNamespace, IOpenApiSchema currentSchema) { - if (!string.IsNullOrEmpty(currentSchema.Reference?.Id) && rootNamespace != null) + if (currentSchema.GetReferenceId() is string refId && !string.IsNullOrEmpty(refId) && rootNamespace != null) { - var parentClassNamespaceName = GetModelsNamespaceNameFromReferenceId(currentSchema.Reference.Id); + var parentClassNamespaceName = GetModelsNamespaceNameFromReferenceId(refId); return rootNamespace.AddNamespace(parentClassNamespaceName); } return currentNamespace; } - private CodeClass AddModelClass(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, string declarationName, CodeNamespace currentNamespace, OpenApiOperation? currentOperation, CodeClass? inheritsFrom = null) + private CodeClass AddModelClass(OpenApiUrlTreeNode currentNode, IOpenApiSchema schema, string declarationName, CodeNamespace currentNamespace, OpenApiOperation? currentOperation, CodeClass? inheritsFrom = null) { - if (inheritsFrom == null && schema.AllOf.Where(static x => x.Reference != null).ToArray() is { Length: 1 } referencedSchemas) + if (inheritsFrom == null && schema.AllOf.OfType().ToArray() is { Length: 1 } referencedSchemas) {// any non-reference would be the current class in some description styles var parentSchema = referencedSchemas[0]; var parentClassNamespace = GetShortestNamespace(currentNamespace, parentSchema); @@ -2096,15 +2121,29 @@ private CodeClass AddModelClass(OpenApiUrlTreeNode currentNode, OpenApiSchema sc } } + var lookupSchema = schema.GetMergedSchemaOriginalReferenceId() is string originalName ? + new OpenApiSchemaReference(originalName, openApiDocument) : + schema; // Recurse into the discriminator & generate its referenced types - var mappings = GetDiscriminatorMappings(currentNode, schema, currentNamespace, newClass, currentOperation) + var mappings = GetDiscriminatorMappings(currentNode, lookupSchema, currentNamespace, newClass, currentOperation) .Where(x => x.Value is { TypeDefinition: CodeClass definition } && definition.DerivesFrom(newClass)); // only the mappings that derive from the current class AddDiscriminatorMethod(newClass, schema.GetDiscriminatorPropertyName(), mappings, static s => s); return newClass; } - private IEnumerable> GetDiscriminatorMappings(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, CodeNamespace currentNamespace, CodeClass? baseClass, OpenApiOperation? currentOperation) + /// + /// Creates a reference to the component so inheritance scenarios get the right class name + /// + /// Component to lookup + /// A reference to the component schema + private OpenApiSchemaReference? GetSchemaReferenceToComponentSchema(string componentName) + { + if (openApiDocument?.Components?.Schemas?.ContainsKey(componentName) ?? false) + return new OpenApiSchemaReference(componentName, openApiDocument); + return null; + } + private IEnumerable> GetDiscriminatorMappings(OpenApiUrlTreeNode currentNode, IOpenApiSchema schema, CodeNamespace currentNamespace, CodeClass? baseClass, OpenApiOperation? currentOperation) { // Generate types that this discriminator references return schema.GetDiscriminatorMappings(inheritanceIndex) @@ -2289,13 +2328,14 @@ internal static void AddDiscriminatorMethod(CodeClass newClass, string discrimin private CodeType? GetCodeTypeForMapping(OpenApiUrlTreeNode currentNode, string referenceId, CodeNamespace currentNamespace, CodeClass? baseClass, OpenApiOperation? currentOperation) { var componentKey = referenceId?.Replace("#/components/schemas/", string.Empty, StringComparison.OrdinalIgnoreCase); - if (openApiDocument == null || !openApiDocument.Components.Schemas.TryGetValue(componentKey, out var discriminatorSchema)) + if (openApiDocument == null || string.IsNullOrEmpty(componentKey) || openApiDocument.Components?.Schemas is null || GetSchemaReferenceToComponentSchema(componentKey) is not { } discriminatorSchema) { logger.LogWarning("Discriminator {ComponentKey} not found in the OpenAPI document.", componentKey); return null; } + var schemaClone = discriminatorSchema.CreateShallowCopy(); // Call CreateModelDeclarations with isViaDiscriminator=true. This is for a special case where we always generate a base class when types are referenced via a oneOf discriminator. - if (CreateModelDeclarations(currentNode, discriminatorSchema, currentOperation, GetShortestNamespace(currentNamespace, discriminatorSchema), string.Empty, null, string.Empty, false, true) is not CodeType result) + if (CreateModelDeclarations(currentNode, schemaClone, currentOperation, GetShortestNamespace(currentNamespace, schemaClone), string.Empty, null, string.Empty, false, true) is not CodeType result) { logger.LogWarning("Discriminator {ComponentKey} is not a valid model and points to a union type.", componentKey); return null; @@ -2308,14 +2348,14 @@ internal static void AddDiscriminatorMethod(CodeClass newClass, string discrimin } return result; } - private void CreatePropertiesForModelClass(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, CodeNamespace ns, CodeClass model) + private void CreatePropertiesForModelClass(OpenApiUrlTreeNode currentNode, IOpenApiSchema schema, CodeNamespace ns, CodeClass model) { var propertiesToAdd = schema.Properties .Select(x => { var propertySchema = x.Value; var className = $"{model.Name}_{x.Key.CleanupSymbolName()}"; - var shortestNamespaceName = GetModelsNamespaceNameFromReferenceId(propertySchema.Reference?.Id); + var shortestNamespaceName = GetModelsNamespaceNameFromReferenceId(propertySchema.GetReferenceId()); var targetNamespace = string.IsNullOrEmpty(shortestNamespaceName) ? ns : rootNamespace?.FindOrAddNamespace(shortestNamespaceName) ?? ns; var definition = CreateModelDeclarations(currentNode, propertySchema, default, targetNamespace, string.Empty, typeNameForInlineSchema: className); @@ -2454,7 +2494,7 @@ internal static void AddSerializationMembers(CodeClass model, bool includeAdditi } private CodeClass? CreateOperationParameterClass(OpenApiUrlTreeNode node, OperationType operationType, OpenApiOperation operation, CodeClass parentClass) { - var parameters = node.PathItems[Constants.DefaultOpenApiLabel].Parameters.Union(operation.Parameters).Where(static p => p.In == ParameterLocation.Query).ToArray(); + var parameters = node.PathItems[Constants.DefaultOpenApiLabel].Parameters.Union(operation.Parameters ?? Enumerable.Empty()).Where(static p => p.In == ParameterLocation.Query).ToArray(); if (parameters.Length != 0) { var parameterClass = parentClass.AddInnerClass(new CodeClass @@ -2476,7 +2516,7 @@ internal static void AddSerializationMembers(CodeClass model, bool includeAdditi return null; } - private void AddPropertyForQueryParameter(OpenApiUrlTreeNode node, OperationType operationType, OpenApiParameter parameter, CodeClass parameterClass) + private void AddPropertyForQueryParameter(OpenApiUrlTreeNode node, OperationType operationType, IOpenApiParameter parameter, CodeClass parameterClass) { CodeType? resultType = default; var addBackwardCompatibleParameter = false; @@ -2559,12 +2599,12 @@ private static CodeType GetDefaultQueryParameterType() Name = "string", }; } - private static CodeType GetQueryParameterType(OpenApiSchema schema) + private static CodeType GetQueryParameterType(IOpenApiSchema schema) { var paramType = GetPrimitiveType(schema) ?? new() { IsExternal = true, - Name = schema.Items?.Type ?? schema.Type, + Name = schema.Items is not null && (schema.Items.Type & ~JsonSchemaType.Null)?.ToIdentifiers().FirstOrDefault() is string name ? name : "null", }; paramType.CollectionKind = schema.IsArray() ? CodeTypeBase.CodeTypeCollectionKind.Array : default; diff --git a/src/Kiota.Builder/LanguageInformation.cs b/src/Kiota.Builder/LanguageInformation.cs index d58dfa3b6a..2966c90f81 100644 --- a/src/Kiota.Builder/LanguageInformation.cs +++ b/src/Kiota.Builder/LanguageInformation.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.Json.Serialization; using Kiota.Builder.Extensions; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Writers; @@ -30,8 +31,9 @@ public SupportExperience SupportExperience #pragma warning disable CA2227 public HashSet StructuredMimeTypes { get; set; } = new(StringComparer.OrdinalIgnoreCase); #pragma warning restore CA2227 - public void SerializeAsV2(IOpenApiWriter writer) => SerializeAsV3(writer); - public void SerializeAsV3(IOpenApiWriter writer) + public void SerializeAsV2(IOpenApiWriter writer) => SerializeAsV31(writer); + public void SerializeAsV3(IOpenApiWriter writer) => SerializeAsV31(writer); + public void SerializeAsV31(IOpenApiWriter writer) { ArgumentNullException.ThrowIfNull(writer); writer.WriteStartObject(); @@ -44,38 +46,41 @@ public void SerializeAsV3(IOpenApiWriter writer) writer.WriteOptionalCollection(nameof(StructuredMimeTypes).ToFirstCharacterLowerCase(), StructuredMimeTypes, static (w, x) => w.WriteValue(x)); writer.WriteEndObject(); } - public static LanguageInformation Parse(IOpenApiAny source) + public static LanguageInformation Parse(JsonNode source) { - if (source is not OpenApiObject rawObject) throw new ArgumentOutOfRangeException(nameof(source)); + ArgumentNullException.ThrowIfNull(source); + if (source.GetValueKind() is not JsonValueKind.Object || + source.AsObject() is not JsonObject rawObject) throw new ArgumentOutOfRangeException(nameof(source)); var extension = new LanguageInformation(); - if (rawObject.TryGetValue(nameof(Dependencies).ToFirstCharacterLowerCase(), out var dependencies) && dependencies is OpenApiArray arrayValue) + if (rawObject.TryGetPropertyValue(nameof(Dependencies).ToFirstCharacterLowerCase(), out var dependencies) && dependencies is JsonArray arrayValue) { foreach (var entry in arrayValue) - extension.Dependencies.Add(LanguageDependency.Parse(entry)); + if (entry is not null) + extension.Dependencies.Add(LanguageDependency.Parse(entry)); } - if (rawObject.TryGetValue(nameof(DependencyInstallCommand).ToFirstCharacterLowerCase(), out var installCommand) && installCommand is OpenApiString stringValue) + if (rawObject.TryGetPropertyValue(nameof(DependencyInstallCommand).ToFirstCharacterLowerCase(), out var installCommand) && installCommand is JsonValue stringValue) { - extension.DependencyInstallCommand = stringValue.Value; + extension.DependencyInstallCommand = stringValue.GetValue(); } // not parsing the maturity level on purpose, we don't want APIs to be able to change that - if (rawObject.TryGetValue(nameof(ClientClassName).ToFirstCharacterLowerCase(), out var clientClassName) && clientClassName is OpenApiString clientClassNameValue) + if (rawObject.TryGetPropertyValue(nameof(ClientClassName).ToFirstCharacterLowerCase(), out var clientClassName) && clientClassName is JsonValue clientClassNameValue) { - extension.ClientClassName = clientClassNameValue.Value; + extension.ClientClassName = clientClassNameValue.GetValue(); } - if (rawObject.TryGetValue(nameof(ClientNamespaceName).ToFirstCharacterLowerCase(), out var clientNamespaceName) && clientNamespaceName is OpenApiString clientNamespaceNameValue) + if (rawObject.TryGetPropertyValue(nameof(ClientNamespaceName).ToFirstCharacterLowerCase(), out var clientNamespaceName) && clientNamespaceName is JsonValue clientNamespaceNameValue) { - extension.ClientNamespaceName = clientNamespaceNameValue.Value; + extension.ClientNamespaceName = clientNamespaceNameValue.GetValue(); } - if (rawObject.TryGetValue(nameof(StructuredMimeTypes).ToFirstCharacterLowerCase(), out var structuredMimeTypes) && structuredMimeTypes is OpenApiArray structuredMimeTypesValue) + if (rawObject.TryGetPropertyValue(nameof(StructuredMimeTypes).ToFirstCharacterLowerCase(), out var structuredMimeTypes) && structuredMimeTypes is JsonArray structuredMimeTypesValue) { - foreach (var entry in structuredMimeTypesValue.OfType()) - extension.StructuredMimeTypes.Add(entry.Value); + foreach (var entry in structuredMimeTypesValue.OfType()) + extension.StructuredMimeTypes.Add(entry.GetValue()); } - if (rawObject.TryGetValue(nameof(MaturityLevel).ToFirstCharacterLowerCase(), out var maturityLevel) && maturityLevel is OpenApiString maturityLevelValue && Enum.TryParse(maturityLevelValue.Value, true, out var parsedMaturityLevelValue)) + if (rawObject.TryGetPropertyValue(nameof(MaturityLevel).ToFirstCharacterLowerCase(), out var maturityLevel) && maturityLevel is JsonValue maturityLevelValue && maturityLevelValue.GetValueKind() is JsonValueKind.String && Enum.TryParse(maturityLevelValue.GetValue(), true, out var parsedMaturityLevelValue)) { extension.MaturityLevel = parsedMaturityLevelValue; } - if (rawObject.TryGetValue(nameof(SupportExperience).ToFirstCharacterLowerCase(), out var supportExperience) && supportExperience is OpenApiString supportExperienceValue && Enum.TryParse(supportExperienceValue.Value, true, out var parsedSupportExperienceValue)) + if (rawObject.TryGetPropertyValue(nameof(SupportExperience).ToFirstCharacterLowerCase(), out var supportExperience) && supportExperience is JsonValue supportExperienceValue && supportExperienceValue.GetValueKind() is JsonValueKind.String && Enum.TryParse(supportExperienceValue.GetValue(), true, out var parsedSupportExperienceValue)) { extension.SupportExperience = parsedSupportExperienceValue; } @@ -92,8 +97,9 @@ public DependencyType? DependencyType get; set; } private const string TypePropertyName = "type"; - public void SerializeAsV2(IOpenApiWriter writer) => SerializeAsV3(writer); - public void SerializeAsV3(IOpenApiWriter writer) + public void SerializeAsV2(IOpenApiWriter writer) => SerializeAsV31(writer); + public void SerializeAsV3(IOpenApiWriter writer) => SerializeAsV31(writer); + public void SerializeAsV31(IOpenApiWriter writer) { ArgumentNullException.ThrowIfNull(writer); writer.WriteStartObject(); @@ -105,19 +111,21 @@ public void SerializeAsV3(IOpenApiWriter writer) } writer.WriteEndObject(); } - public static LanguageDependency Parse(IOpenApiAny source) + public static LanguageDependency Parse(JsonNode source) { - if (source is not OpenApiObject rawObject) throw new ArgumentOutOfRangeException(nameof(source)); + ArgumentNullException.ThrowIfNull(source); + if (source.GetValueKind() is not JsonValueKind.Object || + source.AsObject() is not JsonObject rawObject) throw new ArgumentOutOfRangeException(nameof(source)); var extension = new LanguageDependency(); - if (rawObject.TryGetValue(nameof(Name).ToFirstCharacterLowerCase(), out var name) && name is OpenApiString stringValue) + if (rawObject.TryGetPropertyValue(nameof(Name).ToFirstCharacterLowerCase(), out var nameNode) && nameNode is JsonValue nameJsonValue && nameJsonValue.TryGetValue(out var nameValue)) { - extension.Name = stringValue.Value; + extension.Name = nameValue; } - if (rawObject.TryGetValue(nameof(Version).ToFirstCharacterLowerCase(), out var version) && version is OpenApiString versionValue) + if (rawObject.TryGetPropertyValue(nameof(Version).ToFirstCharacterLowerCase(), out var versionNode) && versionNode is JsonValue versionJsonValue && versionJsonValue.TryGetValue(out var versionValue)) { - extension.Version = versionValue.Value; + extension.Version = versionValue; } - if (rawObject.TryGetValue(TypePropertyName, out var typeValue) && typeValue is OpenApiString typeStringValue && Enum.TryParse(typeStringValue.Value, true, out var parsedTypeValue)) + if (rawObject.TryGetPropertyValue(TypePropertyName, out var typeNode) && typeNode is JsonValue typeJsonValue && typeJsonValue.TryGetValue(out var typeValue) && Enum.TryParse(typeValue, true, out var parsedTypeValue)) { extension.DependencyType = parsedTypeValue; } diff --git a/src/Kiota.Builder/OpenApiDocumentDownloadService.cs b/src/Kiota.Builder/OpenApiDocumentDownloadService.cs index ae586c86a3..1739f502b5 100644 --- a/src/Kiota.Builder/OpenApiDocumentDownloadService.cs +++ b/src/Kiota.Builder/OpenApiDocumentDownloadService.cs @@ -16,6 +16,7 @@ using Kiota.Builder.WorkspaceManagement; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Validations; @@ -30,6 +31,8 @@ public OpenApiDocumentDownloadService(HttpClient httpClient, ILogger logger) ArgumentNullException.ThrowIfNull(logger); HttpClient = httpClient; Logger = logger; + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader()); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader()); } private static readonly AsyncKeyedLocker localFilesLock = new(o => { @@ -135,25 +138,24 @@ ex is SecurityException || { // couldn't parse the URL, it's probably a local file } - var reader = new OpenApiStreamReader(settings); - var readResult = await reader.ReadAsync(input, cancellationToken).ConfigureAwait(false); + var readResult = await OpenApiDocument.LoadAsync(input, settings: settings, cancellationToken: cancellationToken).ConfigureAwait(false); stopwatch.Stop(); if (generating) - foreach (var warning in readResult.OpenApiDiagnostic.Warnings) + foreach (var warning in readResult.Diagnostic.Warnings) Logger.LogWarning("OpenAPI warning: {Pointer} - {Warning}", warning.Pointer, warning.Message); - if (readResult.OpenApiDiagnostic.Errors.Any()) + if (readResult.Diagnostic.Errors.Any()) { - Logger.LogTrace("{Timestamp}ms: Parsed OpenAPI with errors. {Count} paths found.", stopwatch.ElapsedMilliseconds, readResult.OpenApiDocument?.Paths?.Count ?? 0); - foreach (var parsingError in readResult.OpenApiDiagnostic.Errors) + Logger.LogTrace("{Timestamp}ms: Parsed OpenAPI with errors. {Count} paths found.", stopwatch.ElapsedMilliseconds, readResult.Document?.Paths?.Count ?? 0); + foreach (var parsingError in readResult.Diagnostic.Errors) { Logger.LogError("OpenAPI error: {Pointer} - {Message}", parsingError.Pointer, parsingError.Message); } } else { - Logger.LogTrace("{Timestamp}ms: Parsed OpenAPI successfully. {Count} paths found.", stopwatch.ElapsedMilliseconds, readResult.OpenApiDocument?.Paths?.Count ?? 0); + Logger.LogTrace("{Timestamp}ms: Parsed OpenAPI successfully. {Count} paths found.", stopwatch.ElapsedMilliseconds, readResult.Document?.Paths?.Count ?? 0); } - return readResult.OpenApiDocument; + return readResult.Document; } } diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiAiReasoningInstructionsExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiAiReasoningInstructionsExtension.cs index f690e1df81..8cfd72a30e 100644 --- a/src/Kiota.Builder/OpenApiExtensions/OpenApiAiReasoningInstructionsExtension.cs +++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiAiReasoningInstructionsExtension.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; using Microsoft.OpenApi; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Writers; @@ -28,11 +29,11 @@ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion) writer.WriteEndArray(); } } - public static OpenApiAiReasoningInstructionsExtension Parse(IOpenApiAny source) + public static OpenApiAiReasoningInstructionsExtension Parse(JsonNode source) { - if (source is not OpenApiArray rawArray) throw new ArgumentOutOfRangeException(nameof(source)); + if (source is not JsonArray rawArray) throw new ArgumentOutOfRangeException(nameof(source)); var result = new OpenApiAiReasoningInstructionsExtension(); - result.ReasoningInstructions.AddRange(rawArray.OfType().Select(x => x.Value)); + result.ReasoningInstructions.AddRange(rawArray.OfType().Where(static x => x.GetValueKind() is JsonValueKind.String).Select(static x => x.GetValue())); return result; } } diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiAiRespondingInstructionsExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiAiRespondingInstructionsExtension.cs index b8137ec225..440f90f03b 100644 --- a/src/Kiota.Builder/OpenApiExtensions/OpenApiAiRespondingInstructionsExtension.cs +++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiAiRespondingInstructionsExtension.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; using Microsoft.OpenApi; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Writers; @@ -28,11 +29,11 @@ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion) writer.WriteEndArray(); } } - public static OpenApiAiRespondingInstructionsExtension Parse(IOpenApiAny source) + public static OpenApiAiRespondingInstructionsExtension Parse(JsonNode source) { - if (source is not OpenApiArray rawArray) throw new ArgumentOutOfRangeException(nameof(source)); + if (source is not JsonArray rawArray) throw new ArgumentOutOfRangeException(nameof(source)); var result = new OpenApiAiRespondingInstructionsExtension(); - result.RespondingInstructions.AddRange(rawArray.OfType().Select(x => x.Value)); + result.RespondingInstructions.AddRange(rawArray.OfType().Where(static x => x.GetValueKind() is JsonValueKind.String).Select(static x => x.GetValue())); return result; } } diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiDescriptionForModelExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiDescriptionForModelExtension.cs index ba198df7c9..0a174f9fa3 100644 --- a/src/Kiota.Builder/OpenApiExtensions/OpenApiDescriptionForModelExtension.cs +++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiDescriptionForModelExtension.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Any; +using System.Text.Json.Nodes; namespace Kiota.Builder.OpenApiExtensions; @@ -10,7 +10,7 @@ public string? Description get; set; } protected override string? ValueSelector => Description; - public static OpenApiDescriptionForModelExtension Parse(IOpenApiAny source) + public static OpenApiDescriptionForModelExtension Parse(JsonNode source) { return new OpenApiDescriptionForModelExtension { diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiKiotaExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiKiotaExtension.cs index 80a9149f2f..6c7d9398d8 100644 --- a/src/Kiota.Builder/OpenApiExtensions/OpenApiKiotaExtension.cs +++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiKiotaExtension.cs @@ -1,9 +1,9 @@ using System; using System.Linq; +using System.Text.Json.Nodes; using Kiota.Builder.Configuration; using Kiota.Builder.Extensions; using Microsoft.OpenApi; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Writers; @@ -30,11 +30,11 @@ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion) writer.WriteEndObject(); } } - public static OpenApiKiotaExtension Parse(IOpenApiAny source) + public static OpenApiKiotaExtension Parse(JsonNode source) { - if (source is not OpenApiObject rawObject) throw new ArgumentOutOfRangeException(nameof(source)); + if (source is not JsonObject jsonNode) throw new ArgumentOutOfRangeException(nameof(source)); var extension = new OpenApiKiotaExtension(); - if (rawObject.TryGetValue(nameof(LanguagesInformation).ToFirstCharacterLowerCase(), out var languagesInfo) && languagesInfo is OpenApiObject objectValue) + if (jsonNode.TryGetPropertyValue(nameof(LanguagesInformation).ToFirstCharacterLowerCase(), out var languagesInfo) && languagesInfo is JsonObject objectValue) { extension.LanguagesInformation = LanguagesInformation.Parse(objectValue); } diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiKiotaMergedExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiKiotaMergedExtension.cs new file mode 100644 index 0000000000..3556fe5e4d --- /dev/null +++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiKiotaMergedExtension.cs @@ -0,0 +1,26 @@ + +using System; +using Microsoft.OpenApi; +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Writers; + +namespace Kiota.Builder.OpenApiExtensions; +/// +/// Temporary stores the original reference id when we merge schemas so the reference id can be used to lookup the inheritance index and find the discriminator values +/// +internal class OpenApiKiotaMergedExtension : IOpenApiExtension +{ + public static string Name => "x-kiota-merged"; + public string OriginalName + { + get; + } + public OpenApiKiotaMergedExtension(string originalName) + { + ArgumentException.ThrowIfNullOrEmpty(originalName); + OriginalName = originalName; + } + public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion) + { + } +} diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiLegalInfoUrlExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiLegalInfoUrlExtension.cs index 3f042fc70c..10a52bce1a 100644 --- a/src/Kiota.Builder/OpenApiExtensions/OpenApiLegalInfoUrlExtension.cs +++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiLegalInfoUrlExtension.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Any; +using System.Text.Json.Nodes; namespace Kiota.Builder.OpenApiExtensions; @@ -10,7 +10,7 @@ public string? Legal get; set; } protected override string? ValueSelector => Legal; - public static OpenApiLegalInfoUrlExtension Parse(IOpenApiAny source) + public static OpenApiLegalInfoUrlExtension Parse(JsonNode source) { return new OpenApiLegalInfoUrlExtension { diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiLogoExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiLogoExtension.cs index edd5a2daf5..6ddab21631 100644 --- a/src/Kiota.Builder/OpenApiExtensions/OpenApiLogoExtension.cs +++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiLogoExtension.cs @@ -1,7 +1,8 @@ using System; +using System.Text.Json; +using System.Text.Json.Nodes; using Kiota.Builder.Extensions; using Microsoft.OpenApi; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Writers; @@ -16,13 +17,13 @@ public string? Url { get; set; } - public static OpenApiLogoExtension Parse(IOpenApiAny source) + public static OpenApiLogoExtension Parse(JsonNode source) { - if (source is not OpenApiObject rawObject) throw new ArgumentOutOfRangeException(nameof(source)); + if (source is not JsonObject rawObject) throw new ArgumentOutOfRangeException(nameof(source)); var extension = new OpenApiLogoExtension(); - if (rawObject.TryGetValue(nameof(Url).ToFirstCharacterLowerCase(), out var url) && url is OpenApiString urlValue) + if (rawObject.TryGetPropertyValue(nameof(Url).ToFirstCharacterLowerCase(), out var url) && url is JsonValue urlValue && urlValue.GetValueKind() is JsonValueKind.String && urlValue.TryGetValue(out var urlStrValue)) { - extension.Url = urlValue.Value; + extension.Url = urlStrValue; } return extension; } diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiPrivacyPolicyUrlExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiPrivacyPolicyUrlExtension.cs index 4cbc47c72a..039b2f54fe 100644 --- a/src/Kiota.Builder/OpenApiExtensions/OpenApiPrivacyPolicyUrlExtension.cs +++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiPrivacyPolicyUrlExtension.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Any; +using System.Text.Json.Nodes; namespace Kiota.Builder.OpenApiExtensions; @@ -10,7 +10,7 @@ public string? Privacy get; set; } protected override string? ValueSelector => Privacy; - public static OpenApiPrivacyPolicyUrlExtension Parse(IOpenApiAny source) + public static OpenApiPrivacyPolicyUrlExtension Parse(JsonNode source) { return new OpenApiPrivacyPolicyUrlExtension { diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiSimpleStringExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiSimpleStringExtension.cs index 9978939205..53ba859256 100644 --- a/src/Kiota.Builder/OpenApiExtensions/OpenApiSimpleStringExtension.cs +++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiSimpleStringExtension.cs @@ -1,6 +1,7 @@ using System; +using System.Text.Json; +using System.Text.Json.Nodes; using Microsoft.OpenApi; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Writers; @@ -20,9 +21,10 @@ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion) writer.WriteValue(ValueSelector); } } - public static string ParseString(IOpenApiAny source) + public static string ParseString(JsonNode source) { - if (source is not OpenApiString rawString) throw new ArgumentOutOfRangeException(nameof(source)); - return rawString.Value; + if (source is not JsonValue rawString || + rawString.GetValueKind() is not JsonValueKind.String) throw new ArgumentOutOfRangeException(nameof(source)); + return rawString.GetValue(); } } diff --git a/src/Kiota.Builder/Plugins/OpenApiPluginWalker.cs b/src/Kiota.Builder/Plugins/OpenApiPluginWalker.cs index 245dd014ac..4ce8bf152f 100644 --- a/src/Kiota.Builder/Plugins/OpenApiPluginWalker.cs +++ b/src/Kiota.Builder/Plugins/OpenApiPluginWalker.cs @@ -18,6 +18,9 @@ public class OpenApiPluginWalker : OpenApiVisitorBase public override void Visit(IOpenApiExtensible openApiExtensible) { ArgumentNullException.ThrowIfNull(openApiExtensible); + if (openApiExtensible.Extensions is not { Count: > 0 }) + return; + // remove any extensions we do not support foreach (var extension in openApiExtensible.Extensions.Where(static extension => !SupportedExtensions.Contains(extension.Key))) { @@ -31,6 +34,9 @@ public override void Visit(OpenApiResponses response) { ArgumentNullException.ThrowIfNull(response); + if (response.Count < 1) + return; + // Ensure description strings are not empty strings. foreach (var responseItem in response.Where(static res => string.IsNullOrEmpty(res.Value.Description))) { diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index b4aa402fee..63d858edd8 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -14,6 +14,8 @@ using Microsoft.OpenApi.ApiManifest; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Services; using Microsoft.OpenApi.Writers; @@ -59,14 +61,14 @@ public async Task GenerateManifestAsync(CancellationToken cancellationToken = de await using var descriptionStream = File.Create(descriptionFullPath, 4096); await using var fileWriter = new StreamWriter(descriptionStream); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - var descriptionWriter = new OpenApiYamlWriter(fileWriter); + var descriptionWriter = new OpenApiYamlWriter(fileWriter, new() { InlineLocalReferences = true, InlineExternalReferences = true }); var trimmedPluginDocument = GetDocumentWithTrimmedComponentsAndResponses(OAIDocument); PrepareDescriptionForCopilot(trimmedPluginDocument); // trimming a second time to remove any components that are no longer used after the inlining trimmedPluginDocument = GetDocumentWithTrimmedComponentsAndResponses(trimmedPluginDocument); trimmedPluginDocument.Info.Title = trimmedPluginDocument.Info.Title[..^9]; // removing the second ` - Subset` suffix from the title trimmedPluginDocument.SerializeAsV3(descriptionWriter); - descriptionWriter.Flush(); + await descriptionWriter.FlushAsync(cancellationToken).ConfigureAwait(false); // 3. write the plugins @@ -113,11 +115,11 @@ private sealed class MappingCleanupVisitor(OpenApiDocument openApiDocument) : Op { private readonly OpenApiDocument _document = openApiDocument; - public override void Visit(OpenApiSchema schema) + public override void Visit(IOpenApiSchema schema) { if (schema.Discriminator?.Mapping is null) return; - var keysToRemove = schema.Discriminator.Mapping.Where(x => !_document.Components.Schemas.ContainsKey(x.Value.Split('/', StringSplitOptions.RemoveEmptyEntries)[^1])).Select(static x => x.Key).ToArray(); + var keysToRemove = schema.Discriminator.Mapping.Where(x => _document.Components?.Schemas is null || !_document.Components.Schemas.ContainsKey(x.Value.Split('/', StringSplitOptions.RemoveEmptyEntries)[^1])).Select(static x => x.Key).ToArray(); foreach (var key in keysToRemove) schema.Discriminator.Mapping.Remove(key); base.Visit(schema); @@ -126,20 +128,20 @@ public override void Visit(OpenApiSchema schema) private sealed class AllOfPropertiesRetrievalVisitor : OpenApiVisitorBase { - public override void Visit(OpenApiSchema schema) + public override void Visit(IOpenApiSchema schema) { if (schema.AllOf is not { Count: > 0 }) return; var allPropertiesToAdd = GetAllProperties(schema).ToArray(); foreach (var allOfEntry in schema.AllOf) - SelectFirstAnyOneOfVisitor.CopyRelevantInformation(allOfEntry, schema, false, false, false); + SelectFirstAnyOneOfVisitor.CopyRelevantInformation(allOfEntry, schema, false, false); foreach (var (key, value) in allPropertiesToAdd) schema.Properties.TryAdd(key, value); schema.AllOf.Clear(); base.Visit(schema); } - private static IEnumerable> GetAllProperties(OpenApiSchema schema) + private static IEnumerable> GetAllProperties(IOpenApiSchema schema) { return schema.AllOf is not null ? schema.AllOf.SelectMany(static x => GetAllProperties(x)).Union(schema.Properties) : @@ -147,9 +149,49 @@ private static IEnumerable> GetAllProperties } } + private sealed class ReplaceFirstSchemaByReference : OpenApiVisitorBase + { + public override void Visit(OpenApiMediaType mediaType) + { + mediaType.Schema = GetFirstSchema(mediaType.Schema); + base.Visit(mediaType); + } + public override void Visit(IOpenApiParameter parameter) + { + if (parameter is OpenApiParameter openApiParameter) + openApiParameter.Schema = GetFirstSchema(parameter.Schema); + base.Visit(parameter); + } + public override void Visit(IOpenApiHeader header) + { + if (header is OpenApiHeader openApiHeader) + openApiHeader.Schema = GetFirstSchema(header.Schema); + base.Visit(header); + } + public override void Visit(IOpenApiSchema schema) + { + if (schema is OpenApiSchema openApiSchema) + { + openApiSchema.Items = GetFirstSchema(schema.Items); + var properties = new Dictionary(schema.Properties); + foreach (var (key, value) in properties) + schema.Properties[key] = GetFirstSchema(value); + } + base.Visit(schema); + } + private static IOpenApiSchema? GetFirstSchema(IOpenApiSchema? schema) + { + if (schema is null) return null; + if (schema.AnyOf is { Count: > 0 } && schema.AnyOf[0] is OpenApiSchemaReference anyOfSchemaReference) + return anyOfSchemaReference; + if (schema.OneOf is { Count: > 0 } && schema.OneOf[0] is OpenApiSchemaReference oneOfSchemaReference) + return oneOfSchemaReference; + return schema; + } + } private sealed class SelectFirstAnyOneOfVisitor : OpenApiVisitorBase { - public override void Visit(OpenApiSchema schema) + public override void Visit(IOpenApiSchema schema) { if (schema.AnyOf is { Count: > 0 }) { @@ -163,72 +205,71 @@ public override void Visit(OpenApiSchema schema) } base.Visit(schema); } - internal static void CopyRelevantInformation(OpenApiSchema source, OpenApiSchema target, bool includeProperties = true, bool includeReference = true, bool includeDiscriminator = true) + internal static void CopyRelevantInformation(IOpenApiSchema source, IOpenApiSchema target, bool includeProperties = true, bool includeDiscriminator = true) { - if (!string.IsNullOrEmpty(source.Type)) - target.Type = source.Type; - if (!string.IsNullOrEmpty(source.Format)) - target.Format = source.Format; - if (source.Items is not null) - target.Items = source.Items; - if (source.Properties is not null && includeProperties) - target.Properties = new Dictionary(source.Properties); - if (source.Required is not null) - target.Required = new HashSet(source.Required); - if (source.AdditionalProperties is not null) - target.AdditionalProperties = source.AdditionalProperties; - if (source.Enum is not null) - target.Enum = [.. source.Enum]; - if (source.ExclusiveMaximum is not null) - target.ExclusiveMaximum = source.ExclusiveMaximum; - if (source.ExclusiveMinimum is not null) - target.ExclusiveMinimum = source.ExclusiveMinimum; - if (source.Maximum is not null) - target.Maximum = source.Maximum; - if (source.Minimum is not null) - target.Minimum = source.Minimum; - if (source.MaxItems is not null) - target.MaxItems = source.MaxItems; - if (source.MinItems is not null) - target.MinItems = source.MinItems; - if (source.MaxLength is not null) - target.MaxLength = source.MaxLength; - if (source.MinLength is not null) - target.MinLength = source.MinLength; - if (source.Pattern is not null) - target.Pattern = source.Pattern; - if (source.MaxProperties is not null) - target.MaxProperties = source.MaxProperties; - if (source.MinProperties is not null) - target.MinProperties = source.MinProperties; - if (source.UniqueItems is not null) - target.UniqueItems = source.UniqueItems; - if (source.Nullable) - target.Nullable = true; - if (source.ReadOnly) - target.ReadOnly = true; - if (source.WriteOnly) - target.WriteOnly = true; - if (source.Deprecated) - target.Deprecated = true; - if (source.Xml is not null) - target.Xml = source.Xml; - if (source.ExternalDocs is not null) - target.ExternalDocs = source.ExternalDocs; - if (source.Example is not null) - target.Example = source.Example; - if (source.Extensions is not null) - target.Extensions = new Dictionary(source.Extensions); - if (source.Discriminator is not null && includeDiscriminator) - target.Discriminator = source.Discriminator; - if (!string.IsNullOrEmpty(source.Description)) - target.Description = source.Description; - if (!string.IsNullOrEmpty(source.Title)) - target.Title = source.Title; - if (source.Default is not null) - target.Default = source.Default; - if (source.Reference is not null && includeReference) - target.Reference = source.Reference; + if (target is OpenApiSchema openApiSchema) + { + if (source.Type is not null && source.Type.HasValue) + openApiSchema.Type = source.Type; + if (!string.IsNullOrEmpty(source.Format)) + openApiSchema.Format = source.Format; + if (source.Items is not null) + openApiSchema.Items = source.Items; + if (source.Properties is not null && includeProperties) + openApiSchema.Properties = new Dictionary(source.Properties); + if (source.Required is not null) + openApiSchema.Required = new HashSet(source.Required); + if (source.AdditionalProperties is not null) + openApiSchema.AdditionalProperties = source.AdditionalProperties; + if (source.Enum is not null) + openApiSchema.Enum = [.. source.Enum]; + if (source.ExclusiveMaximum is not null) + openApiSchema.ExclusiveMaximum = source.ExclusiveMaximum; + if (source.ExclusiveMinimum is not null) + openApiSchema.ExclusiveMinimum = source.ExclusiveMinimum; + if (source.Maximum is not null) + openApiSchema.Maximum = source.Maximum; + if (source.Minimum is not null) + openApiSchema.Minimum = source.Minimum; + if (source.MaxItems is not null) + openApiSchema.MaxItems = source.MaxItems; + if (source.MinItems is not null) + openApiSchema.MinItems = source.MinItems; + if (source.MaxLength is not null) + openApiSchema.MaxLength = source.MaxLength; + if (source.MinLength is not null) + openApiSchema.MinLength = source.MinLength; + if (source.Pattern is not null) + openApiSchema.Pattern = source.Pattern; + if (source.MaxProperties is not null) + openApiSchema.MaxProperties = source.MaxProperties; + if (source.MinProperties is not null) + openApiSchema.MinProperties = source.MinProperties; + if (source.UniqueItems is not null) + openApiSchema.UniqueItems = source.UniqueItems; + if (source.ReadOnly) + openApiSchema.ReadOnly = true; + if (source.WriteOnly) + openApiSchema.WriteOnly = true; + if (source.Deprecated) + openApiSchema.Deprecated = true; + if (source.Xml is not null) + openApiSchema.Xml = source.Xml; + if (source.ExternalDocs is not null) + openApiSchema.ExternalDocs = source.ExternalDocs; + if (source.Example is not null) + openApiSchema.Example = source.Example; + if (source.Extensions is not null) + openApiSchema.Extensions = new Dictionary(source.Extensions); + if (source.Discriminator is not null && includeDiscriminator) + openApiSchema.Discriminator = source.Discriminator; + if (!string.IsNullOrEmpty(source.Description)) + openApiSchema.Description = source.Description; + if (!string.IsNullOrEmpty(source.Title)) + openApiSchema.Title = source.Title; + if (source.Default is not null) + openApiSchema.Default = source.Default; + } } } @@ -259,10 +300,10 @@ public override void Visit(OpenApiOperation operation) operation.ExternalDocs = null; base.Visit(operation); } - public override void Visit(OpenApiSchema schema) + public override void Visit(IOpenApiSchema schema) { - if (schema.ExternalDocs is not null) - schema.ExternalDocs = null; + if (schema.ExternalDocs is not null && schema is OpenApiSchema openApiSchema) + openApiSchema.ExternalDocs = null; base.Visit(schema); } public override void Visit(OpenApiTag tag) @@ -283,6 +324,10 @@ private static void PrepareDescriptionForCopilot(OpenApiDocument document) var errorResponsesCleanupWalker = new OpenApiWalker(errorResponsesCleanupVisitor); errorResponsesCleanupWalker.Walk(document); + var replaceFirstSchemaByReference = new ReplaceFirstSchemaByReference(); + var replaceFirstSchemaByReferenceWalker = new OpenApiWalker(replaceFirstSchemaByReference); + replaceFirstSchemaByReferenceWalker.Walk(document); + var selectFirstAnyOneOfVisitor = new SelectFirstAnyOneOfVisitor(); var selectFirstAnyOneOfWalker = new OpenApiWalker(selectFirstAnyOneOfVisitor); selectFirstAnyOneOfWalker.Walk(document); @@ -425,7 +470,7 @@ private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimes var auth = configAuth; try { - auth = configAuth ?? GetAuth(operation.Security ?? document.SecurityRequirements); + auth = configAuth ?? GetAuth(operation.Security ?? document.SecurityRequirements ?? []); } catch (UnsupportedSecuritySchemeException e) { @@ -438,7 +483,7 @@ private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimes // Configuration overrides document information Auth = auth, Spec = new OpenApiRuntimeSpec { Url = openApiDocumentPath }, - RunForFunctions = [operation.OperationId] + RunForFunctions = [operation.OperationId!] }); var summary = operation.Summary.CleanupXMLString(); @@ -446,7 +491,7 @@ private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimes functions.Add(new Function { - Name = operation.OperationId, + Name = operation.OperationId!, Description = !string.IsNullOrEmpty(description) ? description : summary, States = GetStatesFromOperation(operation), @@ -483,7 +528,7 @@ private static Auth GetAuth(IList securityRequiremen return (opSecurity is null || opSecurity.UnresolvedReference) ? new AnonymousAuth() : GetAuthFromSecurityScheme(opSecurity); } - private static Auth GetAuthFromSecurityScheme(OpenApiSecurityScheme securityScheme) + private static Auth GetAuthFromSecurityScheme(OpenApiSecuritySchemeReference securityScheme) { string name = securityScheme.Reference.Id; return securityScheme.Type switch @@ -526,7 +571,8 @@ SecuritySchemeType.Http when securityScheme.Scheme.Equals("bearer", StringCompar private static State? GetStateFromExtension(OpenApiOperation openApiOperation, string extensionName, Func> instructionsExtractor) { - if (openApiOperation.Extensions.TryGetValue(extensionName, out var rExtRaw) && + if (openApiOperation.Extensions is not null && + openApiOperation.Extensions.TryGetValue(extensionName, out var rExtRaw) && rExtRaw is T rExt && instructionsExtractor(rExt).Exists(static x => !string.IsNullOrEmpty(x))) { diff --git a/src/Kiota.Builder/Validation/GetWithBody.cs b/src/Kiota.Builder/Validation/GetWithBody.cs index a4f60dee96..dbd6086fe0 100644 --- a/src/Kiota.Builder/Validation/GetWithBody.cs +++ b/src/Kiota.Builder/Validation/GetWithBody.cs @@ -1,9 +1,10 @@ using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Validations; namespace Kiota.Builder.Validation; -public class GetWithBody : ValidationRule +public class GetWithBody : ValidationRule { public GetWithBody() : base(nameof(GetWithBody), static (context, pathItem) => { diff --git a/src/Kiota.Builder/Validation/InconsistentTypeFormatPair.cs b/src/Kiota.Builder/Validation/InconsistentTypeFormatPair.cs index 636bf96689..510333254f 100644 --- a/src/Kiota.Builder/Validation/InconsistentTypeFormatPair.cs +++ b/src/Kiota.Builder/Validation/InconsistentTypeFormatPair.cs @@ -1,15 +1,16 @@ using System; using System.Collections.Generic; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Validations; namespace Kiota.Builder.Validation; -public class InconsistentTypeFormatPair : ValidationRule +public class InconsistentTypeFormatPair : ValidationRule { - private static readonly Dictionary> validPairs = new(StringComparer.OrdinalIgnoreCase) + private static readonly Dictionary> validPairs = new() { - ["string"] = new(StringComparer.OrdinalIgnoreCase) { + [JsonSchemaType.String] = new(StringComparer.OrdinalIgnoreCase) { "commonmark", "html", "date", @@ -21,7 +22,7 @@ public class InconsistentTypeFormatPair : ValidationRule "binary", "byte", }, - ["integer"] = new(StringComparer.OrdinalIgnoreCase) { + [JsonSchemaType.Integer] = new(StringComparer.OrdinalIgnoreCase) { "int32", "int64", "int8", @@ -29,7 +30,7 @@ public class InconsistentTypeFormatPair : ValidationRule "int16", "uint16", }, - ["number"] = new(StringComparer.OrdinalIgnoreCase) { + [JsonSchemaType.Number] = new(StringComparer.OrdinalIgnoreCase) { "float", "double", "decimal", @@ -41,19 +42,17 @@ public class InconsistentTypeFormatPair : ValidationRule "uint16", }, }; - private static readonly HashSet escapedTypes = new(StringComparer.OrdinalIgnoreCase) { - "array", - "boolean", - "const", - "enum", - "null", - "object", - }; + private static readonly HashSet escapedTypes = [ + JsonSchemaType.Array, + JsonSchemaType.Boolean, + JsonSchemaType.Null, + JsonSchemaType.Object, + ]; public InconsistentTypeFormatPair() : base(nameof(InconsistentTypeFormatPair), static (context, schema) => { - if (string.IsNullOrEmpty(schema?.Type) || string.IsNullOrEmpty(schema.Format) || KnownAndNotSupportedFormats.knownAndUnsupportedFormats.Contains(schema.Format) || escapedTypes.Contains(schema.Type)) + if (schema is null || !schema.Type.HasValue || string.IsNullOrEmpty(schema.Format) || KnownAndNotSupportedFormats.knownAndUnsupportedFormats.Contains(schema.Format) || escapedTypes.Contains(schema.Type.Value)) return; - if (!validPairs.TryGetValue(schema.Type, out var validFormats) || !validFormats.Contains(schema.Format)) + if (!validPairs.TryGetValue(schema.Type.Value, out var validFormats) || !validFormats.Contains(schema.Format)) context.CreateWarning(nameof(InconsistentTypeFormatPair), $"The format {schema.Format} is not supported by Kiota for the type {schema.Type} and the string type will be used."); }) { diff --git a/src/Kiota.Builder/Validation/KnownAndNotSupportedFormats.cs b/src/Kiota.Builder/Validation/KnownAndNotSupportedFormats.cs index 4dc4c5922d..36b731b8ad 100644 --- a/src/Kiota.Builder/Validation/KnownAndNotSupportedFormats.cs +++ b/src/Kiota.Builder/Validation/KnownAndNotSupportedFormats.cs @@ -1,12 +1,12 @@  using System; using System.Collections.Generic; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Validations; namespace Kiota.Builder.Validation; -public class KnownAndNotSupportedFormats : ValidationRule +public class KnownAndNotSupportedFormats : ValidationRule { internal static readonly HashSet knownAndUnsupportedFormats = new(StringComparer.OrdinalIgnoreCase) { "email", diff --git a/src/Kiota.Builder/Validation/MissingDiscriminator.cs b/src/Kiota.Builder/Validation/MissingDiscriminator.cs index c889f5cf5a..b544cc5eea 100644 --- a/src/Kiota.Builder/Validation/MissingDiscriminator.cs +++ b/src/Kiota.Builder/Validation/MissingDiscriminator.cs @@ -5,6 +5,7 @@ using Kiota.Builder.Configuration; using Kiota.Builder.Extensions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Validations; namespace Kiota.Builder.Validation; @@ -14,7 +15,7 @@ public MissingDiscriminator(GenerationConfiguration configuration) : base(nameof { var idx = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); document.InitializeInheritanceIndex(idx); - if (document.Components != null) + if (document.Components is { Schemas.Count: > 0 }) Parallel.ForEach(document.Components.Schemas, entry => { ValidateSchema(entry.Value, context, idx, entry.Key); @@ -22,8 +23,8 @@ public MissingDiscriminator(GenerationConfiguration configuration) : base(nameof var inlineSchemasToValidate = document.Paths ?.SelectMany(static x => x.Value.Operations.Values.Select(y => (x.Key, Operation: y))) .SelectMany(x => x.Operation.GetResponseSchemas(OpenApiOperationExtensions.SuccessCodes, configuration.StructuredMimeTypes).Select(y => (x.Key, Schema: y))) - .Where(static x => string.IsNullOrEmpty(x.Schema.Reference?.Id)) - .ToArray() ?? Array.Empty<(string, OpenApiSchema)>(); + .Where(static x => x.Schema is OpenApiSchema) + .ToArray() ?? []; Parallel.ForEach(inlineSchemasToValidate, entry => { ValidateSchema(entry.Schema, context, idx, entry.Key); @@ -31,7 +32,7 @@ public MissingDiscriminator(GenerationConfiguration configuration) : base(nameof }) { } - private static void ValidateSchema(OpenApiSchema schema, IValidationContext context, ConcurrentDictionary> idx, string address) + private static void ValidateSchema(IOpenApiSchema schema, IValidationContext context, ConcurrentDictionary> idx, string address) { if (!schema.IsInclusiveUnion() && !schema.IsExclusiveUnion()) return; diff --git a/src/Kiota.Builder/Validation/MultipleServerEntries.cs b/src/Kiota.Builder/Validation/MultipleServerEntries.cs index 05ff96e5c5..98b3dd2167 100644 --- a/src/Kiota.Builder/Validation/MultipleServerEntries.cs +++ b/src/Kiota.Builder/Validation/MultipleServerEntries.cs @@ -8,7 +8,7 @@ public class MultipleServerEntries : ValidationRule { public MultipleServerEntries() : base(nameof(MultipleServerEntries), static (context, document) => { - if (document.Servers.GroupBy(static x => x.Url, StringComparer.OrdinalIgnoreCase).Count() > 1) + if (document.Servers?.GroupBy(static x => x.Url, StringComparer.OrdinalIgnoreCase).Count() > 1) context.CreateWarning(nameof(MultipleServerEntries), "Multiple servers entries were found in the OpenAPI description. Only the first one will be used. The root URL can be set manually with the request adapter."); }) diff --git a/src/Kiota.Builder/Validation/NoContentWithBody.cs b/src/Kiota.Builder/Validation/NoContentWithBody.cs index 539c4430e7..29b1c5b960 100644 --- a/src/Kiota.Builder/Validation/NoContentWithBody.cs +++ b/src/Kiota.Builder/Validation/NoContentWithBody.cs @@ -8,7 +8,7 @@ public class NoContentWithBody : ValidationRule { public NoContentWithBody() : base(nameof(NoContentWithBody), static (context, operation) => { - if (operation.Responses.TryGetValue("204", out var response) && (response?.Content?.Any() ?? false)) + if (operation.Responses is not null && operation.Responses.TryGetValue("204", out var response) && (response?.Content?.Any() ?? false)) context.CreateWarning(nameof(NoContentWithBody), "A 204 response with a body media type was found. The response body will be ignored."); }) { diff --git a/src/Kiota.Builder/Validation/NoServerEntry.cs b/src/Kiota.Builder/Validation/NoServerEntry.cs index 17513ed4f7..895744b4b5 100644 --- a/src/Kiota.Builder/Validation/NoServerEntry.cs +++ b/src/Kiota.Builder/Validation/NoServerEntry.cs @@ -1,5 +1,4 @@ -using System.Linq; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Validations; namespace Kiota.Builder.Validation; @@ -8,7 +7,7 @@ public class NoServerEntry : ValidationRule { public NoServerEntry() : base(nameof(NoServerEntry), static (context, document) => { - if (!document.Servers.Any() || string.IsNullOrEmpty(document.Servers.First().Url?.TrimEnd('/'))) + if (document.Servers is not { Count: > 0 } || string.IsNullOrEmpty(document.Servers[0].Url?.TrimEnd('/'))) context.CreateWarning(nameof(NoServerEntry), "A servers entry (v3) or host + basePath + schemes properties (v2) was not present in the OpenAPI description. The root URL will need to be set manually with the request adapter."); }) diff --git a/src/Kiota.Builder/Validation/OpenApiSchemaComparer.cs b/src/Kiota.Builder/Validation/OpenApiSchemaComparer.cs index 5b145e9daa..862ef81eb8 100644 --- a/src/Kiota.Builder/Validation/OpenApiSchemaComparer.cs +++ b/src/Kiota.Builder/Validation/OpenApiSchemaComparer.cs @@ -2,49 +2,46 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Text.Json.Nodes; using Kiota.Builder.Extensions; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; namespace Kiota.Builder.Validation; -internal class OpenApiSchemaComparer : IEqualityComparer +internal class OpenApiSchemaComparer : IEqualityComparer { private readonly OpenApiDiscriminatorComparer discriminatorComparer; - private readonly OpenApiAnyComparer openApiAnyComparer; - private readonly KeyValueComparer schemaMapComparer; + private readonly JsonNodeComparer jsonNodeComparer; public OpenApiSchemaComparer( OpenApiDiscriminatorComparer? discriminatorComparer = null, - OpenApiAnyComparer? openApiAnyComparer = null, - KeyValueComparer? schemaMapComparer = null) + JsonNodeComparer? jsonNodeComparer = null) { this.discriminatorComparer = discriminatorComparer ?? new OpenApiDiscriminatorComparer(); - this.openApiAnyComparer = openApiAnyComparer ?? new OpenApiAnyComparer(); - this.schemaMapComparer = schemaMapComparer ?? new KeyValueComparer(StringComparer.Ordinal, this); + this.jsonNodeComparer = jsonNodeComparer ?? new JsonNodeComparer(); } /// - public bool Equals(OpenApiSchema? x, OpenApiSchema? y) + public bool Equals(IOpenApiSchema? x, IOpenApiSchema? y) { // this workaround might result in collisions, however so far this has not been a problem // implemented this way to avoid stack overflow caused by schemas referencing themselves return x == null && y == null || x != null && y != null && GetHashCode(x) == GetHashCode(y); } /// - public int GetHashCode([DisallowNull] OpenApiSchema obj) + public int GetHashCode([DisallowNull] IOpenApiSchema obj) { var hash = new HashCode(); GetHashCodeInternal(obj, [], ref hash); return hash.ToHashCode(); } - private void GetHashCodeInternal([DisallowNull] OpenApiSchema obj, HashSet visitedSchemas, ref HashCode hash) + private void GetHashCodeInternal([DisallowNull] IOpenApiSchema obj, HashSet visitedSchemas, ref HashCode hash) { if (obj is null) return; if (!visitedSchemas.Add(obj)) return; hash.Add(obj.Deprecated); - hash.Add(obj.Nullable); hash.Add(obj.Discriminator, discriminatorComparer); GetHashCodeInternal(obj.AdditionalProperties, visitedSchemas, ref hash); hash.Add(obj.AdditionalPropertiesAllowed); @@ -53,7 +50,7 @@ private void GetHashCodeInternal([DisallowNull] OpenApiSchema obj, HashSet +internal class JsonNodeComparer : IEqualityComparer { /// - public bool Equals(IOpenApiAny? x, IOpenApiAny? y) + public bool Equals(JsonNode? x, JsonNode? y) { if (x is null || y is null) return object.Equals(x, y); // TODO: Can we use the OpenAPI.NET implementation of Equals? - return x.AnyType == y.AnyType && string.Equals(x.ToString(), y.ToString(), StringComparison.OrdinalIgnoreCase); + return x.GetValueKind() == y.GetValueKind() && string.Equals(x.ToJsonString(), y.ToJsonString(), StringComparison.OrdinalIgnoreCase); } /// - public int GetHashCode([DisallowNull] IOpenApiAny obj) + public int GetHashCode([DisallowNull] JsonNode obj) { var hash = new HashCode(); if (obj == null) return hash.ToHashCode(); diff --git a/src/Kiota.Builder/Validation/UrlFormEncodedComplex.cs b/src/Kiota.Builder/Validation/UrlFormEncodedComplex.cs index 78a4b31572..d763e132f5 100644 --- a/src/Kiota.Builder/Validation/UrlFormEncodedComplex.cs +++ b/src/Kiota.Builder/Validation/UrlFormEncodedComplex.cs @@ -3,6 +3,7 @@ using Kiota.Builder.Configuration; using Kiota.Builder.Extensions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Validations; namespace Kiota.Builder.Validation; @@ -13,19 +14,19 @@ public class UrlFormEncodedComplex : ValidationRule }; public UrlFormEncodedComplex() : base(nameof(UrlFormEncodedComplex), static (context, operation) => { - if (operation.GetRequestSchema(validContentTypes) is OpenApiSchema requestSchema) - ValidateSchema(requestSchema, context, operation.OperationId, "request body"); - if (operation.GetResponseSchema(validContentTypes) is OpenApiSchema responseSchema) - ValidateSchema(responseSchema, context, operation.OperationId, "response body"); + if (operation.GetRequestSchema(validContentTypes) is { } requestSchema) + ValidateSchema(requestSchema, context, "request body"); + if (operation.GetResponseSchema(validContentTypes) is { } responseSchema) + ValidateSchema(responseSchema, context, "response body"); }) { } - private static void ValidateSchema(OpenApiSchema schema, IValidationContext context, string operationId, string schemaName) + private static void ValidateSchema(IOpenApiSchema schema, IValidationContext context, string schemaName) { if (schema == null) return; if (!schema.IsObjectType()) - context.CreateWarning(nameof(UrlFormEncodedComplex), $"The operation {operationId} has a {schemaName} which is not an object type. This is not supported by Kiota and serialization will fail."); + context.CreateWarning(nameof(UrlFormEncodedComplex), $"The operation {context.PathString} has a {schemaName} which is not an object type. This is not supported by Kiota and serialization will fail."); if (schema.Properties.Any(static x => x.Value.IsObjectType())) - context.CreateWarning(nameof(UrlFormEncodedComplex), $"The operation {operationId} has a {schemaName} with a complex properties and the url form encoded content type. This is not supported by Kiota and serialization of complex properties will fail."); + context.CreateWarning(nameof(UrlFormEncodedComplex), $"The operation {context.PathString} has a {schemaName} with a complex properties and the url form encoded content type. This is not supported by Kiota and serialization of complex properties will fail."); } } diff --git a/src/Kiota.Builder/Validation/ValidationRuleSetExtensions.cs b/src/Kiota.Builder/Validation/ValidationRuleSetExtensions.cs index c54eb22904..c6cb1b0d65 100644 --- a/src/Kiota.Builder/Validation/ValidationRuleSetExtensions.cs +++ b/src/Kiota.Builder/Validation/ValidationRuleSetExtensions.cs @@ -1,5 +1,7 @@ using System; using Kiota.Builder.Configuration; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Validations; namespace Kiota.Builder.Validation; @@ -13,18 +15,18 @@ public static void AddKiotaValidationRules(this ValidationRuleSet ruleSet, Gener configuration ??= new(); if (configuration.DisabledValidationRules.Contains(AllValidationRule)) return; - ruleSet.AddRuleIfEnabled(configuration, new NoServerEntry()); - ruleSet.AddRuleIfEnabled(configuration, new MultipleServerEntries()); - ruleSet.AddRuleIfEnabled(configuration, new GetWithBody()); - ruleSet.AddRuleIfEnabled(configuration, new KnownAndNotSupportedFormats()); - ruleSet.AddRuleIfEnabled(configuration, new InconsistentTypeFormatPair()); - ruleSet.AddRuleIfEnabled(configuration, new UrlFormEncodedComplex()); - ruleSet.AddRuleIfEnabled(configuration, new DivergentResponseSchema(configuration)); - ruleSet.AddRuleIfEnabled(configuration, new MissingDiscriminator(configuration)); + ruleSet.AddRuleIfEnabled(configuration, new NoServerEntry(), typeof(OpenApiDocument)); + ruleSet.AddRuleIfEnabled(configuration, new MultipleServerEntries(), typeof(OpenApiDocument)); + ruleSet.AddRuleIfEnabled(configuration, new GetWithBody(), typeof(IOpenApiPathItem)); + ruleSet.AddRuleIfEnabled(configuration, new KnownAndNotSupportedFormats(), typeof(IOpenApiSchema)); + ruleSet.AddRuleIfEnabled(configuration, new InconsistentTypeFormatPair(), typeof(IOpenApiSchema)); + ruleSet.AddRuleIfEnabled(configuration, new UrlFormEncodedComplex(), typeof(OpenApiOperation)); + ruleSet.AddRuleIfEnabled(configuration, new DivergentResponseSchema(configuration), typeof(OpenApiOperation)); + ruleSet.AddRuleIfEnabled(configuration, new MissingDiscriminator(configuration), typeof(OpenApiDocument)); } - private static void AddRuleIfEnabled(this ValidationRuleSet ruleSet, GenerationConfiguration configuration, T instance) where T : ValidationRule + private static void AddRuleIfEnabled(this ValidationRuleSet ruleSet, GenerationConfiguration configuration, T instance, Type ruleType) where T : ValidationRule { - if (!configuration.DisabledValidationRules.Contains(instance.GetType().Name)) - ruleSet.Add(instance); + if (!configuration.DisabledValidationRules.Contains(typeof(T).Name)) + ruleSet.Add(ruleType, instance); } } diff --git a/src/Kiota.Builder/Writers/Java/JavaConventionService.cs b/src/Kiota.Builder/Writers/Java/JavaConventionService.cs index 29d4191c63..51e78829e8 100644 --- a/src/Kiota.Builder/Writers/Java/JavaConventionService.cs +++ b/src/Kiota.Builder/Writers/Java/JavaConventionService.cs @@ -86,7 +86,7 @@ public override string TranslateType(CodeType type) "sbyte" => "Short", "decimal" or "Decimal" => "BigDecimal", "void" or "boolean" when !type.IsNullable => type.Name, //little casing hack - "binary" or "base64" or "base64url" or "Base64url" => "byte[]", + "binary" or "Binary" or "base64" or "Base64" or "base64url" or "Base64url" => "byte[]", "Guid" => "UUID", _ when type.Name.Contains('.', StringComparison.OrdinalIgnoreCase) => type.Name, // casing _ => type.Name is string typeName && !string.IsNullOrEmpty(typeName) ? typeName : "Object", diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs index 4608d707aa..96244dee1c 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs @@ -330,9 +330,9 @@ private string GetSerializerFunctionName(CodeElement codeElement, CodeType retur private string FindFunctionInNameSpace(string functionName, CodeElement codeElement, CodeType returnType) { - var myNamespace = returnType.TypeDefinition!.GetImmediateParentOfType(); + var myNamespace = returnType.TypeDefinition?.GetImmediateParentOfType() ?? throw new InvalidOperationException("Namespace not found for return type"); - CodeFunction[] codeFunctions = myNamespace.FindChildrenByName(functionName).ToArray(); + CodeFunction[] codeFunctions = [.. myNamespace.FindChildrenByName(functionName)]; var codeFunction = Array.Find(codeFunctions, func => func.GetImmediateParentOfType().Name == myNamespace.Name) ?? diff --git a/src/kiota/kiota.csproj b/src/kiota/kiota.csproj index 23641c06cb..2ca8b2d58a 100644 --- a/src/kiota/kiota.csproj +++ b/src/kiota/kiota.csproj @@ -46,8 +46,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Kiota.Builder.Tests/ContentTypeMappingTests.cs b/tests/Kiota.Builder.Tests/ContentTypeMappingTests.cs index 07cd3bd7c4..159db34f5c 100644 --- a/tests/Kiota.Builder.Tests/ContentTypeMappingTests.cs +++ b/tests/Kiota.Builder.Tests/ContentTypeMappingTests.cs @@ -8,6 +8,8 @@ using Kiota.Builder.Extensions; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Models.References; using Moq; using Xunit; @@ -81,23 +83,6 @@ public void Dispose() [Theory] public void GeneratesTheRightReturnTypeBasedOnContentAndStatus(string contentType, string statusCode, bool addModel, string acceptedContentType, string returnType) { - var myObjectSchema = new OpenApiSchema - { - Type = "object", - Properties = new Dictionary { - { - "id", new OpenApiSchema { - Type = "string", - } - } - }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false - }; var document = new OpenApiDocument { Paths = new OpenApiPaths @@ -112,7 +97,7 @@ public void GeneratesTheRightReturnTypeBasedOnContentAndStatus(string contentTyp [statusCode] = new OpenApiResponse { Content = { [contentType] = new OpenApiMediaType { - Schema = addModel ? myObjectSchema : null + Schema = addModel ? new OpenApiSchemaReference("myobject") : null } } }, @@ -123,13 +108,25 @@ public void GeneratesTheRightReturnTypeBasedOnContentAndStatus(string contentTyp }, Components = new() { - Schemas = new Dictionary { + Schemas = new Dictionary { { - "myobject", myObjectSchema + "myobject", new OpenApiSchema + { + Type = JsonSchemaType.Object, + Properties = new Dictionary { + { + "id", new OpenApiSchema { + Type = JsonSchemaType.String, + } + } + }, + } } } } }; + document.RegisterComponents(); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder( mockLogger.Object, @@ -175,23 +172,6 @@ public void GeneratesTheRightReturnTypeBasedOnContentAndStatus(string contentTyp [Theory] public void GeneratesTheRightParameterTypeBasedOnContentAndStatus(string contentType, bool addModel, string acceptedContentType, string parameterType) { - var myObjectSchema = new OpenApiSchema - { - Type = "object", - Properties = new Dictionary { - { - "id", new OpenApiSchema { - Type = "string", - } - } - }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false - }; var document = new OpenApiDocument { Paths = new OpenApiPaths @@ -204,7 +184,7 @@ public void GeneratesTheRightParameterTypeBasedOnContentAndStatus(string content RequestBody = new OpenApiRequestBody { Content = { [contentType] = new OpenApiMediaType { - Schema = addModel ? myObjectSchema : null + Schema = addModel ? new OpenApiSchemaReference("myobject") : null } } }, @@ -218,13 +198,25 @@ public void GeneratesTheRightParameterTypeBasedOnContentAndStatus(string content }, Components = new() { - Schemas = new Dictionary { + Schemas = new Dictionary { { - "myobject", myObjectSchema + "myobject", new OpenApiSchema + { + Type = JsonSchemaType.Object, + Properties = new Dictionary { + { + "id", new OpenApiSchema { + Type = JsonSchemaType.String, + } + } + }, + } } } } }; + document.RegisterComponents(); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder( mockLogger.Object, @@ -268,22 +260,7 @@ public void GeneratesTheRightAcceptHeaderBasedOnContentAndStatus(string contentM { ["200"] = new OpenApiResponse { Content = contentMediaTypes.Split(',').Select(x => new {Key = x.Trim(), value = new OpenApiMediaType { - Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { - { - "id", new OpenApiSchema { - Type = "string", - } - } - }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false - } + Schema = new OpenApiSchemaReference("myobject"), } }).ToDictionary(x => x.Key, x => x.value) }, @@ -294,28 +271,24 @@ public void GeneratesTheRightAcceptHeaderBasedOnContentAndStatus(string contentM }, Components = new() { - Schemas = new Dictionary { + Schemas = new Dictionary { { "myobject", new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false } } } } }; + document.RegisterComponents(); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder( mockLogger.Object, @@ -359,22 +332,7 @@ public void GeneratesTheRightContentTypeHeaderBasedOnContentAndStatus(string con RequestBody = new OpenApiRequestBody { Content = contentMediaTypes.Split(',').Select(x => new {Key = x.Trim(), value = new OpenApiMediaType { - Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { - { - "id", new OpenApiSchema { - Type = "string", - } - } - }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false - } + Schema = new OpenApiSchemaReference("myobject"), } }).ToDictionary(x => x.Key, x => x.value) }, @@ -384,28 +342,24 @@ public void GeneratesTheRightContentTypeHeaderBasedOnContentAndStatus(string con }, Components = new() { - Schemas = new Dictionary { + Schemas = new Dictionary { { "myobject", new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false } } } } }; + document.RegisterComponents(); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder( mockLogger.Object, diff --git a/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs b/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs index 080e14f6a4..d56217ccc5 100644 --- a/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs @@ -73,7 +73,7 @@ public void Defensive() Assert.Throws(() => new PublicApiExportService(null)); } - private static readonly Dictionary> Validators = new() + private static readonly Dictionary>> Validators = new() { { GenerationLanguage.CSharp, ValidateExportCSharp }, { GenerationLanguage.Go, ValidateExportGo }, @@ -122,7 +122,7 @@ public async Task GeneratesExportsAndFileHasExpectedAssertionsAsync(GenerationLa Assert.NotEqual(0, outputStream.Length); // output is not empty using var streamReader = new StreamReader(outputStream); - var contents = (await streamReader.ReadToEndAsync()).Split(Environment.NewLine); + var contents = new HashSet((await streamReader.ReadToEndAsync()).Split(Environment.NewLine), StringComparer.Ordinal); if (!Validators.TryGetValue(generationLanguage, out var validator)) { @@ -132,7 +132,7 @@ public async Task GeneratesExportsAndFileHasExpectedAssertionsAsync(GenerationLa validator.Invoke(contents); } - private static void ValidateExportCSharp(string[] exportContents) + private static void ValidateExportCSharp(HashSet exportContents) { Assert.NotEmpty(exportContents); Assert.Contains("ExportNamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance @@ -145,7 +145,7 @@ private static void ValidateExportCSharp(string[] exportContents) Assert.Contains("ExportNamespace.Models.Microsoft.Graph.user::|public|OtherNames:List", exportContents);// captures collection info in language specific format } - private static void ValidateExportJava(string[] exportContents) + private static void ValidateExportJava(HashSet exportContents) { Assert.NotEmpty(exportContents); Assert.Contains("exportnamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance @@ -160,7 +160,7 @@ private static void ValidateExportJava(string[] exportContents) Assert.Contains("exportnamespace.models.microsoft.graph.User::|public|setOtherNames(value?:java.util.List):void", exportContents);// captures collection info in language specific format } - private static void ValidateExportGo(string[] exportContents) + private static void ValidateExportGo(HashSet exportContents) { Assert.NotEmpty(exportContents); Assert.Contains("exportNamespace.Graph-->*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.BaseRequestBuilder", exportContents); // captures class inheritance @@ -178,7 +178,7 @@ private static void ValidateExportGo(string[] exportContents) Assert.Contains("exportNamespace.models.microsoft.graph.user::|public|SetOtherNames(value:[]string):void", exportContents);// captures collection info in language specific format } - private static void ValidateExportPython(string[] exportContents) + private static void ValidateExportPython(HashSet exportContents) { Assert.NotEmpty(exportContents); Assert.Contains("exportNamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance @@ -193,7 +193,7 @@ private static void ValidateExportPython(string[] exportContents) Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|other_names(value:list[str]):None", exportContents);// captures collection info in language specific format } - private static void ValidateExportTypeScript(string[] exportContents) + private static void ValidateExportTypeScript(HashSet exportContents) { Assert.NotEmpty(exportContents); Assert.Contains("exportNamespace.Graph~~>BaseRequestBuilder", exportContents); // captures class inheritance. TS does not do inheritance due to interfaces. @@ -210,7 +210,7 @@ private static void ValidateExportTypeScript(string[] exportContents) Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|otherNames:string[]", exportContents);// captures collection info in language specific format } - private static void ValidateExportPhp(string[] exportContents) + private static void ValidateExportPhp(HashSet exportContents) { Assert.NotEmpty(exportContents); Assert.Contains("exportNamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance diff --git a/tests/Kiota.Builder.Tests/Extensions/OpenApiDeprecationExtensionExtensionsTests.cs b/tests/Kiota.Builder.Tests/Extensions/OpenApiDeprecationExtensionExtensionsTests.cs index 10de8508c1..41711fad5c 100644 --- a/tests/Kiota.Builder.Tests/Extensions/OpenApiDeprecationExtensionExtensionsTests.cs +++ b/tests/Kiota.Builder.Tests/Extensions/OpenApiDeprecationExtensionExtensionsTests.cs @@ -6,6 +6,7 @@ using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.MicrosoftExtensions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Services; using Xunit; @@ -192,6 +193,21 @@ public void GetsDeprecationOnOperationWithDeprecatedInlineResponseSchema() [Fact] public void GetsNoDeprecationOnOperationWithDeprecatedReferenceResponseSchema() { + var schema = new OpenApiSchema + { + Deprecated = true, + Extensions = new Dictionary + { + { OpenApiDeprecationExtension.Name, new OpenApiDeprecationExtension { + Description = "description", + Version = "version", + RemovalDate = new DateTimeOffset(2023, 05, 04, 0, 0, 0, TimeSpan.Zero), + Date = new DateTimeOffset(2021, 05, 04, 0, 0, 0, TimeSpan.Zero), + } } + } + }; + var document = new OpenApiDocument(); + document.AddComponent("schema", schema); var operation = new OpenApiOperation { Deprecated = false, @@ -204,24 +220,7 @@ public void GetsNoDeprecationOnOperationWithDeprecatedReferenceResponseSchema() { { "application/json", new OpenApiMediaType { - Schema = new OpenApiSchema - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "someSchema" - }, - Deprecated = true, - Extensions = new Dictionary - { - { OpenApiDeprecationExtension.Name, new OpenApiDeprecationExtension { - Description = "description", - Version = "version", - RemovalDate = new DateTimeOffset(2023, 05, 04, 0, 0, 0, TimeSpan.Zero), - Date = new DateTimeOffset(2021, 05, 04, 0, 0, 0, TimeSpan.Zero), - } } - } - } + Schema = new OpenApiSchemaReference("schema", document) } } } @@ -292,6 +291,21 @@ public void GetsDeprecationOnOperationWithNullRequestBodyContentTypeInstance() [Fact] public void GetsNoDeprecationOnOperationWithDeprecatedReferenceRequestSchema() { + var schema = new OpenApiSchema + { + Deprecated = true, + Extensions = new Dictionary + { + { OpenApiDeprecationExtension.Name, new OpenApiDeprecationExtension { + Description = "description", + Version = "version", + RemovalDate = new DateTimeOffset(2023, 05, 04, 0, 0, 0, TimeSpan.Zero), + Date = new DateTimeOffset(2021, 05, 04, 0, 0, 0, TimeSpan.Zero), + } } + } + }; + var document = new OpenApiDocument(); + document.AddComponent("schema", schema); var operation = new OpenApiOperation { Deprecated = false, @@ -301,24 +315,7 @@ public void GetsNoDeprecationOnOperationWithDeprecatedReferenceRequestSchema() { { "application/json", new OpenApiMediaType { - Schema = new OpenApiSchema - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "someSchema" - }, - Deprecated = true, - Extensions = new Dictionary - { - { OpenApiDeprecationExtension.Name, new OpenApiDeprecationExtension { - Description = "description", - Version = "version", - RemovalDate = new DateTimeOffset(2023, 05, 04, 0, 0, 0, TimeSpan.Zero), - Date = new DateTimeOffset(2021, 05, 04, 0, 0, 0, TimeSpan.Zero), - } } - } - } + Schema = new OpenApiSchemaReference("schema", document) } } } @@ -399,28 +396,26 @@ public void GetsDeprecationInformationOnParameterWithDeprecatedInlineSchema() [Fact] public void GetsNoDeprecationInformationOnParameterWithDeprecatedReferenceSchema() { - var parameter = new OpenApiParameter + var schema = new OpenApiSchema { - Deprecated = false, - Schema = new OpenApiSchema + Deprecated = true, + Extensions = new Dictionary { - Reference = new OpenApiReference - { - Id = "id", - Type = ReferenceType.Schema - }, - Deprecated = true, - Extensions = new Dictionary - { - { OpenApiDeprecationExtension.Name, new OpenApiDeprecationExtension { - Description = "description", - Version = "version", - RemovalDate = new DateTimeOffset(2023, 05, 04, 0, 0, 0, TimeSpan.Zero), - Date = new DateTimeOffset(2021, 05, 04, 0, 0, 0, TimeSpan.Zero), - } } - } + { OpenApiDeprecationExtension.Name, new OpenApiDeprecationExtension { + Description = "description", + Version = "version", + RemovalDate = new DateTimeOffset(2023, 05, 04, 0, 0, 0, TimeSpan.Zero), + Date = new DateTimeOffset(2021, 05, 04, 0, 0, 0, TimeSpan.Zero), + } } } }; + var document = new OpenApiDocument(); + document.AddComponent("schema", schema); + var parameter = new OpenApiParameter + { + Deprecated = false, + Schema = new OpenApiSchemaReference("schema", document) + }; var deprecationInformation = parameter.GetDeprecationInformation(); Assert.NotNull(deprecationInformation); Assert.False(deprecationInformation.IsDeprecated); @@ -460,30 +455,28 @@ public void GetsDeprecationInformationOnParameterWithDeprecatedInlineContentSche [Fact] public void GetsNoDeprecationInformationOnParameterWithDeprecatedReferenceContentSchema() { + var schema = new OpenApiSchema + { + Deprecated = true, + Extensions = new Dictionary + { + { OpenApiDeprecationExtension.Name, new OpenApiDeprecationExtension { + Description = "description", + Version = "version", + RemovalDate = new DateTimeOffset(2023, 05, 04, 0, 0, 0, TimeSpan.Zero), + Date = new DateTimeOffset(2021, 05, 04, 0, 0, 0, TimeSpan.Zero), + } } + } + }; + var document = new OpenApiDocument(); + document.AddComponent("schema", schema); var parameter = new OpenApiParameter { Deprecated = false, Content = new Dictionary() { { "application/json", new OpenApiMediaType() { - Schema = new OpenApiSchema - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "id" - }, - Deprecated = true, - Extensions = new Dictionary - { - { OpenApiDeprecationExtension.Name, new OpenApiDeprecationExtension { - Description = "description", - Version = "version", - RemovalDate = new DateTimeOffset(2023, 05, 04, 0, 0, 0, TimeSpan.Zero), - Date = new DateTimeOffset(2021, 05, 04, 0, 0, 0, TimeSpan.Zero), - } } - } - } + Schema = new OpenApiSchemaReference("schema", document) } } } diff --git a/tests/Kiota.Builder.Tests/Extensions/OpenApiOperationExtensionsTests.cs b/tests/Kiota.Builder.Tests/Extensions/OpenApiOperationExtensionsTests.cs index 79114497b6..a1499e0b6e 100644 --- a/tests/Kiota.Builder.Tests/Extensions/OpenApiOperationExtensionsTests.cs +++ b/tests/Kiota.Builder.Tests/Extensions/OpenApiOperationExtensionsTests.cs @@ -16,10 +16,10 @@ public void GetsResponseSchema() var operation = new OpenApiOperation { Responses = new() { - { "200", new() { + { "200", new OpenApiResponse() { Content = new Dictionary { {"application/json", new() { - Schema = new() + Schema = new OpenApiSchema() }} } }} @@ -28,10 +28,10 @@ public void GetsResponseSchema() var operation2 = new OpenApiOperation { Responses = new() { - { "400", new() { + { "400", new OpenApiResponse() { Content = new Dictionary { {"application/json", new() { - Schema = new() + Schema = new OpenApiSchema() }} } }} @@ -40,10 +40,10 @@ public void GetsResponseSchema() var operation3 = new OpenApiOperation { Responses = new() { - { "200", new() { + { "200", new OpenApiResponse() { Content = new Dictionary { {"application/invalid", new() { - Schema = new() + Schema = new OpenApiSchema() }} } }} diff --git a/tests/Kiota.Builder.Tests/Extensions/OpenApiReferenceExtensionsTests.cs b/tests/Kiota.Builder.Tests/Extensions/OpenApiReferenceExtensionsTests.cs deleted file mode 100644 index 107cbe325d..0000000000 --- a/tests/Kiota.Builder.Tests/Extensions/OpenApiReferenceExtensionsTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Kiota.Builder.Extensions; - -using Microsoft.OpenApi.Models; - -using Xunit; - -namespace Kiota.Builder.Tests.Extensions; -public class OpenApiReferenceExtensionsTests -{ - [Fact] - public void GetsClassName() - { - var reference = new OpenApiReference - { - Id = "microsoft.graph.user" - }; - Assert.Equal("user", reference.GetClassName()); - } - [Fact] - public void GetsClassNameDefensive() - { - var reference = new OpenApiReference(); - Assert.Empty(reference.GetClassName()); - } -} diff --git a/tests/Kiota.Builder.Tests/Extensions/OpenApiSchemaExtensionsTests.cs b/tests/Kiota.Builder.Tests/Extensions/OpenApiSchemaExtensionsTests.cs index db1ff23416..dd5edbd963 100644 --- a/tests/Kiota.Builder.Tests/Extensions/OpenApiSchemaExtensionsTests.cs +++ b/tests/Kiota.Builder.Tests/Extensions/OpenApiSchemaExtensionsTests.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; - +using System.Text.Json.Nodes; using Kiota.Builder.Extensions; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; - +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Models.References; using Xunit; namespace Kiota.Builder.Tests.Extensions; @@ -48,9 +48,9 @@ public void Defensive() Assert.False(OpenApiSchemaExtensions.IsReferencedSchema(null)); Assert.Null(OpenApiSchemaExtensions.MergeIntersectionSchemaEntries(null)); - Assert.False(new OpenApiSchema { Reference = null }.IsReferencedSchema()); - Assert.False(new OpenApiSchema { Type = null }.IsArray()); - Assert.False(new OpenApiSchema { Type = null }.IsObjectType()); + Assert.False(new OpenApiSchema { }.IsReferencedSchema()); + Assert.False(new OpenApiSchema { Type = JsonSchemaType.Null }.IsArray()); + Assert.False(new OpenApiSchema { Type = JsonSchemaType.Null }.IsObjectType()); Assert.False(new OpenApiSchema { AnyOf = null }.IsInclusiveUnion()); Assert.False(new OpenApiSchema { AllOf = null }.IsInherited()); Assert.False(new OpenApiSchema { AllOf = null }.IsIntersection()); @@ -61,28 +61,37 @@ public void Defensive() } [Fact] - public void ExternalReferencesAreNotSupported() + public void IsExclusiveUnionMatchesTypeArrays() { - var mockSchema = new OpenApiSchema + Assert.True(new OpenApiSchema { - Reference = new OpenApiReference - { - Id = "example.json#/path/to/component", - ExternalResource = "http://example.com/example.json", - }, - }; - Assert.Throws(() => mockSchema.IsReferencedSchema()); + Type = JsonSchemaType.String | JsonSchemaType.Number + }.IsExclusiveUnion()); + Assert.True(new OpenApiSchema + { + Type = JsonSchemaType.String | JsonSchemaType.Number | JsonSchemaType.Null + }.IsExclusiveUnion()); + Assert.False(new OpenApiSchema + { + Type = JsonSchemaType.Number | JsonSchemaType.Null + }.IsExclusiveUnion()); + } + [Fact] + public void ExternalReferencesAreSupported() + { + var mockSchema = new OpenApiSchemaReference("example.json#/path/to/component", null, "http://example.com/example.json"); + Assert.True(mockSchema.IsReferencedSchema()); + } + [Fact] + public void SchemasAreNotConsideredReferences() + { + var mockSchema = new OpenApiSchema(); + Assert.False(mockSchema.IsReferencedSchema()); } [Fact] public void LocalReferencesAreSupported() { - var mockSchema = new OpenApiSchema - { - Reference = new OpenApiReference - { - Id = "#/path/to/component", - }, - }; + var mockSchema = new OpenApiSchemaReference("#/path/to/component"); Assert.True(mockSchema.IsReferencedSchema()); } [Fact] @@ -91,11 +100,11 @@ public void GetSchemaNameAllOfTitleEmpty() var schema = new OpenApiSchema { AllOf = [ - new() + new OpenApiSchema() { Title = "microsoft.graph.entity" }, - new() + new OpenApiSchema() { Title = "microsoft.graph.user" } @@ -111,20 +120,8 @@ public void GetSchemaNameAllOfReference() var schema = new OpenApiSchema { AllOf = [ - new() - { - Reference = new() - { - Id = "microsoft.graph.entity" - } - }, - new() - { - Reference = new() - { - Id = "microsoft.graph.user" - } - } + new OpenApiSchemaReference("microsoft.graph.entity"), + new OpenApiSchemaReference("microsoft.graph.user"), ] }; var names = schema.GetSchemaNames(); @@ -138,14 +135,14 @@ public void GetSchemaNameAllOfNestedTitleEmpty() var schema = new OpenApiSchema { AllOf = [ - new() + new OpenApiSchema() { AllOf = [ - new() + new OpenApiSchema() { Title = "microsoft.graph.entity" }, - new() + new OpenApiSchema() { Title = "microsoft.graph.user" } @@ -163,23 +160,11 @@ public void GetSchemaNameAllOfNestedReference() var schema = new OpenApiSchema { AllOf = [ - new() + new OpenApiSchema() { AllOf = [ - new() - { - Reference = new() - { - Id = "microsoft.graph.entity" - } - }, - new() - { - Reference = new() - { - Id = "microsoft.graph.user" - } - } + new OpenApiSchemaReference("microsoft.graph.entity"), + new OpenApiSchemaReference("microsoft.graph.user"), ] } ] @@ -195,11 +180,11 @@ public void GetSchemaNameAnyOfTitleEmpty() var schema = new OpenApiSchema { AnyOf = [ - new() + new OpenApiSchema() { Title = "microsoft.graph.entity" }, - new() + new OpenApiSchema() { Title = "microsoft.graph.user" } @@ -215,21 +200,9 @@ public void GetSchemaNameAnyOfReference() var schema = new OpenApiSchema { AnyOf = [ - new() - { - Reference = new() - { - Id = "microsoft.graph.entity" - } - }, - new() - { - Reference = new() - { - Id = "microsoft.graph.user" - } - } - ] + new OpenApiSchemaReference("microsoft.graph.entity"), + new OpenApiSchemaReference("microsoft.graph.user"), + ] }; var names = schema.GetSchemaNames(); Assert.Contains("entity", names); @@ -242,11 +215,11 @@ public void GetSchemaNameOneOfTitleEmpty() var schema = new OpenApiSchema { OneOf = [ - new() + new OpenApiSchema() { Title = "microsoft.graph.entity" }, - new() + new OpenApiSchema() { Title = "microsoft.graph.user" } @@ -262,21 +235,9 @@ public void GetSchemaNameOneOfReference() var schema = new OpenApiSchema { OneOf = [ - new() - { - Reference = new() - { - Id = "microsoft.graph.entity" - } - }, - new() - { - Reference = new() - { - Id = "microsoft.graph.user" - } - } - ] + new OpenApiSchemaReference("microsoft.graph.entity"), + new OpenApiSchemaReference("microsoft.graph.user"), + ] }; var names = schema.GetSchemaNames(); Assert.Contains("entity", names); @@ -288,7 +249,7 @@ public void GetSchemaNameItemsTitleEmpty() { var schema = new OpenApiSchema { - Items = new() + Items = new OpenApiSchema() { Title = "microsoft.graph.entity" }, @@ -302,13 +263,7 @@ public void GetSchemaNameItemsReference() { var schema = new OpenApiSchema { - Items = new() - { - Reference = new() - { - Id = "microsoft.graph.entity" - } - }, + Items = new OpenApiSchemaReference("microsoft.graph.entity") }; var names = schema.GetSchemaNames(); Assert.Contains("entity", names); @@ -329,13 +284,7 @@ public void GetSchemaNameTitleEmpty() [Fact] public void GetSchemaNameReference() { - var schema = new OpenApiSchema - { - Reference = new() - { - Id = "microsoft.graph.entity" - } - }; + var schema = new OpenApiSchemaReference("microsoft.graph.entity"); var names = schema.GetSchemaNames(); Assert.Contains("entity", names); Assert.Equal("entity", schema.GetSchemaName()); @@ -354,18 +303,10 @@ public void GetReferenceIdsAllOf() { var schema = new OpenApiSchema { - AllOf = new List { - new() { - Reference = new() { - Id = "microsoft.graph.entity" - } - }, - new() { - Reference = new() { - Id = "microsoft.graph.user" - } - } - } + AllOf = [ + new OpenApiSchemaReference("microsoft.graph.entity"), + new OpenApiSchemaReference("microsoft.graph.user") + ] }; var names = schema.GetSchemaReferenceIds(); Assert.Contains("microsoft.graph.entity", names); @@ -376,22 +317,14 @@ public void GetReferenceIdsAllOfNested() { var schema = new OpenApiSchema { - AllOf = new List { - new() { - AllOf = new List { - new() { - Reference = new() { - Id = "microsoft.graph.entity" - } - }, - new() { - Reference = new() { - Id = "microsoft.graph.user" - } - } - } + AllOf = [ + new OpenApiSchema() { + AllOf = [ + new OpenApiSchemaReference("microsoft.graph.entity"), + new OpenApiSchemaReference("microsoft.graph.user") + ] } - } + ] }; var names = schema.GetSchemaReferenceIds(); Assert.Contains("microsoft.graph.entity", names); @@ -402,18 +335,10 @@ public void GetReferenceIdsAnyOf() { var schema = new OpenApiSchema { - AnyOf = new List { - new() { - Reference = new() { - Id = "microsoft.graph.entity" - } - }, - new() { - Reference = new() { - Id = "microsoft.graph.user" - } - } - } + AnyOf = [ + new OpenApiSchemaReference("microsoft.graph.entity"), + new OpenApiSchemaReference("microsoft.graph.user") + ] }; var names = schema.GetSchemaReferenceIds(); Assert.Contains("microsoft.graph.entity", names); @@ -424,18 +349,10 @@ public void GetReferenceIdsOneOf() { var schema = new OpenApiSchema { - OneOf = new List { - new() { - Reference = new() { - Id = "microsoft.graph.entity" - } - }, - new() { - Reference = new() { - Id = "microsoft.graph.user" - } - } - } + OneOf = [ + new OpenApiSchemaReference("microsoft.graph.entity"), + new OpenApiSchemaReference("microsoft.graph.user") + ] }; var names = schema.GetSchemaReferenceIds(); Assert.Contains("microsoft.graph.entity", names); @@ -446,13 +363,7 @@ public void GetReferenceIdsItems() { var schema = new OpenApiSchema { - Items = new() - { - Reference = new() - { - Id = "microsoft.graph.entity" - } - }, + Items = new OpenApiSchemaReference("microsoft.graph.entity"), }; var names = schema.GetSchemaReferenceIds(); Assert.Contains("microsoft.graph.entity", names); @@ -461,13 +372,7 @@ public void GetReferenceIdsItems() [Fact] public void GetReferenceIdsTitle() { - var schema = new OpenApiSchema - { - Reference = new() - { - Id = "microsoft.graph.entity" - } - }; + var schema = new OpenApiSchemaReference("microsoft.graph.entity"); var names = schema.GetSchemaReferenceIds(); Assert.Contains("microsoft.graph.entity", names); Assert.Single(names); @@ -484,23 +389,15 @@ public void IsInherited() { var schema = new OpenApiSchema { - AllOf = new List { - new() { - Type = "object", - Reference = new() { - Id = "microsoft.graph.entity" - }, - Properties = new Dictionary() { - ["id"] = new OpenApiSchema() - } - }, - new() { - Type = "object", - Properties = new Dictionary() { + AllOf = [ + new OpenApiSchemaReference("microsoft.graph.entity"), + new OpenApiSchema() { + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["firstName"] = new OpenApiSchema() } } - } + ] }; Assert.True(schema.IsInherited()); Assert.False(schema.IsIntersection()); @@ -508,91 +405,98 @@ public void IsInherited() [Fact] public void IsIntersection() { - var schema = new OpenApiSchema + var document = new OpenApiDocument() { - AllOf = new List { - new() { - Type = "object", - Reference = new() { - Id = "microsoft.graph.entity" - }, - Properties = new Dictionary() { - ["id"] = new OpenApiSchema() - } - }, - new() { - Type = "object", - Reference = new() { - Id = "microsoft.graph.user" + Components = new() + { + Schemas = new Dictionary + { + ["microsoft.graph.entity"] = new OpenApiSchema + { + Type = JsonSchemaType.Object, + Properties = new Dictionary() + { + ["id"] = new OpenApiSchema() + } }, - Properties = new Dictionary() { - ["firstName"] = new OpenApiSchema() + ["microsoft.graph.user"] = new OpenApiSchema + { + Type = JsonSchemaType.Object, + Properties = new Dictionary() + { + ["firstName"] = new OpenApiSchema() + } } } } }; + document.RegisterComponents(); + var schema = new OpenApiSchema + { + AllOf = [ + new OpenApiSchemaReference("microsoft.graph.entity", document), + new OpenApiSchemaReference("microsoft.graph.user", document), + ] + }; Assert.False(schema.IsInherited()); Assert.True(schema.IsIntersection()); schema = new OpenApiSchema { - AllOf = new List { - new() { - Type = "object", - Properties = new Dictionary() { + AllOf = [ + new OpenApiSchema() { + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["id"] = new OpenApiSchema() } }, - new() { - Type = "object", - Properties = new Dictionary() { + new OpenApiSchema() { + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["firstName"] = new OpenApiSchema() } } - } + ] }; Assert.False(schema.IsInherited()); Assert.True(schema.IsIntersection()); schema = new OpenApiSchema { - AllOf = new List { - new() { - Type = "object", - Properties = new Dictionary() { + AllOf = [ + new OpenApiSchema() { + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["id"] = new OpenApiSchema() } } - } + ] }; Assert.False(schema.IsInherited()); Assert.False(schema.IsIntersection()); + var userIdSchema = new OpenApiSchema + { + Title = "UserId", + Description = "unique identifier", + Type = JsonSchemaType.String, + Pattern = "^[1-9][0-9]*$", + Example = "1323232", + }; + var tmpDocument = new OpenApiDocument(); + tmpDocument.AddComponent("UserId", userIdSchema); schema = new OpenApiSchema { Title = "Trader Id", - AllOf = new List { - new () - { - Title = "UserId", - Description = "unique identifier", - Type = "string", - Pattern = "^[1-9][0-9]*$", - Example = new OpenApiString("1323232"), - Reference = new OpenApiReference - { - Id = "UserId" // This property makes the schema "meaningful" - } - } - }, - Reference = new OpenApiReference - { - Id = "TraderId" // This property makes the schema "meaningful" - } + AllOf = [ + new OpenApiSchemaReference("UserId", tmpDocument),// This property makes the schema "meaningful" + ], }; + tmpDocument.AddComponent("TraderId", schema); + var schemaReference = new OpenApiSchemaReference("TraderId", tmpDocument); // This property makes the schema "meaningful" - Assert.False(schema.IsInherited()); - Assert.False(schema.IsIntersection()); + Assert.False(schemaReference.IsInherited()); + Assert.False(schemaReference.IsIntersection()); } [Fact] public void MergesIntersection() @@ -601,26 +505,20 @@ public void MergesIntersection() { Description = "description", Deprecated = true, - AllOf = new List { - new() { - Type = "object", - Reference = new() { - Id = "microsoft.graph.entity" - }, - Properties = new Dictionary() { + AllOf = [ + new OpenApiSchema() { + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["id"] = new OpenApiSchema() } }, - new() { - Type = "object", - Reference = new() { - Id = "microsoft.graph.user" - }, - Properties = new Dictionary() { + new OpenApiSchema() { + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["firstName"] = new OpenApiSchema() } } - } + ] }; var result = schema.MergeIntersectionSchemaEntries(); Assert.False(schema.IsInherited()); @@ -637,32 +535,32 @@ public void MergesIntersectionRecursively() { Description = "description", Deprecated = true, - AllOf = new List { - new() { - Type = "object", - Properties = new Dictionary() { + AllOf = [ + new OpenApiSchema() { + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["id"] = new OpenApiSchema() } }, - new() { - Type = "object", - AllOf = new List() { - new () { - Type = "object", - Properties = new Dictionary() { + new OpenApiSchema() { + Type = JsonSchemaType.Object, + AllOf = [ + new OpenApiSchema() { + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["firstName"] = new OpenApiSchema(), ["lastName"] = new OpenApiSchema() } }, - new () { - Type = "object", - Properties = new Dictionary() { + new OpenApiSchema() { + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["lastName"] = new OpenApiSchema() } }, - } + ] } - } + ] }; var result = schema.MergeIntersectionSchemaEntries(); Assert.False(schema.IsInherited()); @@ -679,30 +577,29 @@ public class MergeSingleInclusiveUnionInheritanceOrIntersectionSchemaEntries [Fact] public void DoesMergeWithInheritance() { + var baseClassSchema = new OpenApiSchema() + { + }; + var document = new OpenApiDocument(); + document.AddComponent("BaseClass", baseClassSchema); var schema = new OpenApiSchema() { - Type = "object", + Type = JsonSchemaType.Object, AnyOf = [ - new() + new OpenApiSchema() { - Properties = new Dictionary() + Properties = new Dictionary() { ["one"] = new OpenApiSchema(), }, AllOf = [ - new() - { - Reference = new() - { - Id = "BaseClass" - }, - }, - new() + new OpenApiSchemaReference("BaseClass", document), + new OpenApiSchema() { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["firstName"] = new OpenApiSchema(), ["lastName"] = new OpenApiSchema() @@ -726,37 +623,37 @@ public void DoesMergeWithIntersection() { var schema = new OpenApiSchema() { - Type = "object", + Type = JsonSchemaType.Object, AnyOf = [ - new() + new OpenApiSchema() { - Properties = new Dictionary() + Properties = new Dictionary() { ["one"] = new OpenApiSchema(), }, AllOf = [ - new() + new OpenApiSchema() { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["first"] = new OpenApiSchema(), } }, - new() + new OpenApiSchema() { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["second"] = new OpenApiSchema(), } }, - new() + new OpenApiSchema() { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["third"] = new OpenApiSchema(), } @@ -777,30 +674,29 @@ public void DoesMergeWithIntersection() [Fact] public void DoesNotMergeWithMoreThanOneInclusiveEntry() { + var baseClassSchema = new OpenApiSchema() + { + }; + var document = new OpenApiDocument(); + document.AddComponent("BaseClass", baseClassSchema); var schema = new OpenApiSchema() { - Type = "object", + Type = JsonSchemaType.Object, AnyOf = [ - new() + new OpenApiSchema() { - Properties = new Dictionary() + Properties = new Dictionary() { ["one"] = new OpenApiSchema(), }, AllOf = [ - new() + new OpenApiSchemaReference("BaseClass", document), + new OpenApiSchema() { - Reference = new() - { - Id = "BaseClass" - }, - }, - new() - { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["firstName"] = new OpenApiSchema(), ["lastName"] = new OpenApiSchema() @@ -808,7 +704,7 @@ public void DoesNotMergeWithMoreThanOneInclusiveEntry() }, ] }, - new() { Type = "object" }, + new OpenApiSchema() { Type = JsonSchemaType.Object }, ], }; @@ -820,17 +716,17 @@ public void DoesNotMergeWithoutInheritanceOrIntersection() { var schema = new OpenApiSchema() { - Type = "object", + Type = JsonSchemaType.Object, AnyOf = [ - new() + new OpenApiSchema() { AllOf = [ - new() + new OpenApiSchema() { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["firstName"] = new OpenApiSchema(), ["lastName"] = new OpenApiSchema() @@ -851,30 +747,29 @@ public class MergeSingleExclusiveUnionInheritanceOrIntersectionSchemaEntries [Fact] public void DoesMergeWithInheritance() { + var baseClassSchema = new OpenApiSchema() + { + }; + var document = new OpenApiDocument(); + document.AddComponent("BaseClass", baseClassSchema); var schema = new OpenApiSchema() { - Type = "object", + Type = JsonSchemaType.Object, OneOf = [ - new() + new OpenApiSchema() { - Properties = new Dictionary() + Properties = new Dictionary() { ["one"] = new OpenApiSchema(), }, AllOf = [ - new() + new OpenApiSchemaReference("BaseClass", document), + new OpenApiSchema() { - Reference = new() - { - Id = "BaseClass" - }, - }, - new() - { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["firstName"] = new OpenApiSchema(), ["lastName"] = new OpenApiSchema() @@ -898,37 +793,37 @@ public void DoesMergeWithIntersection() { var schema = new OpenApiSchema() { - Type = "object", + Type = JsonSchemaType.Object, OneOf = [ - new() + new OpenApiSchema() { - Properties = new Dictionary() + Properties = new Dictionary() { ["one"] = new OpenApiSchema(), }, AllOf = [ - new() + new OpenApiSchema() { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["first"] = new OpenApiSchema(), } }, - new() + new OpenApiSchema() { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["second"] = new OpenApiSchema(), } }, - new() + new OpenApiSchema() { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["third"] = new OpenApiSchema(), } @@ -949,30 +844,29 @@ public void DoesMergeWithIntersection() [Fact] public void DoesNotMergeWithMoreThanOneExclusiveEntry() { + var baseClassSchema = new OpenApiSchema() + { + }; + var document = new OpenApiDocument(); + document.AddComponent("BaseClass", baseClassSchema); var schema = new OpenApiSchema() { - Type = "object", + Type = JsonSchemaType.Object, OneOf = [ - new() + new OpenApiSchema() { - Properties = new Dictionary() + Properties = new Dictionary() { ["one"] = new OpenApiSchema(), }, AllOf = [ - new() - { - Reference = new() - { - Id = "BaseClass" - }, - }, - new() + new OpenApiSchemaReference("BaseClass", document), + new OpenApiSchema() { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["firstName"] = new OpenApiSchema(), ["lastName"] = new OpenApiSchema() @@ -980,7 +874,7 @@ public void DoesNotMergeWithMoreThanOneExclusiveEntry() }, ] }, - new() { Type = "object" }, + new OpenApiSchema() { Type = JsonSchemaType.Object }, ], }; @@ -992,17 +886,17 @@ public void DoesNotMergeWithoutInheritanceOrIntersection() { var schema = new OpenApiSchema() { - Type = "object", + Type = JsonSchemaType.Object, OneOf = [ - new() + new OpenApiSchema() { AllOf = [ - new() + new OpenApiSchema() { - Type = "object", - Properties = new Dictionary() + Type = JsonSchemaType.Object, + Properties = new Dictionary() { ["firstName"] = new OpenApiSchema(), ["lastName"] = new OpenApiSchema() @@ -1023,7 +917,7 @@ public void IsArrayFalseOnEmptyItems() { var schema = new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema(), }; Assert.False(schema.IsArray()); @@ -1033,7 +927,7 @@ public void IsArrayFalseOnNullItems() { var schema = new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, }; Assert.False(schema.IsArray()); } @@ -1042,42 +936,41 @@ public void IsEnumFailsOnEmptyMembers() { var schema = new OpenApiSchema { - Type = "string", - Enum = new List(), + Type = JsonSchemaType.String, + Enum = [], }; Assert.False(schema.IsEnum()); - schema.Enum.Add(new OpenApiString("")); + schema.Enum.Add(""); Assert.False(schema.IsEnum()); } private static readonly OpenApiSchema enumSchema = new OpenApiSchema { Title = "riskLevel", - Enum = new List - { - new OpenApiString("low"), - new OpenApiString("medium"), - new OpenApiString("high"), - new OpenApiString("hidden"), - new OpenApiString("none"), - new OpenApiString("unknownFutureValue") - }, - Type = "string" + Enum = + [ + "low", + "medium", + "high", + "hidden", + "none", + "unknownFutureValue" + ], + Type = JsonSchemaType.String }; [Fact] public void IsEnumIgnoresNullableUnions() { var schema = new OpenApiSchema { - AnyOf = new List - { + AnyOf = + [ enumSchema, new OpenApiSchema { - Type = "object", - Nullable = true + Type = JsonSchemaType.Object, } - } + ] }; Assert.False(schema.IsEnum()); } @@ -1086,15 +979,14 @@ public void IsEnumFailsOnNullableInheritance() { var schema = new OpenApiSchema { - AllOf = new List - { + AllOf = + [ enumSchema, new OpenApiSchema { - Type = "object", - Nullable = true + Type = JsonSchemaType.Object, } - } + ] }; Assert.False(schema.IsEnum()); } @@ -1103,21 +995,20 @@ public void IsEnumIgnoresNullableExclusiveUnions() { var schema = new OpenApiSchema { - OneOf = new List - { + OneOf = + [ enumSchema, new OpenApiSchema { - Type = "object", - Nullable = true + Type = JsonSchemaType.Object, } - } + ] }; Assert.False(schema.IsEnum()); } private static readonly OpenApiSchema numberSchema = new OpenApiSchema { - Type = "number", + Type = JsonSchemaType.Number, Format = "double", }; [Fact] @@ -1125,16 +1016,15 @@ public void IsEnumDoesNotMaskExclusiveUnions() { var schema = new OpenApiSchema { - OneOf = new List - { + OneOf = + [ enumSchema, numberSchema, new OpenApiSchema { - Type = "object", - Nullable = true + Type = JsonSchemaType.Object, } - } + ] }; Assert.False(schema.IsEnum()); } @@ -1143,16 +1033,15 @@ public void IsEnumDoesNotMaskUnions() { var schema = new OpenApiSchema { - AnyOf = new List - { + AnyOf = + [ enumSchema, numberSchema, new OpenApiSchema { - Type = "object", - Nullable = true + Type = JsonSchemaType.Object, } - } + ] }; Assert.False(schema.IsEnum()); } @@ -1161,31 +1050,28 @@ public void IsOdataPrimitive() { var schema = new OpenApiSchema { - OneOf = new List - { - new () + OneOf = + [ + new OpenApiSchema() { - Type = "number", + Type = JsonSchemaType.Number | JsonSchemaType.Null, Format = "double", - Nullable = true }, - new () + new OpenApiSchema() { - Type = "string", - Nullable = true + Type = JsonSchemaType.String | JsonSchemaType.Null, }, - new () + new OpenApiSchema() { - Enum = new List() - { - new OpenApiString("INF"), - new OpenApiString("INF"), - new OpenApiString("NaN"), - }, - Type = "string", - Nullable = true + Enum = + [ + "-INF", + "INF", + "NaN", + ], + Type = JsonSchemaType.String | JsonSchemaType.Null, } - } + ] }; Assert.True(schema.IsODataPrimitiveType()); } @@ -1194,27 +1080,27 @@ public void IsOdataPrimitiveBackwardCompatible() { var schema = new OpenApiSchema { - OneOf = new List - { - new () + OneOf = + [ + new OpenApiSchema() { - Type = "number", + Type = JsonSchemaType.Number | JsonSchemaType.Null, Format = "double", }, - new () + new OpenApiSchema() { - Type = "string", + Type = JsonSchemaType.String | JsonSchemaType.Null, }, - new () + new OpenApiSchema() { - Enum = new List() - { - new OpenApiString("INF"), - new OpenApiString("INF"), - new OpenApiString("NaN"), - } + Enum = + [ + "-INF", + "INF", + "NaN", + ] } - } + ] }; Assert.True(schema.IsODataPrimitiveType()); } @@ -1223,42 +1109,22 @@ public void ReturnsEmptyPropertyNameOnCircularReferences() { var entitySchema = new OpenApiSchema { - Reference = new OpenApiReference + Properties = new Dictionary { - Id = "microsoft.graph.entity" - }, - Properties = new Dictionary - { - ["id"] = new OpenApiSchema - { - Reference = new OpenApiReference - { - Id = "microsoft.graph.entity" - } - } + ["id"] = new OpenApiSchemaReference("microsoft.graph.entity") } }; var userSchema = new OpenApiSchema { - Reference = new OpenApiReference - { - Id = "microsoft.graph.user" - }, OneOf = [ entitySchema, new OpenApiSchema { - Type = "object", - Properties = new Dictionary + Type = JsonSchemaType.Object, + Properties = new Dictionary { - ["firstName"] = new OpenApiSchema - { - Reference = new OpenApiReference - { - Id = "microsoft.graph.entity" - } - } + ["firstName"] = new OpenApiSchemaReference("microsoft.graph.entity") } } ], @@ -1271,10 +1137,26 @@ public void ReturnsEmptyPropertyNameOnCircularReferences() } } }; + var document = new OpenApiDocument(); + document.AddComponent("microsoft.graph.entity", entitySchema); + document.AddComponent("microsoft.graph.user", userSchema); + document.SetReferenceHostDocument(); entitySchema.AllOf = [ userSchema ]; Assert.Empty(userSchema.GetDiscriminatorPropertyName()); } + [Fact] + public void GetsClassName() + { + var reference = new OpenApiSchemaReference("microsoft.graph.user", new()); + Assert.Equal("user", reference.GetClassName()); + } + [Fact] + public void GetsClassNameDefensive() + { + var reference = new OpenApiSchema(); + Assert.Empty(reference.GetClassName()); + } } diff --git a/tests/Kiota.Builder.Tests/Extensions/OpenApiUrlTreeNodeExtensionsTests.cs b/tests/Kiota.Builder.Tests/Extensions/OpenApiUrlTreeNodeExtensionsTests.cs index 718248ee2f..a2ce802019 100644 --- a/tests/Kiota.Builder.Tests/Extensions/OpenApiUrlTreeNodeExtensionsTests.cs +++ b/tests/Kiota.Builder.Tests/Extensions/OpenApiUrlTreeNodeExtensionsTests.cs @@ -6,6 +6,8 @@ using Kiota.Builder.Extensions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Services; using Xunit; @@ -29,7 +31,7 @@ public void Defensive() public void GetsDescription() { var node = OpenApiUrlTreeNode.Create(); - node.PathItems.Add(Label, new() + node.PathItems.Add(Label, new OpenApiPathItem() { Description = "description", Summary = "summary" @@ -46,9 +48,9 @@ public void IsComplexPathWithAnyNumberOfParameters() { Paths = new(), }; - doc.Paths.Add("function()", new()); - doc.Paths.Add("function({param})", new()); - doc.Paths.Add("function({param}, {param2})", new()); + doc.Paths.Add("function()", new OpenApiPathItem()); + doc.Paths.Add("function({param})", new OpenApiPathItem()); + doc.Paths.Add("function({param}, {param2})", new OpenApiPathItem()); var node = OpenApiUrlTreeNode.Create(doc, Label); Assert.False(node.IsComplexPathMultipleParameters()); Assert.False(node.Children.First().Value.IsComplexPathMultipleParameters()); @@ -62,7 +64,7 @@ public void IsPathWithSingleSimpleParameter() { Paths = new(), }; - doc.Paths.Add("{param}", new()); + doc.Paths.Add("{param}", new OpenApiPathItem()); var node = OpenApiUrlTreeNode.Create(doc, Label); Assert.False(node.IsPathSegmentWithSingleSimpleParameter()); Assert.True(node.Children.First().Value.IsPathSegmentWithSingleSimpleParameter()); @@ -74,7 +76,7 @@ public void DoesNodeBelongToItemSubnamespace() { Paths = new(), }; - doc.Paths.Add("{param}", new()); + doc.Paths.Add("{param}", new OpenApiPathItem()); var node = OpenApiUrlTreeNode.Create(doc, Label); Assert.False(node.DoesNodeBelongToItemSubnamespace()); Assert.True(node.Children.First().Value.DoesNodeBelongToItemSubnamespace()); @@ -83,7 +85,7 @@ public void DoesNodeBelongToItemSubnamespace() { Paths = new(), }; - doc.Paths.Add("param}", new()); + doc.Paths.Add("param}", new OpenApiPathItem()); node = OpenApiUrlTreeNode.Create(doc, Label); Assert.False(node.Children.First().Value.DoesNodeBelongToItemSubnamespace()); @@ -91,7 +93,7 @@ public void DoesNodeBelongToItemSubnamespace() { Paths = new(), }; - doc.Paths.Add("{param", new()); + doc.Paths.Add("{param", new OpenApiPathItem()); node = OpenApiUrlTreeNode.Create(doc, Label); Assert.False(node.Children.First().Value.DoesNodeBelongToItemSubnamespace()); } @@ -102,7 +104,7 @@ public void GetNodeNamespaceFromPath() { Paths = new(), }; - doc.Paths.Add("\\users\\messages", new()); + doc.Paths.Add("\\users\\messages", new OpenApiPathItem()); var node = OpenApiUrlTreeNode.Create(doc, Label); Assert.Equal("graph.users.messages", node.Children.First().Value.GetNodeNamespaceFromPath("graph")); Assert.Equal("users.messages", node.Children.First().Value.GetNodeNamespaceFromPath(null)); @@ -114,7 +116,7 @@ public void SanitizesAtSign() { Paths = new(), }; - doc.Paths.Add("\\deviceManagement\\microsoft.graph.getRoleScopeTagsByIds(ids=@ids)", new()); + doc.Paths.Add("\\deviceManagement\\microsoft.graph.getRoleScopeTagsByIds(ids=@ids)", new OpenApiPathItem()); var node = OpenApiUrlTreeNode.Create(doc, Label); Assert.Equal("graph.deviceManagement.microsoftGraphGetRoleScopeTagsByIdsWithIds", node.Children.First().Value.GetNodeNamespaceFromPath("graph")); } @@ -135,25 +137,25 @@ public void GetUrlTemplateSelectsDistinctQueryParameters() { Paths = [], }; - doc.Paths.Add("{param-with-dashes}\\existing-segment", new() + doc.Paths.Add("{param-with-dashes}\\existing-segment", new OpenApiPathItem() { Operations = new Dictionary { { OperationType.Get, new() { Parameters = [ - new() { + new OpenApiParameter() { Name = "param-with-dashes", In = ParameterLocation.Path, Required = true, - Schema = new() { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, }, - new (){ + new OpenApiParameter (){ Name = "$select", In = ParameterLocation.Query, - Schema = new () { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, } @@ -163,20 +165,20 @@ public void GetUrlTemplateSelectsDistinctQueryParameters() { OperationType.Put, new() { Parameters = [ - new() { + new OpenApiParameter() { Name = "param-with-dashes", In = ParameterLocation.Path, Required = true, - Schema = new() { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, }, - new (){ + new OpenApiParameter(){ Name = "$select", In = ParameterLocation.Query, - Schema = new () { - Type = "string" + Schema = new OpenApiSchema () { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, } @@ -196,17 +198,17 @@ public void DifferentUrlTemplatesPerOperation() { Paths = [], }; - doc.Paths.Add("{param-with-dashes}\\existing-segment", new() + doc.Paths.Add("{param-with-dashes}\\existing-segment", new OpenApiPathItem() { Parameters = [ - new() + new OpenApiParameter() { Name = "param-with-dashes", In = ParameterLocation.Path, Required = true, - Schema = new() + Schema = new OpenApiSchema() { - Type = "string" + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, }, @@ -215,11 +217,11 @@ public void DifferentUrlTemplatesPerOperation() { OperationType.Get, new() { Parameters = [ - new (){ + new OpenApiParameter(){ Name = "$select", In = ParameterLocation.Query, - Schema = new () { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, } @@ -246,17 +248,17 @@ public void DifferentUrlTemplatesPerOperationWithRequiredParameter() { Paths = [], }; - doc.Paths.Add("{param-with-dashes}\\existing-segment", new() + doc.Paths.Add("{param-with-dashes}\\existing-segment", new OpenApiPathItem() { Parameters = [ - new() + new OpenApiParameter() { Name = "param-with-dashes", In = ParameterLocation.Path, Required = true, - Schema = new() + Schema = new OpenApiSchema() { - Type = "string" + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, }, @@ -265,11 +267,11 @@ public void DifferentUrlTemplatesPerOperationWithRequiredParameter() { OperationType.Get, new() { Parameters = [ - new (){ + new OpenApiParameter(){ Name = "$select", In = ParameterLocation.Query, - Schema = new () { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, } @@ -279,11 +281,11 @@ public void DifferentUrlTemplatesPerOperationWithRequiredParameter() { OperationType.Post, new() { Parameters = [ - new (){ + new OpenApiParameter(){ Name = "$expand", In = ParameterLocation.Query, - Schema = new () { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, } @@ -296,11 +298,11 @@ public void DifferentUrlTemplatesPerOperationWithRequiredParameter() { OperationType.Delete, new() { Parameters = [ - new (){ + new OpenApiParameter (){ Name = "id", In = ParameterLocation.Query, - Schema = new () { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, Required = true @@ -327,7 +329,7 @@ public void GeneratesRequiredQueryParametersAndOptionalMixInPathItem() { Paths = [], }; - doc.Paths.Add("users\\{id}\\manager", new() + doc.Paths.Add("users\\{id}\\manager", new OpenApiPathItem() { Parameters = { new OpenApiParameter { @@ -335,7 +337,7 @@ public void GeneratesRequiredQueryParametersAndOptionalMixInPathItem() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { @@ -343,7 +345,7 @@ public void GeneratesRequiredQueryParametersAndOptionalMixInPathItem() In = ParameterLocation.Query, Required = false, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { @@ -351,7 +353,7 @@ public void GeneratesRequiredQueryParametersAndOptionalMixInPathItem() In = ParameterLocation.Query, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, @@ -371,7 +373,7 @@ public void GeneratesRequiredQueryParametersAndOptionalMixInOperation() { Paths = [], }; - doc.Paths.Add("users\\{id}\\manager", new() + doc.Paths.Add("users\\{id}\\manager", new OpenApiPathItem() { Operations = new Dictionary { { OperationType.Get, new() { @@ -381,7 +383,7 @@ public void GeneratesRequiredQueryParametersAndOptionalMixInOperation() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { @@ -389,7 +391,7 @@ public void GeneratesRequiredQueryParametersAndOptionalMixInOperation() In = ParameterLocation.Query, Required = false, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { @@ -397,7 +399,7 @@ public void GeneratesRequiredQueryParametersAndOptionalMixInOperation() In = ParameterLocation.Query, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, @@ -415,7 +417,7 @@ public void GeneratesOnlyOptionalQueryParametersInPathItem() { Paths = [], }; - doc.Paths.Add("users\\{id}\\manager", new() + doc.Paths.Add("users\\{id}\\manager", new OpenApiPathItem() { Parameters = { new OpenApiParameter { @@ -423,7 +425,7 @@ public void GeneratesOnlyOptionalQueryParametersInPathItem() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { @@ -431,14 +433,14 @@ public void GeneratesOnlyOptionalQueryParametersInPathItem() In = ParameterLocation.Query, Required = false, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { Name = "apikey", In = ParameterLocation.Query, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, @@ -458,7 +460,7 @@ public void GeneratesOnlyOptionalQueryParametersInOperation() { Paths = [], }; - doc.Paths.Add("users\\{id}\\manager", new() + doc.Paths.Add("users\\{id}\\manager", new OpenApiPathItem() { Operations = new Dictionary { { OperationType.Get, new() { @@ -468,21 +470,21 @@ public void GeneratesOnlyOptionalQueryParametersInOperation() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { Name = "filter", In = ParameterLocation.Query, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { Name = "apikey", In = ParameterLocation.Query, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, @@ -500,7 +502,7 @@ public void GeneratesOnlyRequiredQueryParametersInPathItem() { Paths = [], }; - doc.Paths.Add("users\\{id}\\manager", new() + doc.Paths.Add("users\\{id}\\manager", new OpenApiPathItem() { Parameters = { new OpenApiParameter { @@ -508,7 +510,7 @@ public void GeneratesOnlyRequiredQueryParametersInPathItem() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { @@ -516,7 +518,7 @@ public void GeneratesOnlyRequiredQueryParametersInPathItem() In = ParameterLocation.Query, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { @@ -524,7 +526,7 @@ public void GeneratesOnlyRequiredQueryParametersInPathItem() Required = true, In = ParameterLocation.Query, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, @@ -544,7 +546,7 @@ public void GeneratesOnlyRequiredQueryParametersInOperation() { Paths = [], }; - doc.Paths.Add("users\\{id}\\manager", new() + doc.Paths.Add("users\\{id}\\manager", new OpenApiPathItem() { Operations = new Dictionary { { OperationType.Get, new() { @@ -554,7 +556,7 @@ public void GeneratesOnlyRequiredQueryParametersInOperation() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { @@ -562,7 +564,7 @@ public void GeneratesOnlyRequiredQueryParametersInOperation() Required = true, In = ParameterLocation.Query, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter { @@ -570,7 +572,7 @@ public void GeneratesOnlyRequiredQueryParametersInOperation() Required = true, In = ParameterLocation.Query, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, @@ -589,49 +591,49 @@ public void GetUrlTemplateCleansInvalidParameters() { Paths = [], }; - doc.Paths.Add("{param-with-dashes}\\existing-segment", new() + doc.Paths.Add("{param-with-dashes}\\existing-segment", new OpenApiPathItem() { Operations = new Dictionary { { OperationType.Get, new() { Parameters = [ - new() { + new OpenApiParameter() { Name = "param-with-dashes", In = ParameterLocation.Path, Required = true, - Schema = new() { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, }, - new (){ + new OpenApiParameter(){ Name = "$select", In = ParameterLocation.Query, - Schema = new () { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, }, - new (){ + new OpenApiParameter(){ Name = "api-version", In = ParameterLocation.Query, - Schema = new () { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, }, - new (){ + new OpenApiParameter(){ Name = "api~topic", In = ParameterLocation.Query, - Schema = new () { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, }, - new (){ + new OpenApiParameter(){ Name = "api.encoding", In = ParameterLocation.Query, - Schema = new () { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, } @@ -659,27 +661,27 @@ public void GetsClassNameWithIndexerAndExtension() { Paths = new(), }; - doc.Paths.Add("/reviews/{resource-type}.json", new() + doc.Paths.Add("/reviews/{resource-type}.json", new OpenApiPathItem() { Operations = new Dictionary { { OperationType.Get, new() { - Parameters = new List { - new() { + Parameters = new List { + new OpenApiParameter() { Name = "resource-type", In = ParameterLocation.Path, Required = true, - Schema = new() { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, } }, Responses = new OpenApiResponses() { - {"200", new() { + {"200", new OpenApiResponse() { Content = new Dictionary() { {"application/json", new() { - Schema = new () { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String } }} } @@ -700,18 +702,23 @@ public void GetsClassNameWithSegmentsToSkipForClassNames() { Paths = new(), }; - doc.Paths.Add("/reviews/{resource-type}.json", new() + doc.AddComponent("microsoft.graph.json", new OpenApiSchema + { + Type = JsonSchemaType.Object, + Title = "json", + }); + doc.Paths.Add("/reviews/{resource-type}.json", new OpenApiPathItem() { Operations = new Dictionary { { OperationType.Get, new() { - Parameters = new List { - new() { + Parameters = new List { + new OpenApiParameter() { Name = "resource-type", In = ParameterLocation.Path, Required = true, - Schema = new() { - Type = "string" + Schema = new OpenApiSchema() { + Type = JsonSchemaType.String }, Style = ParameterStyle.Simple, } @@ -719,22 +726,14 @@ public void GetsClassNameWithSegmentsToSkipForClassNames() Responses = new OpenApiResponses() { { - "200", new() + "200", new OpenApiResponse() { Content = new Dictionary() { { "application/json", new() { - Schema = new () - { - Type = "object", - Title = "json", - Reference = new OpenApiReference() - { - Id = "microsoft.graph.json" - } - } + Schema = new OpenApiSchemaReference("microsoft.graph.json"), } } } @@ -745,6 +744,7 @@ public void GetsClassNameWithSegmentsToSkipForClassNames() } } }); + doc.SetReferenceHostDocument(); var node = OpenApiUrlTreeNode.Create(doc, Label); var result = node.Children["reviews"].Children["{resource-type}.json"].GetClassName(new() { "application/json" }); @@ -763,24 +763,19 @@ public void SinglePathParametersAreDeduplicated() { var userSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "displayName", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "#/components/schemas/microsoft.graph.user" - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -794,7 +789,7 @@ public void SinglePathParametersAreDeduplicated() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, }, @@ -807,7 +802,7 @@ public void SinglePathParametersAreDeduplicated() Content = { ["application/json"] = new OpenApiMediaType { - Schema = userSchema + Schema = new OpenApiSchemaReference("microsoft.graph.user") } } } @@ -823,7 +818,7 @@ public void SinglePathParametersAreDeduplicated() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, }, @@ -836,7 +831,7 @@ public void SinglePathParametersAreDeduplicated() Content = { ["application/json"] = new OpenApiMediaType { - Schema = userSchema + Schema = new OpenApiSchemaReference("microsoft.graph.user") } } } @@ -855,7 +850,7 @@ public void SinglePathParametersAreDeduplicated() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, }, @@ -865,7 +860,7 @@ public void SinglePathParametersAreDeduplicated() Content = { ["application/json"] = new OpenApiMediaType { - Schema = userSchema + Schema = new OpenApiSchemaReference("microsoft.graph.user") } } } @@ -876,13 +871,15 @@ public void SinglePathParametersAreDeduplicated() }, Components = new OpenApiComponents { - Schemas = new Dictionary { + Schemas = new Dictionary { { "microsoft.graph.user", userSchema } } } }; + document.RegisterComponents(); + document.SetReferenceHostDocument(); var mockLogger = new CountLogger(); var builder = new KiotaBuilder(mockLogger, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -912,35 +909,25 @@ public void SinglePathParametersAreDeduplicatedAndOrderIsRespected() { var ownerSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "#/components/schemas/owner" - }, - UnresolvedReference = false }; var repoSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "#/components/schemas/repo" - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -957,7 +944,7 @@ public void SinglePathParametersAreDeduplicatedAndOrderIsRespected() Content = { ["application/json"] = new OpenApiMediaType { - Schema = repoSchema + Schema = new OpenApiSchemaReference("repo") } } } @@ -976,7 +963,7 @@ public void SinglePathParametersAreDeduplicatedAndOrderIsRespected() Content = { ["application/json"] = new OpenApiMediaType { - Schema = repoSchema + Schema = new OpenApiSchemaReference("repo") } } } @@ -987,12 +974,14 @@ public void SinglePathParametersAreDeduplicatedAndOrderIsRespected() }, Components = new OpenApiComponents { - Schemas = new Dictionary { + Schemas = new Dictionary { {"owner", ownerSchema}, {"repo", repoSchema} } } }; + document.RegisterComponents(); + document.SetReferenceHostDocument(); var mockLogger = new CountLogger(); var builder = new KiotaBuilder(mockLogger, new GenerationConfiguration { ClientClassName = "GitHub", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -1023,7 +1012,7 @@ public void repro4085() ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } } @@ -1044,7 +1033,7 @@ public void repro4085() ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } } diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs index 7acf60fbb3..0c84c4c754 100644 --- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net.Http; using System.Text; +using System.Text.Json.Nodes; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -14,10 +15,11 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.MicrosoftExtensions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Services; using Moq; @@ -36,6 +38,76 @@ public void Dispose() _httpClient.Dispose(); GC.SuppressFinalize(this); } + [Fact] + public async Task SupportsExternalReferences() + { + var tempFilePathReferee = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + await File.WriteAllTextAsync(tempFilePathReferee, +""" +openapi: 3.1.1 +info: + title: OData Service for namespace microsoft.graph + description: This OData service is located at https://graph.microsoft.com/v1.0 + version: 1.0.1 +servers: + - url: https://graph.microsoft.com/v1.0 +paths: + /placeholder: + get: + responses: + '200': + content: + application/json: + schema: + type: string +components: + schemas: + MySchema: + type: object + properties: + id: + type: string +"""); + var tempFilePathReferrer = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + await File.WriteAllTextAsync(tempFilePathReferrer, +$$$""" +openapi: 3.1.1 +info: + title: OData Service for namespace microsoft.graph + description: This OData service is located at https://graph.microsoft.com/v1.0 + version: 1.0.1 +servers: + - url: https://graph.microsoft.com/v1.0 +paths: + /placeholder: + get: + responses: + '200': + content: + application/json: + schema: + $ref: './{{{Path.GetFileName(tempFilePathReferee)}}}#/components/schemas/MySchema' +components: + schemas: + MySchema: + type: object + properties: + id: + type: string +"""); + var mockLogger = new Mock>(); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePathReferrer, Serializers = ["none"], Deserializers = ["none"] }, _httpClient); + await using var fs = new FileStream(tempFilePathReferrer, FileMode.Open); + var document = await builder.CreateOpenApiDocumentAsync(fs); + var node = builder.CreateUriSpace(document); + builder.SetApiRootUrl(); + var codeModel = builder.CreateSourceModel(node); + var rootNS = codeModel.FindNamespaceByName("ApiSdk"); + Assert.NotNull(rootNS); + var modelClass = rootNS.FindChildByName("MySchema", true); + Assert.NotNull(modelClass); + Assert.Single(modelClass.Properties, static x => x.Name.Equals("id", StringComparison.OrdinalIgnoreCase)); + } [InlineData("https://graph.microsoft.com/description.yaml", "/v1.0", "https://graph.microsoft.com/v1.0")] [InlineData("/home/vsts/a/s/1", "/v1.0", "/v1.0")] [InlineData("https://graph.microsoft.com/docs/description.yaml", "../v1.0", "https://graph.microsoft.com/v1.0")] @@ -426,7 +498,7 @@ public async Task ParsesEnumDescriptionsAsync() StorageAccountType: type: string enum: - - +1 + - '+1' - -1 - Standard_LRS - Standard_ZRS @@ -438,7 +510,7 @@ public async Task ParsesEnumDescriptionsAsync() name: AccountType modelAsString: false values: - - value: +1 + - value: '+1' - value: -1 - value: Standard_LRS description: Locally redundant storage. @@ -734,13 +806,12 @@ public async Task NamesComponentsInlineSchemasProperlyAsync() Assert.NotNull(doClass); var deletedDateTimeProperty = doClass.FindChildByName("DeletedDateTime", false); Assert.NotNull(deletedDateTimeProperty); - var unionType = deletedDateTimeProperty.Type as CodeUnionType; - Assert.NotNull(unionType); + var unionType = Assert.IsType(deletedDateTimeProperty.Type); Assert.Equal("directoryObject_deletedDateTime", unionType.Name, StringComparer.OrdinalIgnoreCase); Assert.Equal(3, unionType.Types.Count()); - Assert.Equal("DateTimeOffset", unionType.Types.First().Name, StringComparer.OrdinalIgnoreCase); - Assert.Equal("directoryObject_deletedDateTimeMember1", unionType.Types.ElementAt(1).Name, StringComparer.OrdinalIgnoreCase); - Assert.Equal("int64", unionType.Types.ElementAt(2).Name, StringComparer.OrdinalIgnoreCase); + Assert.Single(unionType.Types, t => "DateTimeOffset".Equals(t.Name, StringComparison.OrdinalIgnoreCase)); + Assert.Single(unionType.Types, t => "directoryObject_deletedDateTimeMember1".Equals(t.Name, StringComparison.OrdinalIgnoreCase)); + Assert.Single(unionType.Types, t => "int64".Equals(t.Name, StringComparison.OrdinalIgnoreCase)); Assert.Null(modelsNS.FindChildByName("users")); } [Theory] @@ -1354,10 +1425,10 @@ public void Single_path_with_get_collection() { Schema = new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - Type = "int" + Type = JsonSchemaType.Integer } } } @@ -1399,19 +1470,19 @@ public void OData_doubles_as_one_of() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "progress", new OpenApiSchema{ - OneOf = new List{ + OneOf = new List{ new OpenApiSchema{ - Type = "number" + Type = JsonSchemaType.Number }, new OpenApiSchema{ - Type = "string" + Type = JsonSchemaType.String }, new OpenApiSchema { - Enum = new List { new OpenApiString("-INF"), new OpenApiString("INF"), new OpenApiString("NaN") } + Enum = new List { "-INF", "INF", "NaN" } } }, Format = "double" @@ -1451,20 +1522,20 @@ public void OData_doubles_as_one_of_format_inside() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "progress", new OpenApiSchema{ - OneOf = new List{ + OneOf = new List{ new OpenApiSchema{ - Type = "number", + Type = JsonSchemaType.Number, Format = "double" }, new OpenApiSchema{ - Type = "string" + Type = JsonSchemaType.String }, new OpenApiSchema { - Enum = new List { new OpenApiString("-INF"), new OpenApiString("INF"), new OpenApiString("NaN") } + Enum = new List { "-INF", "INF", "NaN" } } }, } @@ -1503,19 +1574,19 @@ public void OData_doubles_as_any_of() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "progress", new OpenApiSchema{ - AnyOf = new List{ + AnyOf = new List{ new OpenApiSchema{ - Type = "number" + Type = JsonSchemaType.Number }, new OpenApiSchema{ - Type = "string" + Type = JsonSchemaType.String }, new OpenApiSchema { - Enum = new List { new OpenApiString("-INF"), new OpenApiString("INF"), new OpenApiString("NaN") } + Enum = new List { "-INF", "INF", "NaN" } } }, Format = "double" @@ -1541,25 +1612,20 @@ public void MultiNestedArraysSupportedAsUntypedNodes() { var fooSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "sortBy", new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } } }, - }, - Reference = new OpenApiReference - { - Id = "#/components/schemas/bar.foo" - }, - UnresolvedReference = false + } }; var document = new OpenApiDocument { @@ -1576,7 +1642,7 @@ public void MultiNestedArraysSupportedAsUntypedNodes() Content = { ["application/json"] = new OpenApiMediaType { - Schema = fooSchema + Schema = new OpenApiSchemaReference("bar.foo") } } } @@ -1585,15 +1651,9 @@ public void MultiNestedArraysSupportedAsUntypedNodes() } }, }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "bar.foo", fooSchema - } - } - } }; + document.AddComponent("bar.foo", fooSchema); + document.SetReferenceHostDocument(); var mockLogger = new CountLogger(); var builder = new KiotaBuilder(mockLogger, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -1609,24 +1669,19 @@ public void Object_Arrays_are_supported() { var userSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "displayName", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "#/components/schemas/microsoft.graph.user" - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -1644,17 +1699,17 @@ public void Object_Arrays_are_supported() ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "value", new OpenApiSchema { - Type = "array", - Items = userSchema + Type = JsonSchemaType.Array, + Items = new OpenApiSchemaReference("microsoft.graph.user") } }, { "unknown", new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { } } @@ -1669,15 +1724,9 @@ public void Object_Arrays_are_supported() } }, }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "microsoft.graph.user", userSchema - } - } - } }; + document.AddComponent("microsoft.graph.user", userSchema); + document.SetReferenceHostDocument(); var mockLogger = new CountLogger(); var builder = new KiotaBuilder(mockLogger, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -1711,7 +1760,7 @@ public void TextPlainEndpointsAreSupported() ["text/plain"] = new OpenApiMediaType { Schema = new OpenApiSchema { - Type = "number", + Type = JsonSchemaType.Number, Format = "int32", } } @@ -1740,52 +1789,42 @@ public void Supports_Path_Parameters() { var resourceActionSchema = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, Title = "resourceAction", - Properties = new Dictionary { + Properties = new Dictionary { { "allowedResourceActions", new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, { "notAllowedResourceActions", new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } } }, - Reference = new OpenApiReference - { - Id = "#/components/schemas/microsoft.graph.resourceAction" - }, - UnresolvedReference = false }; var permissionSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "resourceActions", new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - AnyOf = new List { - resourceActionSchema, + AnyOf = new List { + new OpenApiSchemaReference("microsoft.graph.resourceAction"), } } } } }, - Reference = new OpenApiReference - { - Id = "#/components/schemas/microsoft.graph.rolePermission" - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -1800,7 +1839,7 @@ public void Supports_Path_Parameters() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, @@ -1814,10 +1853,10 @@ public void Supports_Path_Parameters() ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - AnyOf = new List { - permissionSchema, + AnyOf = new List { + new OpenApiSchemaReference("microsoft.graph.rolePermission"), } } } @@ -1829,14 +1868,10 @@ public void Supports_Path_Parameters() } }, }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { "microsoft.graph.rolePermission", permissionSchema }, - { "microsoft.graph.resourceAction", resourceActionSchema }, - } - } }; + document.AddComponent("microsoft.graph.resourceAction", resourceActionSchema); + document.AddComponent("microsoft.graph.rolePermission", permissionSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -1861,52 +1896,42 @@ public void Supports_Path_Query_And_Header_Parameters() { var resourceActionSchema = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, Title = "resourceAction", - Properties = new Dictionary { + Properties = new Dictionary { { "allowedResourceActions", new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, { "notAllowedResourceActions", new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } } }, - Reference = new OpenApiReference - { - Id = "#/components/schemas/microsoft.graph.resourceAction" - }, - UnresolvedReference = false }; var permissionSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "resourceActions", new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - AnyOf = new List { - resourceActionSchema, + AnyOf = new List { + new OpenApiSchemaReference("microsoft.graph.resourceAction"), } } } } }, - Reference = new OpenApiReference - { - Id = "#/components/schemas/microsoft.graph.rolePermission" - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -1921,7 +1946,7 @@ public void Supports_Path_Query_And_Header_Parameters() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter @@ -1930,7 +1955,7 @@ public void Supports_Path_Query_And_Header_Parameters() In = ParameterLocation.Query, Required = false, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }, }, new OpenApiParameter @@ -1940,7 +1965,7 @@ public void Supports_Path_Query_And_Header_Parameters() Description = "ETag", Required = false, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }, }, new OpenApiParameter @@ -1950,7 +1975,7 @@ public void Supports_Path_Query_And_Header_Parameters() Description = "Consistency level", Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }, } }, @@ -1964,10 +1989,10 @@ public void Supports_Path_Query_And_Header_Parameters() ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - AnyOf = new List { - permissionSchema, + AnyOf = new List { + new OpenApiSchemaReference("microsoft.graph.rolePermission"), } } } @@ -1979,14 +2004,10 @@ public void Supports_Path_Query_And_Header_Parameters() } }, }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { "microsoft.graph.rolePermission", permissionSchema }, - { "microsoft.graph.resourceAction", resourceActionSchema }, - } - } }; + document.AddComponent("microsoft.graph.resourceAction", resourceActionSchema); + document.AddComponent("microsoft.graph.rolePermission", permissionSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost", Language = GenerationLanguage.CLI }, _httpClient); var node = builder.CreateUriSpace(document); @@ -2031,7 +2052,7 @@ public void DeduplicatesConflictingParameterNamesForCLI() In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, new OpenApiParameter @@ -2040,7 +2061,7 @@ public void DeduplicatesConflictingParameterNamesForCLI() In = ParameterLocation.Query, Required = false, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }, }, new OpenApiParameter @@ -2049,7 +2070,7 @@ public void DeduplicatesConflictingParameterNamesForCLI() In = ParameterLocation.Header, Required = false, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }, }, }, @@ -2063,10 +2084,10 @@ public void DeduplicatesConflictingParameterNamesForCLI() ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary() { - { "foo", new() { - Type = "string" + Type = JsonSchemaType.Object, + Properties = new Dictionary() { + { "foo", new OpenApiSchema() { + Type = JsonSchemaType.String } } }, @@ -2105,19 +2126,14 @@ public void Inline_Property_Inheritance_Is_Supported() { var resourceSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "info", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "resource" - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -2135,19 +2151,19 @@ public void Inline_Property_Inheritance_Is_Supported() ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "derivedResource", new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "info2", new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "title", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } } @@ -2155,7 +2171,7 @@ public void Inline_Property_Inheritance_Is_Supported() } }, AllOf = [ - resourceSchema, + new OpenApiSchemaReference("resource"), ] } } @@ -2169,15 +2185,9 @@ public void Inline_Property_Inheritance_Is_Supported() } }, }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "#/components/resource", resourceSchema - } - } - } }; + document.AddComponent("resource", resourceSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -2203,18 +2213,13 @@ public void Inline_Property_Inheritance_Is_Supported2() { var resourceSchema = new OpenApiSchema { - Type = "object", - Reference = new OpenApiReference - { - Id = "resource" - }, - UnresolvedReference = false + Type = JsonSchemaType.Object, }; - var properties = new Dictionary + var properties = new Dictionary { - { "info", new OpenApiSchema { Type = "string", } }, - { "derivedResource", new OpenApiSchema { AllOf = new List { resourceSchema, } } }, + { "info", new OpenApiSchema { Type = JsonSchemaType.String, } }, + { "derivedResource", new OpenApiSchema { AllOf = new List { new OpenApiSchemaReference("resource"), } } }, }; resourceSchema.Properties = properties; @@ -2235,9 +2240,9 @@ public void Inline_Property_Inheritance_Is_Supported2() ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - AllOf = new List() + AllOf = new List() { - resourceSchema + new OpenApiSchemaReference("resource"), } } } @@ -2248,15 +2253,9 @@ public void Inline_Property_Inheritance_Is_Supported2() } }, }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "#/components/resource", resourceSchema - } - } - } }; + document.AddComponent("resource", resourceSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -2289,11 +2288,11 @@ public void MapsTime() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "progress", new OpenApiSchema{ - Type = "string", + Type = JsonSchemaType.String, Format = "time" } } @@ -2331,11 +2330,11 @@ public void MapsDate() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "progress", new OpenApiSchema{ - Type = "string", + Type = JsonSchemaType.String, Format = "date" } } @@ -2373,11 +2372,11 @@ public void MapsDuration() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "progress", new OpenApiSchema{ - Type = "string", + Type = JsonSchemaType.String, Format = "duration" } } @@ -2415,11 +2414,11 @@ public void AddsErrorMapping() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "progress", new OpenApiSchema{ - Type = "string", + Type = JsonSchemaType.String, } } } @@ -2435,11 +2434,11 @@ public void AddsErrorMapping() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "errorId", new OpenApiSchema{ - Type = "string", + Type = JsonSchemaType.String, Extensions = new Dictionary { { OpenApiPrimaryErrorMessageExtension.Name, @@ -2463,11 +2462,11 @@ public void AddsErrorMapping() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "serviceErrorId", new OpenApiSchema{ - Type = "string", + Type = JsonSchemaType.String, Extensions = new Dictionary { { OpenApiPrimaryErrorMessageExtension.Name, @@ -2491,7 +2490,7 @@ public void AddsErrorMapping() { Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } } @@ -2504,11 +2503,11 @@ public void AddsErrorMapping() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "authenticationRealm", new OpenApiSchema{ - Type = "string", + Type = JsonSchemaType.String, Extensions = new Dictionary { { OpenApiPrimaryErrorMessageExtension.Name, @@ -2521,7 +2520,7 @@ public void AddsErrorMapping() }, { "authenticationCode", new OpenApiSchema{ - Type = "string", + Type = JsonSchemaType.String, } } } @@ -2585,11 +2584,11 @@ public void IgnoresErrorCodesWithNoSchema() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "progress", new OpenApiSchema{ - Type = "string", + Type = JsonSchemaType.String, } } } @@ -2634,20 +2633,14 @@ public void DoesntAddSuffixesToErrorTypesWhenComponents() { var errorSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "errorId", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.error", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var errorResponse = new OpenApiResponse { @@ -2655,15 +2648,9 @@ public void DoesntAddSuffixesToErrorTypesWhenComponents() { ["application/json"] = new OpenApiMediaType { - Schema = errorSchema + Schema = new OpenApiSchemaReference("microsoft.graph.error") } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.error", - Type = ReferenceType.Response - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -2684,11 +2671,11 @@ public void DoesntAddSuffixesToErrorTypesWhenComponents() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "progress", new OpenApiSchema{ - Type = "string", + Type = JsonSchemaType.String, } } } @@ -2696,28 +2683,18 @@ public void DoesntAddSuffixesToErrorTypesWhenComponents() } } }, - ["4XX"] = errorResponse, - ["5XX"] = errorResponse, - ["401"] = errorResponse + ["4XX"] = new OpenApiResponseReference("microsoft.graph.error"), + ["5XX"] = new OpenApiResponseReference("microsoft.graph.error"), + ["401"] = new OpenApiResponseReference("microsoft.graph.error") } } } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "microsoft.graph.error", errorSchema - } - }, - Responses = new Dictionary { - { - "microsoft.graph.error", errorResponse - } - } - }, }; + document.AddComponent("microsoft.graph.error", errorSchema); + document.AddComponent("microsoft.graph.error", errorResponse); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.SetOpenApiDocument(document); @@ -2744,20 +2721,14 @@ public void UsesDefaultAs4XXAnd5XXWhenAbsent() { var errorSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "errorId", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.error", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var errorResponse = new OpenApiResponse { @@ -2765,15 +2736,9 @@ public void UsesDefaultAs4XXAnd5XXWhenAbsent() { ["application/json"] = new OpenApiMediaType { - Schema = errorSchema + Schema = new OpenApiSchemaReference("microsoft.graph.error") } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.error", - Type = ReferenceType.Response - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -2794,11 +2759,11 @@ public void UsesDefaultAs4XXAnd5XXWhenAbsent() { Schema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "progress", new OpenApiSchema{ - Type = "string", + Type = JsonSchemaType.String, } } } @@ -2806,27 +2771,17 @@ public void UsesDefaultAs4XXAnd5XXWhenAbsent() } } }, - ["default"] = errorResponse, - ["401"] = errorResponse + ["default"] = new OpenApiResponseReference("microsoft.graph.error"), + ["401"] = new OpenApiResponseReference("microsoft.graph.error") } } } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "microsoft.graph.error", errorSchema - } - }, - Responses = new Dictionary { - { - "microsoft.graph.error", errorResponse - } - } - }, }; + document.AddComponent("microsoft.graph.error", errorSchema); + document.AddComponent("microsoft.graph.error", errorResponse); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.SetOpenApiDocument(document); @@ -2853,44 +2808,32 @@ public void DoesntAddPropertyHolderOnNonAdditionalModels() { var weatherForecastSchema = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AdditionalPropertiesAllowed = false, - Properties = new Dictionary { + Properties = new Dictionary { { "date", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, Format = "date-time" } }, { "temperature", new OpenApiSchema { - Type = "integer", + Type = JsonSchemaType.Integer, Format = "int32" } } }, - Reference = new OpenApiReference - { - Id = "weatherForecast", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; - var forecastResponse = new OpenApiResponse + var weatherForecastResponse = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = weatherForecastSchema + Schema = new OpenApiSchemaReference("weatherForecast") } }, - Reference = new OpenApiReference - { - Id = "weatherForecast", - Type = ReferenceType.Response - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -2903,26 +2846,16 @@ public void DoesntAddPropertyHolderOnNonAdditionalModels() { Responses = new OpenApiResponses { - ["200"] = forecastResponse + ["200"] = new OpenApiResponseReference("weatherForecast") } } } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "weatherForecast", weatherForecastSchema - } - }, - Responses = new Dictionary { - { - "weatherForecast", forecastResponse - } - } - }, }; + document.AddComponent("weatherForecast", weatherForecastSchema); + document.AddComponent("weatherForecast", weatherForecastResponse); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.SetOpenApiDocument(document); @@ -2938,28 +2871,22 @@ public void SquishesLonelyNullables() { var uploadSessionSchema = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AdditionalPropertiesAllowed = false, - Properties = new Dictionary { + Properties = new Dictionary { { "date", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, Format = "date-time" } }, { "temperature", new OpenApiSchema { - Type = "integer", + Type = JsonSchemaType.Integer, Format = "int32" } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.uploadSession", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -2979,9 +2906,8 @@ public void SquishesLonelyNullables() { Schema = new OpenApiSchema { - Nullable = true, - AnyOf = new List { - uploadSessionSchema + AnyOf = new List { + new OpenApiSchemaReference("microsoft.graph.uploadSession") } } } @@ -2992,15 +2918,9 @@ public void SquishesLonelyNullables() } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "microsoft.graph.uploadSession", uploadSessionSchema - } - }, - }, }; + document.AddComponent("microsoft.graph.uploadSession", uploadSessionSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.SetOpenApiDocument(document); @@ -3022,28 +2942,22 @@ public void SquishesLonelyNullablesBothAnyOf() { var uploadSessionSchema = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AdditionalPropertiesAllowed = false, - Properties = new Dictionary { + Properties = new Dictionary { { "date", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, Format = "date-time" } }, { "temperature", new OpenApiSchema { - Type = "integer", + Type = JsonSchemaType.Integer, Format = "int32" } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.uploadSession", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -3063,10 +2977,9 @@ public void SquishesLonelyNullablesBothAnyOf() { Schema = new OpenApiSchema { - AnyOf = new List { - uploadSessionSchema, + AnyOf = new List { + new OpenApiSchemaReference("microsoft.graph.uploadSession"), new OpenApiSchema { - Nullable = true, } } } @@ -3078,15 +2991,9 @@ public void SquishesLonelyNullablesBothAnyOf() } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "microsoft.graph.uploadSession", uploadSessionSchema - } - }, - }, }; + document.AddComponent("microsoft.graph.uploadSession", uploadSessionSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.SetOpenApiDocument(document); @@ -3108,31 +3015,25 @@ public void SupportsArraysInComposedTypes() { var anyOfSchema = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AdditionalPropertiesAllowed = false, - Properties = new Dictionary { + Properties = new Dictionary { { "date", new OpenApiSchema { AnyOf = [ new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, }, new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, }, }, ] } } }, - Reference = new OpenApiReference - { - Id = "anyOfNullable", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -3150,7 +3051,7 @@ public void SupportsArraysInComposedTypes() Content = new Dictionary { ["application/json"] = new OpenApiMediaType { - Schema = anyOfSchema + Schema = new OpenApiSchemaReference("anyOfNullable") } } } @@ -3159,15 +3060,9 @@ public void SupportsArraysInComposedTypes() } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "anyOfNullable", anyOfSchema - } - }, - }, }; + document.AddComponent("anyOfNullable", anyOfSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.SetOpenApiDocument(document); @@ -3191,31 +3086,23 @@ public void SupportsNullableAnyOf() { var anyOfSchema = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AdditionalPropertiesAllowed = false, - Properties = new Dictionary { + Properties = new Dictionary { { "date", new OpenApiSchema { AnyOf = [ new OpenApiSchema { - Type = "string", - Nullable = true + Type = JsonSchemaType.String, }, new OpenApiSchema { - Type = "number", + Type = JsonSchemaType.Number, Format = "int64", - Nullable = true, } ] } } }, - Reference = new OpenApiReference - { - Id = "anyOfNullable", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -3233,7 +3120,7 @@ public void SupportsNullableAnyOf() Content = new Dictionary { ["application/json"] = new OpenApiMediaType { - Schema = anyOfSchema + Schema = new OpenApiSchemaReference("anyOfNullable") } } } @@ -3242,15 +3129,9 @@ public void SupportsNullableAnyOf() } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "anyOfNullable", anyOfSchema - } - }, - }, }; + document.AddComponent("anyOfNullable", anyOfSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.SetOpenApiDocument(document); @@ -3275,17 +3156,17 @@ public void AddsDiscriminatorMappings() { var entitySchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("#microsoft.graph.entity") + Type = JsonSchemaType.String, + Default = "#microsoft.graph.entity" } } }, @@ -3301,37 +3182,25 @@ public void AddsDiscriminatorMappings() } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.entity", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var directoryObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "tenant", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("#microsoft.graph.directoryObject") + Type = JsonSchemaType.String, + Default = "#microsoft.graph.directoryObject" } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.directoryObject", - Type = ReferenceType.Schema - }, - AllOf = new List { - entitySchema + AllOf = new List { + new OpenApiSchemaReference("microsoft.graph.entity") }, - UnresolvedReference = false }; var directoryObjects = new OpenApiResponse { @@ -3339,15 +3208,9 @@ public void AddsDiscriminatorMappings() { ["application/json"] = new OpenApiMediaType { - Schema = entitySchema + Schema = new OpenApiSchemaReference("microsoft.graph.entity") } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.directoryObjects", - Type = ReferenceType.Response - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -3360,29 +3223,17 @@ public void AddsDiscriminatorMappings() { Responses = new OpenApiResponses { - ["200"] = directoryObjects, + ["200"] = new OpenApiResponseReference("microsoft.graph.directoryObjects"), } } } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "microsoft.graph.entity", entitySchema - }, - { - "microsoft.graph.directoryObject", directoryObjectSchema - } - }, - Responses = new Dictionary { - { - "microsoft.graph.directoryObjects", directoryObjects - } - } - }, }; + document.AddComponent("microsoft.graph.entity", entitySchema); + document.AddComponent("microsoft.graph.directoryObject", directoryObjectSchema); + document.AddComponent("microsoft.graph.directoryObjects", directoryObjects); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -3410,17 +3261,17 @@ public void DoesntAddDiscriminatorMappingsOfNonDerivedTypes() { var entitySchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("#microsoft.graph.entity") + Type = JsonSchemaType.String, + Default = "#microsoft.graph.entity" } } }, @@ -3439,59 +3290,41 @@ public void DoesntAddDiscriminatorMappingsOfNonDerivedTypes() } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.entity", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var directoryObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "tenant", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("#microsoft.graph.directoryObject") + Type = JsonSchemaType.String, + Default = "#microsoft.graph.directoryObject" } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.directoryObject", - Type = ReferenceType.Schema - }, - AllOf = new List { - entitySchema + AllOf = new List { + new OpenApiSchemaReference("microsoft.graph.entity") }, - UnresolvedReference = false }; var fileSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "tenant", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("#microsoft.graph.file") + Type = JsonSchemaType.String, + Default = "#microsoft.graph.file" } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.file", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var directoryObjects = new OpenApiResponse() { @@ -3499,15 +3332,9 @@ public void DoesntAddDiscriminatorMappingsOfNonDerivedTypes() { ["application/json"] = new OpenApiMediaType() { - Schema = entitySchema + Schema = new OpenApiSchemaReference("microsoft.graph.entity") } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.directoryObjects", - Type = ReferenceType.Response - }, - UnresolvedReference = false }; var document = new OpenApiDocument() { @@ -3519,39 +3346,25 @@ public void DoesntAddDiscriminatorMappingsOfNonDerivedTypes() [OperationType.Get] = new OpenApiOperation() { Responses = new OpenApiResponses { - ["200"] = directoryObjects, + ["200"] = new OpenApiResponseReference("microsoft.graph.directoryObjects"), } } } } }, - Components = new OpenApiComponents() - { - Schemas = new Dictionary { - { - "microsoft.graph.entity", entitySchema - }, - { - "microsoft.graph.directoryObject", directoryObjectSchema - }, - { - "microsoft.graph.file", fileSchema - } - }, - Responses = new Dictionary { - { - "microsoft.graph.directoryObjects", directoryObjects - } - } - }, }; + document.AddComponent("microsoft.graph.entity", entitySchema); + document.AddComponent("microsoft.graph.directoryObject", directoryObjectSchema); + document.AddComponent("microsoft.graph.file", fileSchema); + document.AddComponent("microsoft.graph.directoryObjects", directoryObjects); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration() { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var entityClass = codeModel.FindChildByName("entity", true); - var directoryObjectClass = codeModel.FindChildByName("directoryObject", true); Assert.NotNull(entityClass); + Assert.NotNull(codeModel.FindChildByName("directoryObject", true)); var factoryMethod = entityClass.GetChildElements(true).OfType().FirstOrDefault(x => x.IsOfKind(CodeMethodKind.Factory)); Assert.NotNull(factoryMethod); Assert.Equal("@odata.type", entityClass.DiscriminatorInformation.DiscriminatorPropertyName); @@ -3562,17 +3375,17 @@ public async Task AddsDiscriminatorMappingsOneOfImplicitAsync() { var entitySchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("microsoft.graph.entity") + Type = JsonSchemaType.String, + Default = "microsoft.graph.entity" } } }, @@ -3583,51 +3396,33 @@ public async Task AddsDiscriminatorMappingsOneOfImplicitAsync() { PropertyName = "@odata.type", }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.entity", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var directoryObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "tenant", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("microsoft.graph.directoryObject") + Type = JsonSchemaType.String, + Default = "microsoft.graph.directoryObject" } } }, Required = new HashSet { "@odata.type" }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.directoryObject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var directoryObjectsResponse = new OpenApiSchema { - Type = "object", - OneOf = new List { - entitySchema, - directoryObjectSchema + Type = JsonSchemaType.Object, + OneOf = new List { + new OpenApiSchemaReference("microsoft.graph.entity"), + new OpenApiSchemaReference("microsoft.graph.directoryObject") }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.directoryObjects", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var directoryObjects = new OpenApiResponse { @@ -3635,15 +3430,9 @@ public async Task AddsDiscriminatorMappingsOneOfImplicitAsync() { ["application/json"] = new OpenApiMediaType { - Schema = directoryObjectsResponse + Schema = new OpenApiSchemaReference("microsoft.graph.directoryObjects") } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.directoryObjects", - Type = ReferenceType.Response - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -3656,32 +3445,18 @@ public async Task AddsDiscriminatorMappingsOneOfImplicitAsync() { Responses = new OpenApiResponses { - ["200"] = directoryObjects, + ["200"] = new OpenApiResponseReference("microsoft.graph.directoryObjects"), } } } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "microsoft.graph.entity", entitySchema - }, - { - "microsoft.graph.directoryObject", directoryObjectSchema - }, - { - "microsoft.graph.directoryObjects", directoryObjectsResponse - } - }, - Responses = new Dictionary { - { - "microsoft.graph.directoryObjects", directoryObjects - } - } - }, }; + document.AddComponent("microsoft.graph.entity", entitySchema); + document.AddComponent("microsoft.graph.directoryObject", directoryObjectSchema); + document.AddComponent("microsoft.graph.directoryObjects", directoryObjectsResponse); + document.AddComponent("microsoft.graph.directoryObjects", directoryObjects); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var config = new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }; var builder = new KiotaBuilder(mockLogger.Object, config, _httpClient); @@ -3705,17 +3480,17 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitAsync() { var entitySchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("#microsoft.graph.entity") + Type = JsonSchemaType.String, + Default = "#microsoft.graph.entity" } } }, @@ -3726,28 +3501,22 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitAsync() { PropertyName = "@odata.type", }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.entity", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var directoryObjectSchema = new OpenApiSchema { - Type = "object", - AllOf = new List { - entitySchema, + Type = JsonSchemaType.Object, + AllOf = new List { + new OpenApiSchemaReference("microsoft.graph.entity"), new OpenApiSchema { - Properties = new Dictionary { + Properties = new Dictionary { { "tenant", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("microsoft.graph.directoryObject") + Type = JsonSchemaType.String, + Default = "microsoft.graph.directoryObject" } } }, @@ -3756,28 +3525,22 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitAsync() } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.directoryObject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var userSchema = new OpenApiSchema { - Type = "object", - AllOf = new List { - directoryObjectSchema, + Type = JsonSchemaType.Object, + AllOf = new List { + new OpenApiSchemaReference("microsoft.graph.directoryObject"), new OpenApiSchema { - Properties = new Dictionary { + Properties = new Dictionary { { "firstName", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("microsoft.graph.firstName") + Type = JsonSchemaType.String, + Default = "microsoft.graph.firstName" } } }, @@ -3786,12 +3549,6 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitAsync() } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.user", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var directoryObjects = new OpenApiResponse { @@ -3799,15 +3556,9 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitAsync() { ["application/json"] = new OpenApiMediaType { - Schema = directoryObjectSchema + Schema = new OpenApiSchemaReference("microsoft.graph.directoryObject") } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.directoryObjects", - Type = ReferenceType.Response - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -3820,32 +3571,18 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitAsync() { Responses = new OpenApiResponses { - ["200"] = directoryObjects, + ["200"] = new OpenApiResponseReference("microsoft.graph.directoryObjects"), } } } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "microsoft.graph.entity", entitySchema - }, - { - "microsoft.graph.directoryObject", directoryObjectSchema - }, - { - "microsoft.graph.user", userSchema - } - }, - Responses = new Dictionary { - { - "microsoft.graph.directoryObjects", directoryObjects - } - } - }, }; + document.AddComponent("microsoft.graph.entity", entitySchema); + document.AddComponent("microsoft.graph.directoryObject", directoryObjectSchema); + document.AddComponent("microsoft.graph.user", userSchema); + document.AddComponent("microsoft.graph.directoryObjects", directoryObjects); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var config = new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }; var builder = new KiotaBuilder(mockLogger.Object, config, _httpClient); @@ -3874,6 +3611,7 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitAsync() var doFactoryMethod = directoryObjectClass.GetChildElements(true).OfType().FirstOrDefault(static x => x.IsOfKind(CodeMethodKind.Factory)); Assert.NotNull(doFactoryMethod); Assert.Single(directoryObjectClass.DiscriminatorInformation.DiscriminatorMappings); + Assert.DoesNotContain(directoryObjectClass.Properties, static x => x.Name.Equals("id", StringComparison.OrdinalIgnoreCase)); Assert.Contains("microsoft.graph.user", directoryObjectClass.DiscriminatorInformation.DiscriminatorMappings.Select(static x => x.Key)); Assert.Empty(userClass.DiscriminatorInformation.DiscriminatorMappings); } @@ -3883,17 +3621,17 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitWithParentHavingMappings { var entitySchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("#microsoft.graph.entity") + Type = JsonSchemaType.String, + Default = "#microsoft.graph.entity" } } }, @@ -3913,29 +3651,23 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitWithParentHavingMappings } } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.entity", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var directoryObjectSchema = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AllOf = [ - entitySchema, + new OpenApiSchemaReference("microsoft.graph.entity"), new OpenApiSchema { - Properties = new Dictionary { + Properties = new Dictionary { { "tenant", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("microsoft.graph.directoryObject") + Type = JsonSchemaType.String, + Default = "microsoft.graph.directoryObject" } } }, @@ -3944,29 +3676,23 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitWithParentHavingMappings } } ], - Reference = new OpenApiReference - { - Id = "microsoft.graph.directoryObject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var userSchema = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AllOf = [ - directoryObjectSchema, + new OpenApiSchemaReference("microsoft.graph.directoryObject"), new OpenApiSchema { - Properties = new Dictionary { + Properties = new Dictionary { { "firstName", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "@odata.type", new OpenApiSchema { - Type = "string", - Default = new OpenApiString("microsoft.graph.firstName") + Type = JsonSchemaType.String, + Default = "microsoft.graph.firstName" } } }, @@ -3975,12 +3701,6 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitWithParentHavingMappings } } ], - Reference = new OpenApiReference - { - Id = "microsoft.graph.user", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var directoryObjects = new OpenApiResponse { @@ -3988,15 +3708,9 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitWithParentHavingMappings { ["application/json"] = new OpenApiMediaType { - Schema = directoryObjectSchema + Schema = new OpenApiSchemaReference("microsoft.graph.directoryObject") } }, - Reference = new OpenApiReference - { - Id = "microsoft.graph.directoryObjects", - Type = ReferenceType.Response - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -4009,32 +3723,18 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitWithParentHavingMappings { Responses = new OpenApiResponses { - ["200"] = directoryObjects, + ["200"] = new OpenApiResponseReference("microsoft.graph.directoryObjects"), } } } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "microsoft.graph.entity", entitySchema - }, - { - "microsoft.graph.directoryObject", directoryObjectSchema - }, - { - "microsoft.graph.user", userSchema - } - }, - Responses = new Dictionary { - { - "microsoft.graph.directoryObjects", directoryObjects - } - } - }, }; + document.AddComponent("microsoft.graph.entity", entitySchema); + document.AddComponent("microsoft.graph.directoryObject", directoryObjectSchema); + document.AddComponent("microsoft.graph.user", userSchema); + document.AddComponent("microsoft.graph.directoryObjects", directoryObjects); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var config = new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }; var builder = new KiotaBuilder(mockLogger.Object, config, _httpClient); @@ -4064,20 +3764,14 @@ public void UnionOfPrimitiveTypesWorks() { var simpleObjet = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "subNS.simpleObject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -4094,10 +3788,10 @@ public void UnionOfPrimitiveTypesWorks() Content = { ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - OneOf = new List { - simpleObjet, + OneOf = new List { + new OpenApiSchemaReference("subNS.simpleObject"), new OpenApiSchema { - Type = "number" + Type = JsonSchemaType.Number } } } @@ -4109,15 +3803,9 @@ public void UnionOfPrimitiveTypesWorks() } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "subNS.simpleObject", simpleObjet - } - } - }, }; + document.AddComponent("subNS.simpleObject", simpleObjet); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -4182,20 +3870,14 @@ public void UnionOfInlineSchemasWorks() { var simpleObjet = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "subNS.simpleObject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -4212,14 +3894,14 @@ public void UnionOfInlineSchemasWorks() Content = { ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - OneOf = new List { - simpleObjet, + OneOf = new List { + new OpenApiSchemaReference("subNS.simpleObject"), new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } } @@ -4234,15 +3916,9 @@ public void UnionOfInlineSchemasWorks() } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "subNS.simpleObject", simpleObjet - } - } - }, }; + document.AddComponent("subNS.simpleObject", simpleObjet); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -4265,20 +3941,14 @@ public void IntersectionOfPrimitiveTypesWorks() { var simpleObjet = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "subNS.simpleObject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -4295,10 +3965,10 @@ public void IntersectionOfPrimitiveTypesWorks() Content = { ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - AnyOf = new List { - simpleObjet, + AnyOf = new List { + new OpenApiSchemaReference("subNS.simpleObject"), new OpenApiSchema { - Type = "number" + Type = JsonSchemaType.Number } } } @@ -4310,15 +3980,9 @@ public void IntersectionOfPrimitiveTypesWorks() } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "subNS.simpleObject", simpleObjet - } - } - }, }; + document.AddComponent("subNS.simpleObject", simpleObjet); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -4341,20 +4005,14 @@ public void IntersectionOfInlineSchemasWorks() { var simpleObjet = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "subNS.simpleObject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -4371,14 +4029,14 @@ public void IntersectionOfInlineSchemasWorks() Content = { ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { - AnyOf = new List { - simpleObjet, + AnyOf = new List { + new OpenApiSchemaReference("subNS.simpleObject"), new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } } @@ -4393,15 +4051,9 @@ public void IntersectionOfInlineSchemasWorks() } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "subNS.simpleObject", simpleObjet - } - } - }, }; + document.AddComponent("subNS.simpleObject", simpleObjet); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -4424,16 +4076,16 @@ public void InheritedTypeWithInlineSchemaWorks() { var baseObject = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "kind", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, @@ -4446,25 +4098,19 @@ public void InheritedTypeWithInlineSchemaWorks() } } }, - Reference = new OpenApiReference - { - Id = "subNS.baseObject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var derivedObject = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AllOf = [ - baseObject, + new OpenApiSchemaReference("subNS.baseObject"), new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "special", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, @@ -4479,36 +4125,24 @@ public void InheritedTypeWithInlineSchemaWorks() }, } ], - Reference = new OpenApiReference - { - Id = "subNS.derivedObject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var secondLevelDerivedObject = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AllOf = [ - derivedObject, + new OpenApiSchemaReference("subNS.derivedObject"), new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "moreSpecial", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } } } ], - Reference = new OpenApiReference - { - Id = "subNS.secondLevelDerivedObject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -4524,7 +4158,7 @@ public void InheritedTypeWithInlineSchemaWorks() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = derivedObject + Schema = new OpenApiSchemaReference("subNS.derivedObject") } } }, @@ -4533,21 +4167,11 @@ public void InheritedTypeWithInlineSchemaWorks() } } }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "subNS.baseObject", baseObject - }, - { - "subNS.derivedObject", derivedObject - }, - { - "subNS.secondLevelDerivedObject", secondLevelDerivedObject - } - } - }, }; + document.AddComponent("subNS.baseObject", baseObject); + document.AddComponent("subNS.derivedObject", derivedObject); + document.AddComponent("subNS.secondLevelDerivedObject", secondLevelDerivedObject); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -4568,38 +4192,66 @@ public void InheritedTypeWithInlineSchemaWorks() Assert.Equal("kind", derivedObjectClass.DiscriminatorInformation.DiscriminatorPropertyName); Assert.NotEmpty(derivedObjectClass.DiscriminatorInformation.DiscriminatorMappings); } - [InlineData("string", "", "string")]// https://spec.openapis.org/registry/format/ - [InlineData("string", "commonmark", "string")] - [InlineData("string", "html", "string")] - [InlineData("string", "date-time", "DateTimeOffset")] - [InlineData("string", "duration", "TimeSpan")] - [InlineData("string", "date", "DateOnly")] - [InlineData("string", "time", "TimeOnly")] - [InlineData("string", "base64url", "base64url")] - [InlineData("string", "uuid", "Guid")] + [InlineData(JsonSchemaType.String, "", "string")]// https://spec.openapis.org/registry/format/ + [InlineData(JsonSchemaType.String, "commonmark", "string")] + [InlineData(JsonSchemaType.String, "html", "string")] + [InlineData(JsonSchemaType.String, "date-time", "DateTimeOffset")] + [InlineData(JsonSchemaType.String, "duration", "TimeSpan")] + [InlineData(JsonSchemaType.String, "date", "DateOnly")] + [InlineData(JsonSchemaType.String, "time", "TimeOnly")] + [InlineData(JsonSchemaType.String, "base64url", "base64url")] + [InlineData(JsonSchemaType.String, "uuid", "Guid")] + // floating points can only be declared as numbers + [InlineData(JsonSchemaType.Number, "double", "double")] + [InlineData(JsonSchemaType.Number, "float", "float")] + [InlineData(JsonSchemaType.Number, "decimal", "decimal")] + // integers can only be declared as numbers or integers + [InlineData(JsonSchemaType.Number, "int32", "integer")] + [InlineData(JsonSchemaType.Integer, "int32", "integer")] + [InlineData(JsonSchemaType.Number, "int64", "int64")] + [InlineData(JsonSchemaType.Integer, "int64", "int64")] + [InlineData(JsonSchemaType.Number, "int8", "sbyte")] + [InlineData(JsonSchemaType.Integer, "int8", "sbyte")] + [InlineData(JsonSchemaType.Number, "int16", "integer")] + [InlineData(JsonSchemaType.Integer, "int16", "integer")] + [InlineData(JsonSchemaType.Number, "uint8", "byte")] + [InlineData(JsonSchemaType.Integer, "uint8", "byte")] + [InlineData(JsonSchemaType.Number, "", "double")] + [InlineData(JsonSchemaType.Integer, "", "integer")] + [InlineData(JsonSchemaType.Boolean, "", "boolean")] + [InlineData(JsonSchemaType.String, "byte", "base64")] + [InlineData(JsonSchemaType.String, "binary", "binary")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "", "string")]// https://spec.openapis.org/registry/format/ + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "commonmark", "string")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "html", "string")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "date-time", "DateTimeOffset")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "duration", "TimeSpan")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "date", "DateOnly")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "time", "TimeOnly")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "base64url", "base64url")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "uuid", "Guid")] // floating points can only be declared as numbers - [InlineData("number", "double", "double")] - [InlineData("number", "float", "float")] - [InlineData("number", "decimal", "decimal")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "double", "double")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "float", "float")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "decimal", "decimal")] // integers can only be declared as numbers or integers - [InlineData("number", "int32", "integer")] - [InlineData("integer", "int32", "integer")] - [InlineData("number", "int64", "int64")] - [InlineData("integer", "int64", "int64")] - [InlineData("number", "int8", "sbyte")] - [InlineData("integer", "int8", "sbyte")] - [InlineData("number", "int16", "integer")] - [InlineData("integer", "int16", "integer")] - [InlineData("number", "uint8", "byte")] - [InlineData("integer", "uint8", "byte")] - [InlineData("number", "", "double")] - [InlineData("integer", "", "integer")] - [InlineData("boolean", "", "boolean")] - [InlineData("", "byte", "base64")] - [InlineData("", "binary", "binary")] - [InlineData("file", null, "binary")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "int32", "integer")] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, "int32", "integer")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "int64", "int64")] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, "int64", "int64")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "int8", "sbyte")] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, "int8", "sbyte")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "int16", "integer")] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, "int16", "integer")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "uint8", "byte")] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, "uint8", "byte")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "", "double")] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, "", "integer")] + [InlineData(JsonSchemaType.Boolean | JsonSchemaType.Null, "", "boolean")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "byte", "base64")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "binary", "binary")] [Theory] - public void MapsPrimitiveFormats(string type, string format, string expected) + public void MapsPrimitiveFormats(JsonSchemaType type, string format, string expected) { var document = new OpenApiDocument { @@ -4639,35 +4291,102 @@ public void MapsPrimitiveFormats(string type, string format, string expected) Assert.Equal(expected, method.ReturnType.Name); Assert.True(method.ReturnType.AllTypes.First().IsExternal); } - [InlineData("string", "", "string")]// https://spec.openapis.org/registry/format/ - [InlineData("string", "commonmark", "string")] - [InlineData("string", "html", "string")] - [InlineData("string", "date-time", "DateTimeOffset")] - [InlineData("string", "duration", "TimeSpan")] - [InlineData("string", "date", "DateOnly")] - [InlineData("string", "time", "TimeOnly")] - [InlineData("string", "base64url", "base64url")] - // floating points can only be declared as numbers - [InlineData("number", "double", "double")] - [InlineData("number", "float", "float")] - [InlineData("number", "decimal", "decimal")] - // integers can only be declared as numbers or integers - [InlineData("number", "int32", "integer")] - [InlineData("integer", "int32", "integer")] - [InlineData("number", "int64", "int64")] - [InlineData("integer", "int64", "int64")] - [InlineData("number", "int8", "sbyte")] - [InlineData("integer", "int8", "sbyte")] - [InlineData("number", "uint8", "byte")] - [InlineData("integer", "uint8", "byte")] - [InlineData("number", "", "double")] - [InlineData("integer", "", "integer")] - [InlineData("boolean", "", "boolean")] - [InlineData("", "byte", "base64")] - [InlineData("", "binary", "binary")] - [InlineData("file", null, "binary")] - [Theory] - public void MapsQueryParameterTypes(string type, string format, string expected) + [InlineData(JsonSchemaType.String, "", "string")]// https://spec.openapis.org/registry/format/ + [InlineData(JsonSchemaType.String, "commonmark", "string")] + [InlineData(JsonSchemaType.String, "html", "string")] + [InlineData(JsonSchemaType.String, "date-time", "DateTimeOffset")] + [InlineData(JsonSchemaType.String, "duration", "TimeSpan")] + [InlineData(JsonSchemaType.String, "date", "DateOnly")] + [InlineData(JsonSchemaType.String, "time", "TimeOnly")] + [InlineData(JsonSchemaType.String, "base64url", "base64url")] + // floating points can only be declared as numbers + [InlineData(JsonSchemaType.Number, "double", "double")] + [InlineData(JsonSchemaType.Number, "float", "float")] + [InlineData(JsonSchemaType.Number, "decimal", "decimal")] + // integers can only be declared as numbers or integers + [InlineData(JsonSchemaType.Number, "int32", "integer")] + [InlineData(JsonSchemaType.Integer, "int32", "integer")] + [InlineData(JsonSchemaType.Number, "int64", "int64")] + [InlineData(JsonSchemaType.Integer, "int64", "int64")] + [InlineData(JsonSchemaType.Number, "int8", "sbyte")] + [InlineData(JsonSchemaType.Integer, "int8", "sbyte")] + [InlineData(JsonSchemaType.Number, "uint8", "byte")] + [InlineData(JsonSchemaType.Integer, "uint8", "byte")] + [InlineData(JsonSchemaType.Number, "", "double")] + [InlineData(JsonSchemaType.Integer, "", "integer")] + [InlineData(JsonSchemaType.Boolean, "", "boolean")] + [InlineData(JsonSchemaType.String, "byte", "base64")] + [InlineData(JsonSchemaType.String, "binary", "binary")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "", "string")]// https://spec.openapis.org/registry/format/ + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "commonmark", "string")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "html", "string")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "date-time", "DateTimeOffset")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "duration", "TimeSpan")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "date", "DateOnly")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "time", "TimeOnly")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "base64url", "base64url")] + // floating points can only be declared as numbers + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "double", "double")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "float", "float")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "decimal", "decimal")] + // integers can only be declared as numbers or integers + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "int32", "integer")] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, "int32", "integer")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "int64", "int64")] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, "int64", "int64")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "int8", "sbyte")] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, "int8", "sbyte")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "uint8", "byte")] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, "uint8", "byte")] + [InlineData(JsonSchemaType.Number | JsonSchemaType.Null, "", "double")] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, "", "integer")] + [InlineData(JsonSchemaType.Boolean | JsonSchemaType.Null, "", "boolean")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "byte", "base64")] + [InlineData(JsonSchemaType.String | JsonSchemaType.Null, "binary", "binary")] + [Theory] + public void MapsQueryParameterTypes(JsonSchemaType type, string format, string expected) + { + var document = new OpenApiDocument + { + Paths = new OpenApiPaths + { + ["primitive"] = new OpenApiPathItem + { + Operations = { + [OperationType.Get] = new OpenApiOperation + { + Parameters = new List { + new OpenApiParameter() { + Name = "query", + In = ParameterLocation.Query, + Schema = new OpenApiSchema { + Type = type, + Format = format + } + } + }, + Responses = new OpenApiResponses + { + ["204"] = new OpenApiResponse() + } + } + } + } + }, + }; + var mockLogger = new Mock>(); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); + var node = builder.CreateUriSpace(document); + var codeModel = builder.CreateSourceModel(node); + var queryParameters = codeModel.FindChildByName("primitiveRequestBuilderGetQueryParameters"); + Assert.NotNull(queryParameters); + var property = queryParameters.Properties.First(static x => x.Name.Equals("query", StringComparison.OrdinalIgnoreCase)); + Assert.NotNull(property); + Assert.Equal(expected, property.Type.Name); + Assert.True(property.Type.AllTypes.First().IsExternal); + } + [Fact] + public void MapsArrayOfTypesAsUnionType() { var document = new OpenApiDocument { @@ -4678,19 +4397,17 @@ public void MapsQueryParameterTypes(string type, string format, string expected) Operations = { [OperationType.Get] = new OpenApiOperation { - Parameters = new List { - new() { - Name = "query", - In = ParameterLocation.Query, - Schema = new OpenApiSchema { - Type = type, - Format = format - } - } - }, Responses = new OpenApiResponses { - ["204"] = new OpenApiResponse() + ["200"] = new OpenApiResponse { + Content = { + ["application/json"] = new OpenApiMediaType { + Schema = new OpenApiSchema { + Type = JsonSchemaType.Number | JsonSchemaType.String, + } + } + } + }, } } } @@ -4701,12 +4418,14 @@ public void MapsQueryParameterTypes(string type, string format, string expected) var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); - var queryParameters = codeModel.FindChildByName("primitiveRequestBuilderGetQueryParameters"); - Assert.NotNull(queryParameters); - var property = queryParameters.Properties.First(static x => x.Name.Equals("query", StringComparison.OrdinalIgnoreCase)); - Assert.NotNull(property); - Assert.Equal(expected, property.Type.Name); - Assert.True(property.Type.AllTypes.First().IsExternal); + var requestBuilder = codeModel.FindChildByName("primitiveRequestBuilder"); + Assert.NotNull(requestBuilder); + var method = requestBuilder.GetChildElements(true).OfType().FirstOrDefault(x => x.IsOfKind(CodeMethodKind.RequestExecutor)); + Assert.NotNull(method); + var unionType = Assert.IsType(method.ReturnType); + Assert.Equal(2, unionType.Types.Count()); + Assert.Contains("string", unionType.Types.Select(static x => x.Name), StringComparer.OrdinalIgnoreCase); + Assert.Contains("double", unionType.Types.Select(static x => x.Name), StringComparer.OrdinalIgnoreCase); } [Fact] public void MapsQueryParameterArrayTypes() @@ -4720,14 +4439,14 @@ public void MapsQueryParameterArrayTypes() Operations = { [OperationType.Get] = new OpenApiOperation { - Parameters = new List { - new() { + Parameters = new List { + new OpenApiParameter() { Name = "query", In = ParameterLocation.Query, Schema = new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - Type = "integer", + Type = JsonSchemaType.Integer, Format = "int64" } } @@ -4768,15 +4487,15 @@ public void MapsEnumQueryParameterType(GenerationLanguage generationLanguage) Operations = { [OperationType.Get] = new OpenApiOperation { - Parameters = new List { - new() { + Parameters = new List { + new OpenApiParameter() { Name = "query", In = ParameterLocation.Query, Schema = new OpenApiSchema { - Type = "string", - Enum = new List { - new OpenApiString("value1"), - new OpenApiString("value2") + Type = JsonSchemaType.String, + Enum = new List { + "value1", + "value2" } } } @@ -4883,12 +4602,12 @@ public void MapsQueryParameterCollectionKinds(bool isArray) { var baseSchema = new OpenApiSchema { - Type = "number", + Type = JsonSchemaType.Number, Format = "int64" }; var arraySchema = new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = baseSchema }; var document = new OpenApiDocument @@ -4900,7 +4619,7 @@ public void MapsQueryParameterCollectionKinds(bool isArray) Operations = { [OperationType.Get] = new OpenApiOperation { - Parameters = new List { + Parameters = new List { new OpenApiParameter { Name = "query", In = ParameterLocation.Query, @@ -4940,7 +4659,7 @@ public void DefaultsQueryParametersWithNoSchemaToString() Operations = { [OperationType.Get] = new OpenApiOperation { - Parameters = new List { + Parameters = new List { new OpenApiParameter { Name = "query", In = ParameterLocation.Query @@ -4971,20 +4690,14 @@ public void DoesntGenerateNamespacesWhenNotRequired() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -5000,7 +4713,7 @@ public void DoesntGenerateNamespacesWhenNotRequired() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -5009,15 +4722,9 @@ public void DoesntGenerateNamespacesWhenNotRequired() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -5034,20 +4741,14 @@ public void GeneratesNamesapacesWhenRequired() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "subns.myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -5063,7 +4764,7 @@ public void GeneratesNamesapacesWhenRequired() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("subns.myobject") } } }, @@ -5072,15 +4773,9 @@ public void GeneratesNamesapacesWhenRequired() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "subns.myobject", myObjectSchema - } - } - } }; + document.AddComponent("subns.myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -5097,20 +4792,14 @@ public void IdsResultInIndexers() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -5126,7 +4815,7 @@ public void IdsResultInIndexers() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -5135,15 +4824,9 @@ public void IdsResultInIndexers() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -5164,46 +4847,39 @@ public void HandlesCollectionOfEnumSchemasInAnyOfWithNullable() var enumSchema = new OpenApiSchema { Title = "riskLevel", - Enum = new List + Enum = new List { - new OpenApiString("low"), - new OpenApiString("medium"), - new OpenApiString("high"), - new OpenApiString("hidden"), - new OpenApiString("none"), - new OpenApiString("unknownFutureValue") + "low", + "medium", + "high", + "hidden", + "none", + "unknownFutureValue" }, - Type = "string" + Type = JsonSchemaType.String }; var myObjectSchema = new OpenApiSchema { Title = "conditionalAccessConditionSet", - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "signInRiskLevels", new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - AnyOf = new List + AnyOf = new List { - enumSchema, + new OpenApiSchemaReference("riskLevel"), new OpenApiSchema { - Type = "object", - Nullable = true + Type = JsonSchemaType.Object, } } } } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument @@ -5220,7 +4896,7 @@ public void HandlesCollectionOfEnumSchemasInAnyOfWithNullable() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -5228,20 +4904,11 @@ public void HandlesCollectionOfEnumSchemasInAnyOfWithNullable() } } } - }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - }, - { - "riskLevel", enumSchema - } - }, - } }; + document.AddComponent("myobject", myObjectSchema); + document.AddComponent("riskLevel", enumSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -5264,35 +4931,29 @@ public void HandlesCollectionOfEnumSchemas() var enumSchema = new OpenApiSchema { Title = "riskLevel", - Enum = new List + Enum = new List { - new OpenApiString("low"), - new OpenApiString("medium"), - new OpenApiString("high"), - new OpenApiString("hidden"), - new OpenApiString("none"), - new OpenApiString("unknownFutureValue") + "low", + "medium", + "high", + "hidden", + "none", + "unknownFutureValue" }, - Type = "string" + Type = JsonSchemaType.String }; var myObjectSchema = new OpenApiSchema { Title = "conditionalAccessConditionSet", - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "signInRiskLevels", new OpenApiSchema { - Type = "array", - Items = enumSchema + Type = JsonSchemaType.Array, + Items = new OpenApiSchemaReference("riskLevel") } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument @@ -5309,7 +4970,7 @@ public void HandlesCollectionOfEnumSchemas() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -5318,19 +4979,10 @@ public void HandlesCollectionOfEnumSchemas() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - }, - { - "riskLevel", enumSchema - } - }, - - } }; + document.AddComponent("myobject", myObjectSchema); + document.AddComponent("riskLevel", enumSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -5352,23 +5004,17 @@ public void InlinePropertiesGenerateTypes() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "tilleggsinformasjon", new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AdditionalProperties = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -5384,7 +5030,7 @@ public void InlinePropertiesGenerateTypes() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -5392,16 +5038,10 @@ public void InlinePropertiesGenerateTypes() } } } - }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -5419,20 +5059,14 @@ public void ModelsDoesntUsePathDescriptionWhenAvailable() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -5452,7 +5086,7 @@ public void ModelsDoesntUsePathDescriptionWhenAvailable() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -5460,16 +5094,10 @@ public void ModelsDoesntUsePathDescriptionWhenAvailable() } } } - }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -5485,20 +5113,14 @@ public void CleansUpInvalidDescriptionCharacters() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, Description = @" some description with invalid characters: ", }; @@ -5516,7 +5138,7 @@ public void CleansUpInvalidDescriptionCharacters() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -5525,15 +5147,9 @@ public void CleansUpInvalidDescriptionCharacters() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -5553,20 +5169,14 @@ public void AcceptVendorsTypes(string contentType) { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -5582,7 +5192,7 @@ public void AcceptVendorsTypes(string contentType) ["200"] = new OpenApiResponse { Content = { [contentType] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -5591,15 +5201,9 @@ public void AcceptVendorsTypes(string contentType) } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -5637,10 +5241,10 @@ public void ModelsUseDescriptionWhenAvailable(bool excludeBackwardCompatible) ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { Description = "some description", - Properties = new Dictionary { + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } } @@ -5693,37 +5297,25 @@ public void Considers200WithSchemaOver2XXWithSchema() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var myOtherObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myotherobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -5739,14 +5331,14 @@ public void Considers200WithSchemaOver2XXWithSchema() ["2XX"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myOtherObjectSchema + Schema = new OpenApiSchemaReference("myotherobject") } } }, ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -5755,14 +5347,10 @@ public void Considers200WithSchemaOver2XXWithSchema() } } }, - Components = new() - { - Schemas = new Dictionary { - { "myobject", myObjectSchema }, - { "myotherobject", myOtherObjectSchema }, - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.AddComponent("myotherobject", myOtherObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder( mockLogger.Object, @@ -5788,20 +5376,14 @@ public void Considers2XXWithSchemaOver204WithNoSchema() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -5817,7 +5399,7 @@ public void Considers2XXWithSchemaOver204WithNoSchema() ["2XX"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -5827,15 +5409,9 @@ public void Considers2XXWithSchemaOver204WithNoSchema() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder( mockLogger.Object, @@ -5859,23 +5435,6 @@ public void Considers2XXWithSchemaOver204WithNoSchema() [Fact] public void Considers204WithNoSchemaOver206WithNoSchema() { - var myObjectSchema = new OpenApiSchema - { - Type = "object", - Properties = new Dictionary { - { - "id", new OpenApiSchema { - Type = "string", - } - } - }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false - }; var document = new OpenApiDocument { Paths = new OpenApiPaths @@ -5894,14 +5453,6 @@ public void Considers204WithNoSchemaOver206WithNoSchema() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; var mockLogger = new Mock>(); var builder = new KiotaBuilder( @@ -5934,20 +5485,14 @@ public void DoesntGenerateVoidExecutorOnMixedNoContent(int statusCode) { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -5963,7 +5508,7 @@ public void DoesntGenerateVoidExecutorOnMixedNoContent(int statusCode) ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -5973,15 +5518,9 @@ public void DoesntGenerateVoidExecutorOnMixedNoContent(int statusCode) } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -6006,20 +5545,14 @@ public void GeneratesVoidReturnTypeForNoContent(int statusCode) { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -6035,7 +5568,7 @@ public void GeneratesVoidReturnTypeForNoContent(int statusCode) [statusCode.ToString()] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6044,15 +5577,9 @@ public void GeneratesVoidReturnTypeForNoContent(int statusCode) } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -6072,31 +5599,21 @@ public void GeneratesVoidReturnTypeForNoContent(int statusCode) [Theory] public void StripsCommonModelsPrefix(string[] componentNames, string stripPrefix) { - var paths = new OpenApiPaths(); - var components = new OpenApiComponents - { - Schemas = new Dictionary() - }; + var document = new OpenApiDocument(); foreach (var componentName in componentNames) { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = componentName, - Type = ReferenceType.Schema - }, - UnresolvedReference = false }; - paths.Add($"answer{componentName}", new OpenApiPathItem + document.Paths.Add($"answer{componentName}", new OpenApiPathItem { Operations = { [OperationType.Get] = new OpenApiOperation @@ -6106,7 +5623,7 @@ public void StripsCommonModelsPrefix(string[] componentNames, string stripPrefix ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference(componentName) } } }, @@ -6114,13 +5631,10 @@ public void StripsCommonModelsPrefix(string[] componentNames, string stripPrefix } } }); - components.Schemas.Add(componentName, myObjectSchema); + document.AddComponent(componentName, myObjectSchema); } - var document = new OpenApiDocument - { - Paths = paths, - Components = components, - }; + document.SetReferenceHostDocument(); + var result = KiotaBuilder.GetDeeperMostCommonNamespaceNameForModels(document); Assert.Equal(stripPrefix, result); } @@ -6129,20 +5643,14 @@ public void HandlesContentParameters() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -6153,7 +5661,7 @@ public void HandlesContentParameters() Operations = { [OperationType.Get] = new OpenApiOperation { - Parameters = new List { + Parameters = new List { new OpenApiParameter { Name = "ids", In = ParameterLocation.Path, @@ -6162,9 +5670,9 @@ public void HandlesContentParameters() { "application/json", new OpenApiMediaType { Schema = new OpenApiSchema { - Type = "array", + Type = JsonSchemaType.Array, Items = new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } } @@ -6177,7 +5685,7 @@ public void HandlesContentParameters() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6186,15 +5694,9 @@ public void HandlesContentParameters() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -6216,20 +5718,14 @@ public void HandlesPagingExtension() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -6248,7 +5744,7 @@ public void HandlesPagingExtension() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6257,15 +5753,9 @@ public void HandlesPagingExtension() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -6285,21 +5775,15 @@ public void SetsReadonlyProperties(bool isReadonly) { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, ReadOnly = isReadonly, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -6315,7 +5799,7 @@ public void SetsReadonlyProperties(bool isReadonly) ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6323,16 +5807,10 @@ public void SetsReadonlyProperties(bool isReadonly) } } } - }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -6349,20 +5827,14 @@ public void SupportsIncludeFilterOnRootPath(string inputPattern, int expectedPat { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -6378,7 +5850,7 @@ public void SupportsIncludeFilterOnRootPath(string inputPattern, int expectedPat ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6387,15 +5859,9 @@ public void SupportsIncludeFilterOnRootPath(string inputPattern, int expectedPat } }, }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { @@ -6414,20 +5880,14 @@ public void SupportsIncludeFilter() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -6443,7 +5903,7 @@ public void SupportsIncludeFilter() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6461,7 +5921,7 @@ public void SupportsIncludeFilter() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6469,16 +5929,10 @@ public void SupportsIncludeFilter() } } }, - }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { @@ -6499,20 +5953,14 @@ public void SupportsExcludeFilter() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -6528,7 +5976,7 @@ public void SupportsExcludeFilter() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6546,7 +5994,7 @@ public void SupportsExcludeFilter() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6554,16 +6002,10 @@ public void SupportsExcludeFilter() } } }, - }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { @@ -6584,20 +6026,14 @@ public void SupportsIncludeFilterWithOperation() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -6613,7 +6049,7 @@ public void SupportsIncludeFilterWithOperation() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6624,7 +6060,7 @@ public void SupportsIncludeFilterWithOperation() RequestBody = new OpenApiRequestBody { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6633,7 +6069,7 @@ public void SupportsIncludeFilterWithOperation() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6644,7 +6080,7 @@ public void SupportsIncludeFilterWithOperation() RequestBody = new OpenApiRequestBody { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6653,7 +6089,7 @@ public void SupportsIncludeFilterWithOperation() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6671,7 +6107,7 @@ public void SupportsIncludeFilterWithOperation() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6689,7 +6125,7 @@ public void SupportsIncludeFilterWithOperation() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6697,16 +6133,10 @@ public void SupportsIncludeFilterWithOperation() } } }, - }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { @@ -6739,20 +6169,14 @@ public void SupportsIndexingParametersInSubPaths() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -6763,13 +6187,13 @@ public void SupportsIndexingParametersInSubPaths() Operations = { [OperationType.Get] = new OpenApiOperation { - Parameters = new List { + Parameters = new List { new OpenApiParameter { Name = "userId", In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, @@ -6778,7 +6202,7 @@ public void SupportsIndexingParametersInSubPaths() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -6786,16 +6210,10 @@ public void SupportsIndexingParametersInSubPaths() } } }, - }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { @@ -7065,11 +6483,11 @@ public async Task IndexerTypeIsAccurateAndBackwardCompatibleIndexersAreAddedAsyn Assert.Equal("The id of the author's posts to retrieve", authorsCollectionIndexer.IndexParameter.Documentation.DescriptionTemplate, StringComparer.OrdinalIgnoreCase); Assert.False(authorsCollectionIndexer.IndexParameter.Type.IsNullable); Assert.False(authorsCollectionIndexer.Deprecation.IsDeprecated); - var authorsCllectionStringIndexer = authorsCollectionRequestBuilder.FindChildByName($"{authorsCollectionIndexer.Name}-string"); - Assert.NotNull(authorsCllectionStringIndexer); - Assert.Equal("string", authorsCllectionStringIndexer.IndexParameter.Type.Name); - Assert.True(authorsCllectionStringIndexer.IndexParameter.Type.IsNullable); - Assert.True(authorsCllectionStringIndexer.Deprecation.IsDeprecated); + var authorsCollectionStringIndexer = authorsCollectionRequestBuilder.FindChildByName($"{authorsCollectionIndexer.Name}-string"); + Assert.NotNull(authorsCollectionStringIndexer); + Assert.Equal("string", authorsCollectionStringIndexer.IndexParameter.Type.Name); + Assert.True(authorsCollectionStringIndexer.IndexParameter.Type.IsNullable); + Assert.True(authorsCollectionStringIndexer.Deprecation.IsDeprecated); var authorsItemRequestBuilderNamespace = codeModel.FindNamespaceByName("ApiSdk.authors.item"); Assert.NotNull(authorsItemRequestBuilderNamespace); var authorsItemRequestBuilder = authorsItemRequestBuilderNamespace.FindChildByName("authorItemRequestBuilder"); @@ -7084,11 +6502,11 @@ public async Task IndexerTypeIsAccurateAndBackwardCompatibleIndexersAreAddedAsyn Assert.Equal("The id of the actor", actorsCollectionIndexer.IndexParameter.Documentation.DescriptionTemplate, StringComparer.OrdinalIgnoreCase); Assert.False(actorsCollectionIndexer.IndexParameter.Type.IsNullable); Assert.False(actorsCollectionIndexer.Deprecation.IsDeprecated); - var actorsCllectionStringIndexer = actorsCollectionRequestBuilder.FindChildByName($"{actorsCollectionIndexer.Name}-string"); - Assert.NotNull(actorsCllectionStringIndexer); - Assert.Equal("string", actorsCllectionStringIndexer.IndexParameter.Type.Name); - Assert.True(actorsCllectionStringIndexer.IndexParameter.Type.IsNullable); - Assert.True(actorsCllectionStringIndexer.Deprecation.IsDeprecated); + var actorsCollectionStringIndexer = actorsCollectionRequestBuilder.FindChildByName($"{actorsCollectionIndexer.Name}-string"); + Assert.NotNull(actorsCollectionStringIndexer); + Assert.Equal("string", actorsCollectionStringIndexer.IndexParameter.Type.Name); + Assert.True(actorsCollectionStringIndexer.IndexParameter.Type.IsNullable); + Assert.True(actorsCollectionStringIndexer.Deprecation.IsDeprecated); var actorsItemRequestBuilderNamespace = codeModel.FindNamespaceByName("ApiSdk.actors.item"); Assert.NotNull(actorsItemRequestBuilderNamespace); var actorsItemRequestBuilder = actorsItemRequestBuilderNamespace.FindChildByName("actorItemRequestBuilder"); @@ -7210,24 +6628,19 @@ public void AddReservedPathParameterSymbol() { var userSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "displayName", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "#/components/schemas/microsoft.graph.user" - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -7235,13 +6648,13 @@ public void AddReservedPathParameterSymbol() { ["users/{id}/manager"] = new OpenApiPathItem { - Parameters = new List { + Parameters = new List { new OpenApiParameter { Name = "id", In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }, Extensions = { ["x-ms-reserved-parameter"] = new OpenApiReservedParameterExtension { @@ -7259,7 +6672,7 @@ public void AddReservedPathParameterSymbol() Content = { ["application/json"] = new OpenApiMediaType { - Schema = userSchema + Schema = new OpenApiSchemaReference("microsoft.graph.user") } } } @@ -7268,15 +6681,9 @@ public void AddReservedPathParameterSymbol() } }, }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "microsoft.graph.user", userSchema - } - } - } }; + document.AddComponent("microsoft.graph.user", userSchema); + document.SetReferenceHostDocument(); var mockLogger = new CountLogger(); var builder = new KiotaBuilder(mockLogger, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -7292,24 +6699,19 @@ public void DoesNotAddReservedPathParameterSymbol() { var userSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "id", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }, { "displayName", new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } } }, - Reference = new OpenApiReference - { - Id = "#/components/schemas/microsoft.graph.user" - }, - UnresolvedReference = false }; var document = new OpenApiDocument { @@ -7317,13 +6719,13 @@ public void DoesNotAddReservedPathParameterSymbol() { ["users/{id}/manager"] = new OpenApiPathItem { - Parameters = new List { + Parameters = new List { new OpenApiParameter { Name = "id", In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }, Extensions = { ["x-ms-reserved-parameter"] = new OpenApiReservedParameterExtension { @@ -7341,7 +6743,7 @@ public void DoesNotAddReservedPathParameterSymbol() Content = { ["application/json"] = new OpenApiMediaType { - Schema = userSchema + Schema = new OpenApiSchemaReference("microsoft.graph.user") } } } @@ -7350,15 +6752,9 @@ public void DoesNotAddReservedPathParameterSymbol() } }, }, - Components = new OpenApiComponents - { - Schemas = new Dictionary { - { - "microsoft.graph.user", userSchema - } - } - } }; + document.AddComponent("microsoft.graph.user", userSchema); + document.SetReferenceHostDocument(); var mockLogger = new CountLogger(); var builder = new KiotaBuilder(mockLogger, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); @@ -7718,7 +7114,8 @@ public async Task CleanupSymbolNameDoesNotCauseNameConflictsInQueryParametersAsy - name: select in: query schema: - type: int64 + type: number + format: int64 responses: '200': content: @@ -7871,7 +7268,7 @@ public async Task SupportsMultiPartFormAsRequestBodyWithoutEncodingWithDefaultMi var bodyParameter = postMethod.Parameters.FirstOrDefault(static x => x.IsOfKind(CodeParameterKind.RequestBody)); Assert.NotNull(bodyParameter); Assert.Equal("MultipartBody", bodyParameter.Type.Name, StringComparer.OrdinalIgnoreCase); - var addressClass = codeModel.FindChildByName("Address"); + var addressClass = codeModel.FindChildByName("Address"); // json is structured, we generated a model Assert.NotNull(addressClass); } [Fact] @@ -7929,9 +7326,8 @@ public async Task SupportsMultiPartFormAsRequestBodyWithoutEncodingWithDefaultMi Assert.NotNull(postMethod); var bodyParameter = postMethod.Parameters.FirstOrDefault(static x => x.IsOfKind(CodeParameterKind.RequestBody)); Assert.NotNull(bodyParameter); - Assert.Equal("DirectoryObjectPostRequestBody", bodyParameter.Type.Name, StringComparer.OrdinalIgnoreCase); //generate the model type as we do not have the serializer for the schema registered. - var addressClass = codeModel.FindChildByName("Address"); - Assert.NotNull(addressClass); + Assert.Equal("MultipartBody", bodyParameter.Type.Name, StringComparer.OrdinalIgnoreCase); + Assert.Null(codeModel.FindChildByName("Address")); // json is not structured so we didn't generate a model for the address } [Fact] public async Task SupportsMultipleContentTypesAsRequestBodyWithDefaultMimeTypesAsync() @@ -9313,8 +8709,8 @@ a database instance and cannot be changed once set. var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var registeredModelClass = codeModel.FindChildByName("RegisteredModel"); - Assert.Null(registeredModelClass.StartBlock.Inherits); Assert.NotNull(registeredModelClass); + Assert.Null(registeredModelClass.StartBlock.Inherits); Assert.Single(registeredModelClass.Properties, static x => x.Kind is CodePropertyKind.AdditionalData); Assert.Single(registeredModelClass.Properties, static x => x.Name.Equals("name", StringComparison.OrdinalIgnoreCase)); Assert.Single(registeredModelClass.Properties, static x => x.Name.Equals("id", StringComparison.OrdinalIgnoreCase)); @@ -9951,20 +9347,14 @@ public void SupportsIncludeFilterAndExcludeWithOperation() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -9980,7 +9370,7 @@ public void SupportsIncludeFilterAndExcludeWithOperation() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -9991,7 +9381,7 @@ public void SupportsIncludeFilterAndExcludeWithOperation() RequestBody = new OpenApiRequestBody { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10000,7 +9390,7 @@ public void SupportsIncludeFilterAndExcludeWithOperation() ["201"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10018,7 +9408,7 @@ public void SupportsIncludeFilterAndExcludeWithOperation() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10029,7 +9419,7 @@ public void SupportsIncludeFilterAndExcludeWithOperation() RequestBody = new OpenApiRequestBody { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10048,15 +9438,9 @@ public void SupportsIncludeFilterAndExcludeWithOperation() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { @@ -10096,20 +9480,14 @@ public void SupportsIncludeFilterAndExcludeWithOperationForSpecificPath() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -10125,7 +9503,7 @@ public void SupportsIncludeFilterAndExcludeWithOperationForSpecificPath() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10136,7 +9514,7 @@ public void SupportsIncludeFilterAndExcludeWithOperationForSpecificPath() RequestBody = new OpenApiRequestBody { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10145,7 +9523,7 @@ public void SupportsIncludeFilterAndExcludeWithOperationForSpecificPath() ["201"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10163,7 +9541,7 @@ public void SupportsIncludeFilterAndExcludeWithOperationForSpecificPath() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10174,7 +9552,7 @@ public void SupportsIncludeFilterAndExcludeWithOperationForSpecificPath() RequestBody = new OpenApiRequestBody { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10193,15 +9571,9 @@ public void SupportsIncludeFilterAndExcludeWithOperationForSpecificPath() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); var mockLogger = new Mock>(); var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { @@ -10241,20 +9613,14 @@ public void CleansUpOperationIdAddsMissingOperationId() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -10270,7 +9636,7 @@ public void CleansUpOperationIdAddsMissingOperationId() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10281,7 +9647,7 @@ public void CleansUpOperationIdAddsMissingOperationId() RequestBody = new OpenApiRequestBody { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10290,7 +9656,7 @@ public void CleansUpOperationIdAddsMissingOperationId() ["201"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10299,15 +9665,9 @@ public void CleansUpOperationIdAddsMissingOperationId() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); KiotaBuilder.CleanupOperationIdForPlugins(document); var operations = document.Paths.SelectMany(path => path.Value.Operations).ToList(); foreach (var path in operations) @@ -10343,20 +9703,14 @@ public void CleansUpOperationIdChangesOperationId() { var myObjectSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary { + Type = JsonSchemaType.Object, + Properties = new Dictionary { { "name", new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, } } }, - Reference = new OpenApiReference - { - Id = "myobject", - Type = ReferenceType.Schema - }, - UnresolvedReference = false, }; var document = new OpenApiDocument { @@ -10372,7 +9726,7 @@ public void CleansUpOperationIdChangesOperationId() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10384,7 +9738,7 @@ public void CleansUpOperationIdChangesOperationId() RequestBody = new OpenApiRequestBody { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10393,7 +9747,7 @@ public void CleansUpOperationIdChangesOperationId() ["201"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10412,7 +9766,7 @@ public void CleansUpOperationIdChangesOperationId() ["200"] = new OpenApiResponse { Content = { ["application/json"] = new OpenApiMediaType { - Schema = myObjectSchema + Schema = new OpenApiSchemaReference("myobject") } } }, @@ -10422,15 +9776,9 @@ public void CleansUpOperationIdChangesOperationId() } } }, - Components = new() - { - Schemas = new Dictionary { - { - "myobject", myObjectSchema - } - } - } }; + document.AddComponent("myobject", myObjectSchema); + document.SetReferenceHostDocument(); KiotaBuilder.CleanupOperationIdForPlugins(document); var operations = document.Paths.SelectMany(path => path.Value.Operations).ToList(); foreach (var path in operations) diff --git a/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiAiReasoningInstructionsExtensionTests.cs b/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiAiReasoningInstructionsExtensionTests.cs index 5b34465903..cd7492744f 100644 --- a/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiAiReasoningInstructionsExtensionTests.cs +++ b/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiAiReasoningInstructionsExtensionTests.cs @@ -1,12 +1,13 @@ using System; using System.IO; using System.Net.Http; +using System.Text; +using System.Text.Json.Nodes; using System.Threading.Tasks; using Kiota.Builder.Configuration; using Kiota.Builder.OpenApiExtensions; using Microsoft.Extensions.Logging; using Microsoft.OpenApi; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Writers; using Moq; using Xunit; @@ -22,10 +23,15 @@ public void Dispose() [Fact] public void Parses() { - var oaiValue = new OpenApiArray { - new OpenApiString("This is a description"), - new OpenApiString("This is a description 2"), - }; + var oaiValueRepresentation = + """ + [ + "This is a description", + "This is a description 2" + ] + """; + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(oaiValueRepresentation)); + var oaiValue = JsonNode.Parse(stream); var value = OpenApiAiReasoningInstructionsExtension.Parse(oaiValue); Assert.NotNull(value); Assert.Equal("This is a description", value.ReasoningInstructions[0]); diff --git a/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiAiRespondingInstructionsExtensionTests.cs b/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiAiRespondingInstructionsExtensionTests.cs index d14f85a70e..e900673fbc 100644 --- a/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiAiRespondingInstructionsExtensionTests.cs +++ b/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiAiRespondingInstructionsExtensionTests.cs @@ -1,12 +1,13 @@ using System; using System.IO; using System.Net.Http; +using System.Text; +using System.Text.Json.Nodes; using System.Threading.Tasks; using Kiota.Builder.Configuration; using Kiota.Builder.OpenApiExtensions; using Microsoft.Extensions.Logging; using Microsoft.OpenApi; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Writers; using Moq; using Xunit; @@ -22,10 +23,15 @@ public void Dispose() [Fact] public void Parses() { - var oaiValue = new OpenApiArray { - new OpenApiString("This is a description"), - new OpenApiString("This is a description 2"), - }; + var oaiValueRepresentation = + """ + [ + "This is a description", + "This is a description 2" + ] + """; + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(oaiValueRepresentation)); + var oaiValue = JsonNode.Parse(stream); var value = OpenApiAiRespondingInstructionsExtension.Parse(oaiValue); Assert.NotNull(value); Assert.Equal("This is a description", value.RespondingInstructions[0]); diff --git a/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiDescriptionForModelExtensionTests.cs b/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiDescriptionForModelExtensionTests.cs index bfcec098c2..c81192394a 100644 --- a/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiDescriptionForModelExtensionTests.cs +++ b/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiDescriptionForModelExtensionTests.cs @@ -6,7 +6,6 @@ using Kiota.Builder.OpenApiExtensions; using Microsoft.Extensions.Logging; using Microsoft.OpenApi; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Writers; using Moq; using Xunit; @@ -22,8 +21,7 @@ public void Dispose() [Fact] public void Parses() { - var oaiValue = new OpenApiString("This is a description"); - var value = OpenApiDescriptionForModelExtension.Parse(oaiValue); + var value = OpenApiDescriptionForModelExtension.Parse("This is a description"); Assert.NotNull(value); Assert.Equal("This is a description", value.Description); } diff --git a/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiKiotaExtensionTests.cs b/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiKiotaExtensionTests.cs index 872f0ca6db..50d598a883 100644 --- a/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiKiotaExtensionTests.cs +++ b/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiKiotaExtensionTests.cs @@ -1,7 +1,8 @@ using System.IO; +using System.Text; +using System.Text.Json.Nodes; using Kiota.Builder.OpenApiExtensions; using Microsoft.OpenApi; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Writers; using Xunit; @@ -49,30 +50,33 @@ public void Serializes() [Fact] public void Parses() { - var oaiValue = new OpenApiObject + var oaiValueRepresentation = + """ { - { "languagesInformation", new OpenApiObject { - {"CSharp", new OpenApiObject { - {"dependencies", new OpenApiArray { - new OpenApiObject { - {"name", new OpenApiString("Microsoft.Graph.Core")}, - {"version", new OpenApiString("1.0.0") }, - {"type", new OpenApiString("bundle")} - } - }}, - {"dependencyInstallCommand", new OpenApiString("dotnet add package") }, - {"maturityLevel", new OpenApiString("Preview")}, - {"supportExperience", new OpenApiString("Microsoft")}, - {"clientClassName", new OpenApiString("GraphServiceClient")}, - {"clientNamespaceName", new OpenApiString("Microsoft.Graph")}, - {"structuredMimeTypes", new OpenApiArray { - new OpenApiString("application/json"), - new OpenApiString("application/xml")} - }, - } + "languagesInformation": { + "CSharp": { + "dependencies": [ + { + "name": "Microsoft.Graph.Core", + "version": "1.0.0", + "type": "bundle" + } + ], + "dependencyInstallCommand": "dotnet add package", + "maturityLevel": "Preview", + "supportExperience": "Microsoft", + "clientClassName": "GraphServiceClient", + "clientNamespaceName": "Microsoft.Graph", + "structuredMimeTypes": [ + "application/json", + "application/xml" + ] } - }} - }; + } + } + """; + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(oaiValueRepresentation)); + var oaiValue = JsonNode.Parse(stream); var value = OpenApiKiotaExtension.Parse(oaiValue); Assert.NotNull(value); Assert.True(value.LanguagesInformation.TryGetValue("CSharp", out var CSEntry)); diff --git a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs index 4eb7b7a8c2..180f7b4d4c 100644 --- a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs @@ -10,7 +10,7 @@ using Microsoft.DeclarativeAgents.Manifest; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Readers; +using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Services; using Moq; using Xunit; @@ -75,13 +75,12 @@ public async Task GeneratesManifestAsync(string inputPluginName, string expected var workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml"; await File.WriteAllTextAsync(simpleDescriptionPath, simpleDescriptionContent); - var mockLogger = new Mock>(); var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, _logger); var outputDirectory = Path.Combine(workingDirectory, "output"); var generationConfiguration = new GenerationConfiguration { OutputPath = outputDirectory, - OpenAPIFilePath = "openapiPath", + OpenAPIFilePath = simpleDescriptionPath, PluginTypes = [PluginType.APIPlugin, PluginType.APIManifest, PluginType.OpenAI], ClientClassName = inputPluginName, ApiRootUrl = "http://localhost/", //Kiota builder would set this for us @@ -234,7 +233,7 @@ public async Task GeneratesManifestAndCleansUpInputDescriptionAsync() var generationConfiguration = new GenerationConfiguration { OutputPath = outputDirectory, - OpenAPIFilePath = "openapiPath", + OpenAPIFilePath = simpleDescriptionPath, PluginTypes = [PluginType.APIPlugin], ClientClassName = "client", ApiRootUrl = "http://localhost/", //Kiota builder would set this for us @@ -260,12 +259,11 @@ public async Task GeneratesManifestAndCleansUpInputDescriptionAsync() Assert.Equal(2, resultingManifest.Document.Capabilities.ConversationStarters.Count);// conversation starters are generated for each function Assert.Empty(resultingManifest.Problems);// no problems are expected with names - var openApiReader = new OpenApiStreamReader(); - // Validate the original file. - var originalOpenApiFile = File.OpenRead(simpleDescriptionPath); - var originalDocument = openApiReader.Read(originalOpenApiFile, out var originalDiagnostic); - Assert.Empty(originalDiagnostic.Errors); + using var originalOpenApiFile = File.OpenRead(simpleDescriptionPath); + var originalResult = await OpenApiDocument.LoadAsync(originalOpenApiFile, "yaml"); + var originalDocument = originalResult.Document; + Assert.Empty(originalResult.Diagnostic.Errors); Assert.Equal(originalDocument.Paths["/test"].Operations[OperationType.Get].Description, resultingManifest.Document.Functions[0].Description);// pulls from description Assert.Equal(originalDocument.Paths["/test/{id}"].Operations[OperationType.Get].Summary, resultingManifest.Document.Functions[1].Description);// pulls from summary @@ -280,12 +278,13 @@ public async Task GeneratesManifestAndCleansUpInputDescriptionAsync() Assert.Equal(2, originalDocument.Paths["/test/{id}"].Operations[OperationType.Get].Responses["200"].Content["application/json"].Schema.AllOf[0].Properties["id"].AnyOf.Count); // anyOf we selected // Validate the output open api file - var resultOpenApiFile = File.OpenRead(Path.Combine(outputDirectory, OpenApiFileName)); - var resultDocument = openApiReader.Read(resultOpenApiFile, out var diagnostic); - Assert.Empty(diagnostic.Errors); + using var resultOpenApiFile = File.OpenRead(Path.Combine(outputDirectory, OpenApiFileName)); + var resultResult = await OpenApiDocument.LoadAsync(resultOpenApiFile, "yaml"); + var resultDocument = resultResult.Document; + Assert.Empty(resultResult.Diagnostic.Errors); // Assertions / validations - Assert.Single(resultDocument.Components.Schemas);// no schema is referenced. so ensure they are all removed + Assert.Empty(resultDocument.Components.Schemas);// no schema is referenced. so ensure they are all removed Assert.Empty(resultDocument.Extensions); // no extension at root (unsupported extension is removed) Assert.Equal(2, resultDocument.Paths.Count); // document has only two paths Assert.Equal(originalDocument.Paths["/test"].Operations[OperationType.Get].Responses.Count - 1, resultDocument.Paths["/test"].Operations[OperationType.Get].Responses.Count); // We removed the error response @@ -297,7 +296,7 @@ public async Task GeneratesManifestAndCleansUpInputDescriptionAsync() Assert.Single(resultDocument.Paths["/test/{id}"].Operations[OperationType.Get].Extensions); // 1 supported extension still present in operation Assert.Empty(resultDocument.Paths["/test/{id}"].Operations[OperationType.Get].Responses["200"].Content["application/json"].Schema.AllOf); // allOf were merged Assert.Empty(resultDocument.Paths["/test/{id}"].Operations[OperationType.Get].Responses["200"].Content["application/json"].Schema.Properties["id"].AnyOf); // anyOf we selected - Assert.Equal("string", resultDocument.Paths["/test/{id}"].Operations[OperationType.Get].Responses["200"].Content["application/json"].Schema.Properties["id"].Type); + Assert.Equal(JsonSchemaType.String, resultDocument.Paths["/test/{id}"].Operations[OperationType.Get].Responses["200"].Content["application/json"].Schema.Properties["id"].Type.Value); Assert.DoesNotContain("500", resultDocument.Paths["/test/{id}"].Operations[OperationType.Get].Responses.Keys, StringComparer.OrdinalIgnoreCase); // We removed the error response } @@ -487,13 +486,12 @@ public async Task GeneratesManifestWithAuthAsync(string securitySchemesComponent var workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml"; await File.WriteAllTextAsync(simpleDescriptionPath, apiDescription); - var mockLogger = new Mock>(); var openApiDocumentDs = new OpenApiDocumentDownloadService(_httpClient, _logger); var outputDirectory = Path.Combine(workingDirectory, "output"); var generationConfiguration = new GenerationConfiguration { OutputPath = outputDirectory, - OpenAPIFilePath = "openapiPath", + OpenAPIFilePath = simpleDescriptionPath, PluginTypes = [PluginType.APIPlugin], ClientClassName = "client", ApiRootUrl = "http://localhost/", //Kiota builder would set this for us @@ -523,7 +521,7 @@ public async Task GeneratesManifestWithAuthAsync(string securitySchemesComponent // Cleanup try { - Directory.Delete(outputDirectory); + Directory.Delete(outputDirectory, true); } catch (Exception) { @@ -566,13 +564,12 @@ public async Task GeneratesManifestWithMultipleSecuritySchemesAsync() var workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml"; await File.WriteAllTextAsync(simpleDescriptionPath, apiDescription); - var mockLogger = new Mock>(); var openApiDocumentDs = new OpenApiDocumentDownloadService(_httpClient, _logger); var outputDirectory = Path.Combine(workingDirectory, "output"); var generationConfiguration = new GenerationConfiguration { OutputPath = outputDirectory, - OpenAPIFilePath = "openapiPath", + OpenAPIFilePath = simpleDescriptionPath, PluginTypes = [PluginType.APIPlugin], ClientClassName = "client", ApiRootUrl = "http://localhost/", //Kiota builder would set this for us @@ -645,7 +642,7 @@ public static TheoryData> Assert.NotEmpty(slicedDocument.Paths); var schema = slicedDocument.Paths["/test"].Operations[OperationType.Post].RequestBody .Content["application/json"].Schema; - Assert.Equal("string", schema.Type); + Assert.Equal(JsonSchemaType.String, schema.Type.Value); Assert.Equal(5, schema.MaxLength); } }, @@ -665,7 +662,7 @@ public static TheoryData> Assert.NotEmpty(slicedDocument.Paths); var schema = slicedDocument.Paths["/test"].Operations[OperationType.Post].RequestBody .Content["application/json"].Schema; - Assert.Equal("object", schema.Type); + Assert.Equal(JsonSchemaType.Object, schema.Type.Value); Assert.Equal(3, schema.Properties.Count); } }, @@ -685,7 +682,7 @@ public static TheoryData> Assert.NotEmpty(slicedDocument.Paths); var schema = slicedDocument.Paths["/test"].Operations[OperationType.Post].RequestBody .Content["application/json"].Schema; - Assert.Equal("object", schema.Type); + Assert.Equal(JsonSchemaType.Object, schema.Type.Value); Assert.Equal(2, schema.Properties.Count); } }, @@ -705,7 +702,7 @@ public static TheoryData> Assert.NotEmpty(slicedDocument.Paths); var schema = slicedDocument.Paths["/test"].Operations[OperationType.Post].RequestBody .Content["application/json"].Schema; - Assert.Equal("object", schema.Type); + Assert.Equal(JsonSchemaType.Object, schema.Type.Value); Assert.Equal(2, schema.Properties.Count); } }, @@ -725,7 +722,7 @@ public static TheoryData> Assert.NotEmpty(slicedDocument.Paths); var schema = slicedDocument.Paths["/test"].Operations[OperationType.Post].RequestBody .Content["application/json"].Schema; - Assert.Equal("object", schema.Type); + Assert.Equal(JsonSchemaType.Object, schema.Type.Value); Assert.Single(schema.Properties); } }, @@ -741,7 +738,7 @@ public static TheoryData> Assert.NotEmpty(slicedDocument.Paths); var schema = slicedDocument.Paths["/test"].Operations[OperationType.Post].RequestBody .Content["application/json"].Schema; - Assert.Equal("object", schema.Type); + Assert.Equal(JsonSchemaType.Object, schema.Type.Value); Assert.Single(schema.Properties); } }, @@ -777,7 +774,7 @@ public async Task MergesAllOfRequestBodyAsync(string content, Action GetDiagnosticFromDocumentAsync(string document) + { + var rule = new DivergentResponseSchema(new()); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(document)); + var settings = new OpenApiReaderSettings(); + settings.RuleSet.Add(typeof(OpenApiOperation), [rule]); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader()); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader()); + var result = await OpenApiDocument.LoadAsync(stream, "yaml", settings); + return result.Diagnostic; } } diff --git a/tests/Kiota.Builder.Tests/Validation/GetWithBodyTests.cs b/tests/Kiota.Builder.Tests/Validation/GetWithBodyTests.cs index 99ba847d8c..2289b89101 100644 --- a/tests/Kiota.Builder.Tests/Validation/GetWithBodyTests.cs +++ b/tests/Kiota.Builder.Tests/Validation/GetWithBodyTests.cs @@ -1,6 +1,10 @@ using System.IO; using System.Text; +using System.Threading.Tasks; using Kiota.Builder.Validation; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Validations; using Xunit; @@ -10,9 +14,8 @@ namespace Kiota.Builder.Tests.Validation; public class GetWithBodyTests { [Fact] - public void AddsAWarningWhenGetWithBody() + public async Task AddsAWarningWhenGetWithBody() { - var rule = new GetWithBody(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -27,20 +30,15 @@ public void AddsAWarningWhenGetWithBody() application/json: responses: '200': + description: some description content: application/json:"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void DoesntAddAWarningWhenGetWithNoBody() + public async Task DoesntAddAWarningWhenGetWithNoBody() { - var rule = new GetWithBody(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -53,20 +51,15 @@ public void DoesntAddAWarningWhenGetWithNoBody() get: responses: '200': + description: some description content: application/json:"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); } [Fact] - public void DoesntAddAWarningWhenPostWithBody() + public async Task DoesntAddAWarningWhenPostWithBody() { - var rule = new GetWithBody(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -81,14 +74,21 @@ public void DoesntAddAWarningWhenPostWithBody() application/json: responses: '200': + description: some description content: application/json:"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); + } + private static async Task GetDiagnosticFromDocumentAsync(string document) + { + var rule = new GetWithBody(); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(document)); + var settings = new OpenApiReaderSettings(); + settings.RuleSet.Add(typeof(IOpenApiPathItem), [rule]); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader()); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader()); + var result = await OpenApiDocument.LoadAsync(stream, "yaml", settings); + return result.Diagnostic; } } diff --git a/tests/Kiota.Builder.Tests/Validation/InconsistentTypeFormatPairTests.cs b/tests/Kiota.Builder.Tests/Validation/InconsistentTypeFormatPairTests.cs index 9b46823c30..bb2e4d7dea 100644 --- a/tests/Kiota.Builder.Tests/Validation/InconsistentTypeFormatPairTests.cs +++ b/tests/Kiota.Builder.Tests/Validation/InconsistentTypeFormatPairTests.cs @@ -1,17 +1,19 @@ using System.IO; using System.Text; +using System.Threading.Tasks; using Kiota.Builder.Validation; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Readers; -using Microsoft.OpenApi.Validations; using Xunit; namespace Kiota.Builder.Tests.Validation; public class InconsistentTypeFormatPairTests { [Fact] - public void AddsAWarningWhenKnownInconsistentPair() + public async Task AddsAWarningWhenKnownInconsistentPair() { - var rule = new InconsistentTypeFormatPair(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -22,23 +24,18 @@ public void AddsAWarningWhenKnownInconsistentPair() get: responses: '200': + description: some description content: application/json: schema: type: string format: int32"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void DoesntAddAWarningWhenSupportedPair() + public async Task DoesntAddAWarningWhenSupportedPair() { - var rule = new InconsistentTypeFormatPair(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -49,23 +46,18 @@ public void DoesntAddAWarningWhenSupportedPair() get: responses: '200': + description: some description content: application/json: schema: type: string format: uuid"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); } [Fact] - public void DoesntFailWhenKnownAlternative() + public async Task DoesntFailWhenKnownAlternative() { - var rule = new InconsistentTypeFormatPair(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -76,17 +68,24 @@ public void DoesntFailWhenKnownAlternative() get: responses: '200': + description: some description content: application/json: schema: type: enum format: string"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); + } + private static async Task GetDiagnosticFromDocumentAsync(string document) + { + var rule = new InconsistentTypeFormatPair(); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(document)); + var settings = new OpenApiReaderSettings(); + settings.RuleSet.Add(typeof(IOpenApiSchema), [rule]); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader()); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader()); + var result = await OpenApiDocument.LoadAsync(stream, "yaml", settings); + return result.Diagnostic; } } diff --git a/tests/Kiota.Builder.Tests/Validation/KnownAndNotSupportedFormatsTests.cs b/tests/Kiota.Builder.Tests/Validation/KnownAndNotSupportedFormatsTests.cs index 8df9463cf9..81ee36bba5 100644 --- a/tests/Kiota.Builder.Tests/Validation/KnownAndNotSupportedFormatsTests.cs +++ b/tests/Kiota.Builder.Tests/Validation/KnownAndNotSupportedFormatsTests.cs @@ -1,8 +1,11 @@ using System.IO; using System.Text; +using System.Threading.Tasks; using Kiota.Builder.Validation; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Readers; -using Microsoft.OpenApi.Validations; using Xunit; namespace Kiota.Builder.Tests.Validation; @@ -10,9 +13,8 @@ namespace Kiota.Builder.Tests.Validation; public class KnownAndNotSupportedFormatsTests { [Fact] - public void AddsAWarningWhenKnownUnsupportedFormat() + public async Task AddsAWarningWhenKnownUnsupportedFormat() { - var rule = new KnownAndNotSupportedFormats(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -23,23 +25,18 @@ public void AddsAWarningWhenKnownUnsupportedFormat() get: responses: '200': + description: some description content: application/json: schema: type: string format: email"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void DoesntAddAWarningWhenSupportedFormat() + public async Task DoesntAddAWarningWhenSupportedFormat() { - var rule = new KnownAndNotSupportedFormats(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -50,23 +47,18 @@ public void DoesntAddAWarningWhenSupportedFormat() get: responses: '200': + description: some description content: application/json: schema: type: string format: uuid"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); } [Fact] - public void DoesntFailWhenNoFormat() + public async Task DoesntFailWhenNoFormat() { - var rule = new KnownAndNotSupportedFormats(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -77,17 +69,23 @@ public void DoesntFailWhenNoFormat() get: responses: '200': + description: some description content: application/json: schema: type: string"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); + } + private static async Task GetDiagnosticFromDocumentAsync(string document) + { + var rule = new KnownAndNotSupportedFormats(); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(document)); + var settings = new OpenApiReaderSettings(); + settings.RuleSet.Add(typeof(IOpenApiSchema), [rule]); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader()); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader()); + var result = await OpenApiDocument.LoadAsync(stream, "yaml", settings); + return result.Diagnostic; } - } diff --git a/tests/Kiota.Builder.Tests/Validation/MissingDiscriminatorTests.cs b/tests/Kiota.Builder.Tests/Validation/MissingDiscriminatorTests.cs index d112997287..efd89a1c2b 100644 --- a/tests/Kiota.Builder.Tests/Validation/MissingDiscriminatorTests.cs +++ b/tests/Kiota.Builder.Tests/Validation/MissingDiscriminatorTests.cs @@ -1,17 +1,18 @@ using System.IO; using System.Text; +using System.Threading.Tasks; using Kiota.Builder.Validation; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Readers; -using Microsoft.OpenApi.Validations; using Xunit; namespace Kiota.Builder.Tests.Validation; public class MissingDiscriminatorTests { [Fact] - public void DoesntAddAWarningWhenBodyIsSimple() + public async Task DoesntAddAWarningWhenBodyIsSimple() { - var rule = new MissingDiscriminator(new()); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -22,23 +23,18 @@ public void DoesntAddAWarningWhenBodyIsSimple() get: responses: '200': + description: some description content: application/json: schema: type: string format: int32"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); } [Fact] - public void AddsWarningOnInlineSchemas() + public async Task AddsWarningOnInlineSchemas() { - var rule = new MissingDiscriminator(new()); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -49,6 +45,7 @@ public void AddsWarningOnInlineSchemas() get: responses: '200': + description: some description content: application/json: schema: @@ -62,18 +59,12 @@ public void AddsWarningOnInlineSchemas() properties: type2: type: string"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void AddsWarningOnComponentSchemas() + public async Task AddsWarningOnComponentSchemas() { - var rule = new MissingDiscriminator(new()); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -101,22 +92,17 @@ public void AddsWarningOnComponentSchemas() get: responses: '200': + description: some description content: application/json: schema: $ref: '#/components/schemas/type3'"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void DoesntAddsWarningOnComponentSchemasWithDiscriminatorInformation() + public async Task DoesntAddsWarningOnComponentSchemasWithDiscriminatorInformation() { - var rule = new MissingDiscriminator(new()); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -146,22 +132,17 @@ public void DoesntAddsWarningOnComponentSchemasWithDiscriminatorInformation() get: responses: '200': + description: some description content: application/json: schema: $ref: '#/components/schemas/type3'"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); } [Fact] - public void DoesntAddsWarningOnComponentSchemasScalars() + public async Task DoesntAddsWarningOnComponentSchemasScalars() { - var rule = new MissingDiscriminator(new()); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -181,16 +162,23 @@ public void DoesntAddsWarningOnComponentSchemasScalars() get: responses: '200': + description: some description content: application/json: schema: $ref: '#/components/schemas/type1'"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); + } + private static async Task GetDiagnosticFromDocumentAsync(string document) + { + var rule = new MissingDiscriminator(new()); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(document)); + var settings = new OpenApiReaderSettings(); + settings.RuleSet.Add(typeof(OpenApiDocument), [rule]); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader()); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader()); + var result = await OpenApiDocument.LoadAsync(stream, "yaml", settings); + return result.Diagnostic; } } diff --git a/tests/Kiota.Builder.Tests/Validation/MultipleServerEntriesTests.cs b/tests/Kiota.Builder.Tests/Validation/MultipleServerEntriesTests.cs index 0bbd417140..d6585196e9 100644 --- a/tests/Kiota.Builder.Tests/Validation/MultipleServerEntriesTests.cs +++ b/tests/Kiota.Builder.Tests/Validation/MultipleServerEntriesTests.cs @@ -1,8 +1,10 @@ using System.IO; using System.Text; +using System.Threading.Tasks; using Kiota.Builder.Validation; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Readers; -using Microsoft.OpenApi.Validations; using Xunit; namespace Kiota.Builder.Tests.Validation; @@ -10,9 +12,8 @@ namespace Kiota.Builder.Tests.Validation; public class MultipleServerEntriesTests { [Fact] - public void AddsAWarningWhenMultipleServersPresent() + public async Task AddsAWarningWhenMultipleServersPresent() { - var rule = new MultipleServerEntries(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -26,20 +27,15 @@ public void AddsAWarningWhenMultipleServersPresent() get: responses: '200': + description: some description content: application/json:"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void DoesntAddAWarningWhenSingleServerPresent() + public async Task DoesntAddAWarningWhenSingleServerPresent() { - var rule = new MultipleServerEntries(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -52,14 +48,21 @@ public void DoesntAddAWarningWhenSingleServerPresent() get: responses: '200': + description: some description content: application/json:"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); + } + private static async Task GetDiagnosticFromDocumentAsync(string document) + { + var rule = new MultipleServerEntries(); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(document)); + var settings = new OpenApiReaderSettings(); + settings.RuleSet.Add(typeof(OpenApiDocument), [rule]); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader()); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader()); + var result = await OpenApiDocument.LoadAsync(stream, "yaml", settings); + return result.Diagnostic; } } diff --git a/tests/Kiota.Builder.Tests/Validation/NoContentWithBodyTests.cs b/tests/Kiota.Builder.Tests/Validation/NoContentWithBodyTests.cs index d0924580c3..ce59591fff 100644 --- a/tests/Kiota.Builder.Tests/Validation/NoContentWithBodyTests.cs +++ b/tests/Kiota.Builder.Tests/Validation/NoContentWithBodyTests.cs @@ -1,8 +1,10 @@ using System.IO; using System.Text; +using System.Threading.Tasks; using Kiota.Builder.Validation; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Readers; -using Microsoft.OpenApi.Validations; using Xunit; namespace Kiota.Builder.Tests.Validation; @@ -10,9 +12,8 @@ namespace Kiota.Builder.Tests.Validation; public class NoContentWithBodyTests { [Fact] - public void AddsAWarningWhen204WithBody() + public async Task AddsAWarningWhen204WithBody() { - var rule = new NoContentWithBody(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -23,20 +24,15 @@ public void AddsAWarningWhen204WithBody() get: responses: '204': + description: some description content: application/json:"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void DoesntAddAWarningWhen204WithNoBody() + public async Task DoesntAddAWarningWhen204WithNoBody() { - var rule = new NoContentWithBody(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -48,19 +44,14 @@ public void DoesntAddAWarningWhen204WithNoBody() /enumeration: get: responses: - '204':"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + '204': + description: some description"; + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); } [Fact] - public void DoesntAddAWarningWhen200WithBody() + public async Task DoesntAddAWarningWhen200WithBody() { - var rule = new NoContentWithBody(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -75,15 +66,21 @@ public void DoesntAddAWarningWhen200WithBody() application/json: responses: '200': + description: some description content: application/json:"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); + } + private static async Task GetDiagnosticFromDocumentAsync(string document) + { + var rule = new NoContentWithBody(); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(document)); + var settings = new OpenApiReaderSettings(); + settings.RuleSet.Add(typeof(OpenApiOperation), [rule]); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader()); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader()); + var result = await OpenApiDocument.LoadAsync(stream, "yaml", settings); + return result.Diagnostic; } - } diff --git a/tests/Kiota.Builder.Tests/Validation/NoServerEntryTests.cs b/tests/Kiota.Builder.Tests/Validation/NoServerEntryTests.cs index acae1bf4b3..cc488df6db 100644 --- a/tests/Kiota.Builder.Tests/Validation/NoServerEntryTests.cs +++ b/tests/Kiota.Builder.Tests/Validation/NoServerEntryTests.cs @@ -1,8 +1,10 @@ using System.IO; using System.Text; +using System.Threading.Tasks; using Kiota.Builder.Validation; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Readers; -using Microsoft.OpenApi.Validations; using Xunit; namespace Kiota.Builder.Tests.Validation; @@ -10,9 +12,8 @@ namespace Kiota.Builder.Tests.Validation; public class NoServerEntryTests { [Fact] - public void AddsAWarningWhenNoServersPresent() + public async Task AddsAWarningWhenNoServersPresent() { - var rule = new NoServerEntry(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -25,18 +26,12 @@ public void AddsAWarningWhenNoServersPresent() '200': content: application/json:"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void DoesntAddAWarningWhenServerPresent() + public async Task DoesntAddAWarningWhenServerPresent() { - var rule = new NoServerEntry(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -51,12 +46,18 @@ public void DoesntAddAWarningWhenServerPresent() '200': content: application/json:"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); + } + private static async Task GetDiagnosticFromDocumentAsync(string document) + { + var rule = new NoServerEntry(); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(document)); + var settings = new OpenApiReaderSettings(); + settings.RuleSet.Add(typeof(OpenApiDocument), [rule]); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader()); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader()); + var result = await OpenApiDocument.LoadAsync(stream, "yaml", settings); + return result.Diagnostic; } } diff --git a/tests/Kiota.Builder.Tests/Validation/OpenApiSchemaComparerTests.cs b/tests/Kiota.Builder.Tests/Validation/OpenApiSchemaComparerTests.cs index e794eb3f3d..943255bb91 100644 --- a/tests/Kiota.Builder.Tests/Validation/OpenApiSchemaComparerTests.cs +++ b/tests/Kiota.Builder.Tests/Validation/OpenApiSchemaComparerTests.cs @@ -13,14 +13,14 @@ public void Defensive() { Assert.Equal(new HashCode().ToHashCode(), _comparer.GetHashCode(null)); Assert.True(_comparer.Equals(null, null)); - Assert.False(_comparer.Equals(new(), null)); - Assert.False(_comparer.Equals(null, new())); + Assert.False(_comparer.Equals(new OpenApiSchema(), null)); + Assert.False(_comparer.Equals(null, new OpenApiSchema())); } [Fact] public void TestEquals() { - Assert.True(_comparer.Equals(new(), new())); + Assert.True(_comparer.Equals(new OpenApiSchema(), new OpenApiSchema())); } [Fact] public void DoesNotStackOverFlowOnCircularReferencesForEquals() diff --git a/tests/Kiota.Builder.Tests/Validation/UrlFormEncodedComplexTests.cs b/tests/Kiota.Builder.Tests/Validation/UrlFormEncodedComplexTests.cs index a45f0f9498..3482735a3e 100644 --- a/tests/Kiota.Builder.Tests/Validation/UrlFormEncodedComplexTests.cs +++ b/tests/Kiota.Builder.Tests/Validation/UrlFormEncodedComplexTests.cs @@ -1,17 +1,18 @@ using System.IO; using System.Text; +using System.Threading.Tasks; using Kiota.Builder.Validation; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Readers; -using Microsoft.OpenApi.Validations; using Xunit; namespace Kiota.Builder.Tests.Validation; public class UrlFormEncodedComplexTests { [Fact] - public void AddsAWarningWhenUrlEncodedNotObjectRequestBody() + public async Task AddsAWarningWhenUrlEncodedNotObjectRequestBody() { - var rule = new UrlFormEncodedComplex(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -28,23 +29,18 @@ public void AddsAWarningWhenUrlEncodedNotObjectRequestBody() format: int32 responses: '200': + description: some description content: application/json: schema: type: string format: int32"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void AddsAWarningWhenUrlEncodedNotObjectResponse() + public async Task AddsAWarningWhenUrlEncodedNotObjectResponse() { - var rule = new UrlFormEncodedComplex(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -55,23 +51,18 @@ public void AddsAWarningWhenUrlEncodedNotObjectResponse() get: responses: '200': + description: some description content: application/x-www-form-urlencoded: schema: type: string format: int32"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void AddsAWarningWhenUrlEncodedComplexPropertyOnRequestBody() + public async Task AddsAWarningWhenUrlEncodedComplexPropertyOnRequestBody() { - var rule = new UrlFormEncodedComplex(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -93,23 +84,18 @@ public void AddsAWarningWhenUrlEncodedComplexPropertyOnRequestBody() type: string responses: '200': + description: some description content: application/json: schema: type: string format: int32"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void AddsAWarningWhenUrlEncodedComplexPropertyOnResponse() + public async Task AddsAWarningWhenUrlEncodedComplexPropertyOnResponse() { - var rule = new UrlFormEncodedComplex(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -120,6 +106,7 @@ public void AddsAWarningWhenUrlEncodedComplexPropertyOnResponse() get: responses: '200': + description: some description content: application/x-www-form-urlencoded: schema: @@ -130,18 +117,12 @@ public void AddsAWarningWhenUrlEncodedComplexPropertyOnResponse() properties: prop: type: string"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Single(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Single(diagnostic.Warnings); } [Fact] - public void DoesntAddAWarningWhenUrlEncoded() + public async Task DoesntAddAWarningWhenUrlEncoded() { - var rule = new UrlFormEncodedComplex(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -152,6 +133,7 @@ public void DoesntAddAWarningWhenUrlEncoded() get: responses: '200': + description: some description content: application/x-www-form-urlencoded: schema: @@ -159,18 +141,12 @@ public void DoesntAddAWarningWhenUrlEncoded() properties: prop: type: string"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); } [Fact] - public void DoesntAddAWarningOnArrayProperty() + public async Task DoesntAddAWarningOnArrayProperty() { - var rule = new UrlFormEncodedComplex(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -192,6 +168,7 @@ public void DoesntAddAWarningOnArrayProperty() format: int32 responses: '200': + description: some description content: application/x-www-form-urlencoded: schema: @@ -199,18 +176,12 @@ public void DoesntAddAWarningOnArrayProperty() properties: prop: type: string"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); } [Fact] - public void DoesntAddAWarningWhenNotUrlEncoded() + public async Task DoesntAddAWarningWhenNotUrlEncoded() { - var rule = new UrlFormEncodedComplex(); var documentTxt = @"openapi: 3.0.1 info: title: OData Service for namespace microsoft.graph @@ -221,17 +192,24 @@ public void DoesntAddAWarningWhenNotUrlEncoded() get: responses: '200': + description: some description content: application/json: schema: type: enum format: string"; - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(documentTxt)); - var reader = new OpenApiStreamReader(new OpenApiReaderSettings - { - RuleSet = new(new ValidationRule[] { rule }), - }); - var doc = reader.Read(stream, out var diag); - Assert.Empty(diag.Warnings); + var diagnostic = await GetDiagnosticFromDocumentAsync(documentTxt); + Assert.Empty(diagnostic.Warnings); + } + private static async Task GetDiagnosticFromDocumentAsync(string document) + { + var rule = new UrlFormEncodedComplex(); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(document)); + var settings = new OpenApiReaderSettings(); + settings.RuleSet.Add(typeof(OpenApiOperation), [rule]); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader()); + OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader()); + var result = await OpenApiDocument.LoadAsync(stream, "yaml", settings); + return result.Diagnostic; } } diff --git a/tests/Kiota.Builder.Tests/Validation/ValidationRuleSetExtensionsTests.cs b/tests/Kiota.Builder.Tests/Validation/ValidationRuleSetExtensionsTests.cs index 7159d17db0..1bba9f1448 100644 --- a/tests/Kiota.Builder.Tests/Validation/ValidationRuleSetExtensionsTests.cs +++ b/tests/Kiota.Builder.Tests/Validation/ValidationRuleSetExtensionsTests.cs @@ -19,7 +19,7 @@ public void DisablesAllRules() var ruleSet = new ValidationRuleSet(); var configuration = new GenerationConfiguration { DisabledValidationRules = new() { "all" } }; ruleSet.AddKiotaValidationRules(configuration); - Assert.Empty(ruleSet); + Assert.Empty(ruleSet.Rules); } [Fact] public void DisablesNoRule() @@ -27,15 +27,15 @@ public void DisablesNoRule() var ruleSet = new ValidationRuleSet(); var configuration = new GenerationConfiguration { DisabledValidationRules = new() }; ruleSet.AddKiotaValidationRules(configuration); - Assert.NotEmpty(ruleSet); + Assert.NotEmpty(ruleSet.Rules); } [Fact] public void DisablesOneRule() { var ruleSet = new ValidationRuleSet(); - var configuration = new GenerationConfiguration { DisabledValidationRules = new() { nameof(NoServerEntry) } }; + var configuration = new GenerationConfiguration { DisabledValidationRules = [nameof(NoServerEntry)] }; ruleSet.AddKiotaValidationRules(configuration); - Assert.NotEmpty(ruleSet); - Assert.DoesNotContain(ruleSet, static x => x.GetType() == typeof(NoServerEntry)); + Assert.NotEmpty(ruleSet.Rules); + Assert.DoesNotContain(ruleSet.Rules, static x => x.GetType() == typeof(NoServerEntry)); } }