diff --git a/.vscode/settings.json b/.vscode/settings.json index 186b10bea..619467ff3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ }, "cSpell.words": [ "csdl", - "Hidi" + "Hidi", + "Xunit" ] } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Attributes/ExperimentalAttribute.cs b/src/Microsoft.OpenApi/Attributes/ExperimentalAttribute.cs new file mode 100644 index 000000000..3d2739e63 --- /dev/null +++ b/src/Microsoft.OpenApi/Attributes/ExperimentalAttribute.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +#if !NET8_0_OR_GREATER +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// Indicates that an API is experimental and may change in the future. + /// + [AttributeUsage( + AttributeTargets.Assembly | + AttributeTargets.Module | + AttributeTargets.Class | + AttributeTargets.Struct | + AttributeTargets.Enum | + AttributeTargets.Constructor | + AttributeTargets.Method | + AttributeTargets.Property | + AttributeTargets.Field | + AttributeTargets.Event | + AttributeTargets.Interface | + AttributeTargets.Delegate, + Inherited = false)] + internal sealed class ExperimentalAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// The diagnostic ID reported when the API is used. + public ExperimentalAttribute(string diagnosticId) + { + DiagnosticId = diagnosticId; + } + + /// + /// Gets the diagnostic ID reported when the API is used. + /// + public string DiagnosticId { get; } + + /// + /// Gets or sets the URL format used by the diagnostic. + /// + public string? UrlFormat { get; set; } + } +} +#endif diff --git a/src/Microsoft.OpenApi/Interfaces/IDeepCopyable.cs b/src/Microsoft.OpenApi/Interfaces/IDeepCopyable.cs new file mode 100644 index 000000000..17e75f90c --- /dev/null +++ b/src/Microsoft.OpenApi/Interfaces/IDeepCopyable.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.OpenApi; +/// +/// Interface for deep copyable objects. +/// +/// The type of the resulting object. +[System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] +public interface IDeepCopyable +{ + /// + /// Create a deep copy of the current instance. + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + T CreateDeepCopy(); +} diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index 81729fa20..7cb3f4d73 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -9,7 +9,7 @@ true true - NU5048 + $(NoWarn);NU5048;OPENAPI001 enable README.md diff --git a/src/Microsoft.OpenApi/Models/BaseOpenApiReference.cs b/src/Microsoft.OpenApi/Models/BaseOpenApiReference.cs index d32080890..647cf77fd 100644 --- a/src/Microsoft.OpenApi/Models/BaseOpenApiReference.cs +++ b/src/Microsoft.OpenApi/Models/BaseOpenApiReference.cs @@ -295,6 +295,12 @@ internal void EnsureHostDocumentIsSet(OpenApiDocument currentDocument) Utils.CheckArgumentNull(currentDocument); hostDocument ??= currentDocument; } + + internal void SetHostDocument(OpenApiDocument currentDocument) + { + Utils.CheckArgumentNull(currentDocument); + hostDocument = currentDocument; + } /// /// Gets the property value from a JsonObject node. /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiCallback.cs b/src/Microsoft.OpenApi/Models/OpenApiCallback.cs index 4832619f7..98c013fcc 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiCallback.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiCallback.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Callback Object: A map of possible out-of band callbacks related to the parent operation. /// - public class OpenApiCallback : IOpenApiExtensible, IOpenApiCallback + public class OpenApiCallback : IOpenApiExtensible, IOpenApiCallback, IDeepCopyable { /// public Dictionary? PathItems { get; set; } @@ -113,5 +113,11 @@ public IOpenApiCallback CreateShallowCopy() { return new OpenApiCallback(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiCallback CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiComponents.cs b/src/Microsoft.OpenApi/Models/OpenApiComponents.cs index c2f420ada..ac96909da 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiComponents.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiComponents.cs @@ -10,7 +10,7 @@ namespace Microsoft.OpenApi /// /// Components Object. /// - public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// An object to hold reusable Objects. @@ -96,6 +96,13 @@ public OpenApiComponents(OpenApiComponents? components) Extensions = components?.Extensions != null ? new Dictionary(components.Extensions) : null; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiComponents CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open API v3.2. /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiContact.cs b/src/Microsoft.OpenApi/Models/OpenApiContact.cs index 385129af7..d61be3fb8 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiContact.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiContact.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Contact Object. /// - public class OpenApiContact : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiContact : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// The identifying name of the contact person/organization. @@ -47,6 +47,12 @@ public OpenApiContact(OpenApiContact contact) Email = contact?.Email ?? Email; Extensions = contact?.Extensions != null ? new Dictionary(contact.Extensions) : null; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiContact CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } /// /// Serialize to Open Api v3.2 /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiDeepCopyContext.cs b/src/Microsoft.OpenApi/Models/OpenApiDeepCopyContext.cs new file mode 100644 index 000000000..a2fe48f87 --- /dev/null +++ b/src/Microsoft.OpenApi/Models/OpenApiDeepCopyContext.cs @@ -0,0 +1,922 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +#pragma warning disable CS0618 + +using System; +using System.Collections.Generic; +#if NET +using System.Collections.Immutable; +#endif +using System.Linq; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Text.Json.Nodes; + +namespace Microsoft.OpenApi; + +internal sealed class OpenApiDeepCopyContext +{ + private readonly Dictionary _items = new(ReferenceEqualityComparer.Instance); + + internal T Copy(T source) + where T : class + { + Utils.CheckArgumentNull(source); + return CopyDynamic(source); + } + + private T? GetExisting(object source) + where T : class + { + if (_items.TryGetValue(source, out var value)) + { + return (T)value; + } + + return null; + } + + private void Add(object source, object target) + { + _items[source] = target; + } + + internal OpenApiDocument Copy(OpenApiDocument source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiDocument + { + Workspace = source.Workspace?.BaseUrl != null ? new(source.Workspace.BaseUrl) : source.Workspace != null ? new OpenApiWorkspace() : null, + Info = Copy(source.Info), + JsonSchemaDialect = source.JsonSchemaDialect, + Self = source.Self, + Servers = CopyList(source.Servers), + Paths = Copy(source.Paths), + Webhooks = CopyMap(source.Webhooks), + Components = source.Components is not null ? Copy(source.Components) : null, + Security = CopyList(source.Security), + ExternalDocs = source.ExternalDocs is not null ? Copy(source.ExternalDocs) : null, + Extensions = CopyExtensions(source.Extensions), + Metadata = CopyMetadata(source.Metadata), + BaseUri = source.BaseUri + }; + Add(source, target); + if (source.Tags is not null) + { + target.Tags = CopyTags(source.Tags); + } + SetReferenceHostDocument(target); + target.RegisterComponents(); + return target; + } + + internal OpenApiInfo Copy(OpenApiInfo source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiInfo + { + Title = source.Title, + Summary = source.Summary, + Description = source.Description, + Version = source.Version, + TermsOfService = source.TermsOfService is not null ? CopyUri(source.TermsOfService) : null, + Contact = source.Contact is not null ? Copy(source.Contact) : null, + License = source.License is not null ? Copy(source.License) : null, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + return target; + } + + internal OpenApiContact Copy(OpenApiContact source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiContact + { + Name = source.Name, + Url = source.Url is not null ? CopyUri(source.Url) : null, + Email = source.Email, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + return target; + } + + internal OpenApiLicense Copy(OpenApiLicense source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiLicense + { + Name = source.Name, + Identifier = source.Identifier, + Url = source.Url is not null ? CopyUri(source.Url) : null, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + return target; + } + + internal OpenApiExternalDocs Copy(OpenApiExternalDocs source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiExternalDocs + { + Description = source.Description, + Url = source.Url is not null ? CopyUri(source.Url) : null, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + return target; + } + + internal OpenApiServer Copy(OpenApiServer source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiServer + { + Description = source.Description, + Name = source.Name, + Url = source.Url, + Variables = CopyMap(source.Variables), + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + return target; + } + + internal OpenApiServerVariable Copy(OpenApiServerVariable source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiServerVariable + { + Description = source.Description, + Default = source.Default, + Enum = source.Enum != null ? [.. source.Enum] : null, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + return target; + } + + internal OpenApiPaths Copy(OpenApiPaths source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiPaths { Extensions = CopyExtensions(source.Extensions) }; + Add(source, target); + foreach (var item in source) + { + target.Add(item.Key, Copy(item.Value)); + } + return target; + } + + internal OpenApiComponents Copy(OpenApiComponents source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiComponents(); + Add(source, target); + target.Schemas = CopyMap(source.Schemas); + target.Responses = CopyMap(source.Responses); + target.Parameters = CopyMap(source.Parameters); + target.Examples = CopyMap(source.Examples); + target.RequestBodies = CopyMap(source.RequestBodies); + target.Headers = CopyMap(source.Headers); + target.SecuritySchemes = CopyMap(source.SecuritySchemes); + target.Links = CopyMap(source.Links); + target.Callbacks = CopyMap(source.Callbacks); + target.PathItems = CopyMap(source.PathItems); + target.MediaTypes = CopyMap(source.MediaTypes); + target.Extensions = CopyExtensions(source.Extensions); + return target; + } + + internal OpenApiPathItem Copy(OpenApiPathItem source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiPathItem + { + Summary = source.Summary, + Description = source.Description, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + target.Operations = source.Operations != null + ? new Dictionary(source.Operations.ToDictionary(static x => x.Key, x => Copy(x.Value))) + : null; + target.Servers = CopyList(source.Servers); + target.Parameters = CopyList(source.Parameters); + return target; + } + + internal OpenApiOperation Copy(OpenApiOperation source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiOperation + { + Summary = source.Summary, + Description = source.Description, + OperationId = source.OperationId, + Deprecated = source.Deprecated, + Extensions = CopyExtensions(source.Extensions), + Metadata = CopyMetadata(source.Metadata) + }; + Add(source, target); + target.Tags = source.Tags != null ? new HashSet(source.Tags.Select(t => Copy(t)), OpenApiTagComparer.Instance) : null; + target.ExternalDocs = source.ExternalDocs is not null ? Copy(source.ExternalDocs) : null; + target.Parameters = CopyList(source.Parameters); + target.RequestBody = source.RequestBody is not null ? Copy(source.RequestBody) : null; + target.Responses = source.Responses is not null ? Copy(source.Responses) : []; + target.Callbacks = CopyMap(source.Callbacks); + target.Security = CopyList(source.Security); + target.Servers = CopyList(source.Servers); + return target; + } + + internal OpenApiResponses Copy(OpenApiResponses source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiResponses { Extensions = CopyExtensions(source.Extensions) }; + Add(source, target); + foreach (var item in source) + { + target.Add(item.Key, Copy(item.Value)); + } + return target; + } + + internal OpenApiSecurityRequirement Copy(OpenApiSecurityRequirement source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiSecurityRequirement(); + Add(source, target); + foreach (var item in source) + { + target.Add(Copy(item.Key), item.Value != null ? [.. item.Value] : []); + } + return target; + } + + internal IOpenApiPathItem Copy(IOpenApiPathItem source) => source switch + { + OpenApiPathItemReference reference => Copy(reference), + OpenApiPathItem pathItem => Copy(pathItem), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal IOpenApiCallback Copy(IOpenApiCallback source) => source switch + { + OpenApiCallbackReference reference => Copy(reference), + OpenApiCallback callback => Copy(callback), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal OpenApiCallback Copy(OpenApiCallback source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiCallback { Extensions = CopyExtensions(source.Extensions) }; + Add(source, target); + target.PathItems = source.PathItems?.ToDictionary(static x => x.Key, x => Copy(x.Value)); + return target; + } + + internal IOpenApiParameter Copy(IOpenApiParameter source) => source switch + { + OpenApiParameterReference reference => Copy(reference), + OpenApiParameter parameter => Copy(parameter), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal OpenApiParameter Copy(OpenApiParameter source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiParameter + { + Name = source.Name, + In = source.In, + Description = source.Description, + Required = source.Required, + Deprecated = source.Deprecated, + AllowEmptyValue = source.AllowEmptyValue, + Style = source.Style, + Explode = source.Explode, + AllowReserved = source.AllowReserved, + Example = source.Example is not null ? CopyJsonNode(source.Example) : null, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + target.Schema = source.Schema is not null ? Copy(source.Schema) : null; + target.Examples = CopyMap(source.Examples); + target.Content = CopyMap(source.Content); + return target; + } + + internal IOpenApiRequestBody Copy(IOpenApiRequestBody source) => source switch + { + OpenApiRequestBodyReference reference => Copy(reference), + OpenApiRequestBody requestBody => Copy(requestBody), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal OpenApiRequestBody Copy(OpenApiRequestBody source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiRequestBody + { + Description = source.Description, + Required = source.Required, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + target.Content = CopyMap(source.Content); + return target; + } + + internal IOpenApiResponse Copy(IOpenApiResponse source) => source switch + { + OpenApiResponseReference reference => Copy(reference), + OpenApiResponse response => Copy(response), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal OpenApiResponse Copy(OpenApiResponse source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiResponse + { + Summary = source.Summary, + Description = source.Description, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + target.Headers = CopyMap(source.Headers); + target.Content = CopyMap(source.Content); + target.Links = CopyMap(source.Links); + return target; + } + + internal IOpenApiHeader Copy(IOpenApiHeader source) => source switch + { + OpenApiHeaderReference reference => Copy(reference), + OpenApiHeader header => Copy(header), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal OpenApiHeader Copy(OpenApiHeader source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiHeader(source.CreateShallowCopy()) + { + Example = source.Example is not null ? CopyJsonNode(source.Example) : null, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + target.Schema = source.Schema is not null ? Copy(source.Schema) : null; + target.Examples = CopyMap(source.Examples); + target.Content = CopyMap(source.Content); + return target; + } + + internal IOpenApiMediaType Copy(IOpenApiMediaType source) => source switch + { + OpenApiMediaTypeReference reference => Copy(reference), + OpenApiMediaType mediaType => Copy(mediaType), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal OpenApiMediaType Copy(OpenApiMediaType source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiMediaType + { + Example = source.Example is not null ? CopyJsonNode(source.Example) : null, + ItemEncoding = source.ItemEncoding is not null ? Copy(source.ItemEncoding) : null, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + target.Schema = source.Schema is not null ? Copy(source.Schema) : null; + target.ItemSchema = source.ItemSchema is not null ? Copy(source.ItemSchema) : null; + target.Examples = CopyMap(source.Examples); + target.Encoding = CopyMap(source.Encoding); + target.PrefixEncoding = CopyList(source.PrefixEncoding); + return target; + } + + internal OpenApiEncoding Copy(OpenApiEncoding source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiEncoding + { + ContentType = source.ContentType, + Style = source.Style, + Explode = source.Explode, + AllowReserved = source.AllowReserved, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + target.Headers = CopyMap(source.Headers); + target.Encoding = CopyMap(source.Encoding); + target.ItemEncoding = source.ItemEncoding is not null ? Copy(source.ItemEncoding) : null; + target.PrefixEncoding = CopyList(source.PrefixEncoding); + return target; + } + + internal IOpenApiExample Copy(IOpenApiExample source) => source switch + { + OpenApiExampleReference reference => Copy(reference), + OpenApiExample example => Copy(example), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal OpenApiExample Copy(OpenApiExample source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiExample + { + Summary = source.Summary, + Description = source.Description, + ExternalValue = source.ExternalValue, + Value = source.Value is not null ? CopyJsonNode(source.Value) : null, + DataValue = source.DataValue is not null ? CopyJsonNode(source.DataValue) : null, + SerializedValue = source.SerializedValue, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + return target; + } + + internal IOpenApiLink Copy(IOpenApiLink source) => source switch + { + OpenApiLinkReference reference => Copy(reference), + OpenApiLink link => Copy(link), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal OpenApiLink Copy(OpenApiLink source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiLink + { + OperationRef = source.OperationRef, + OperationId = source.OperationId, + RequestBody = source.RequestBody is not null ? Copy(source.RequestBody) : null, + Description = source.Description, + Server = source.Server is not null ? Copy(source.Server) : null, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + target.Parameters = CopyMap(source.Parameters); + return target; + } + + internal RuntimeExpressionAnyWrapper Copy(RuntimeExpressionAnyWrapper source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new RuntimeExpressionAnyWrapper(source); + Add(source, target); + return target; + } + + internal IOpenApiSecurityScheme Copy(IOpenApiSecurityScheme source) => source switch + { + OpenApiSecuritySchemeReference reference => Copy(reference), + OpenApiSecurityScheme securityScheme => Copy(securityScheme), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal OpenApiSecurityScheme Copy(OpenApiSecurityScheme source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiSecurityScheme + { + Type = source.Type, + Description = source.Description, + Name = source.Name, + In = source.In, + Scheme = source.Scheme, + BearerFormat = source.BearerFormat, + OpenIdConnectUrl = source.OpenIdConnectUrl is not null ? CopyUri(source.OpenIdConnectUrl) : null, + OAuth2MetadataUrl = source.OAuth2MetadataUrl is not null ? CopyUri(source.OAuth2MetadataUrl) : null, + Deprecated = source.Deprecated, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + target.Flows = source.Flows is not null ? Copy(source.Flows) : null; + return target; + } + + internal OpenApiOAuthFlows Copy(OpenApiOAuthFlows source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiOAuthFlows { Extensions = CopyExtensions(source.Extensions) }; + Add(source, target); + target.Implicit = source.Implicit is not null ? Copy(source.Implicit) : null; + target.Password = source.Password is not null ? Copy(source.Password) : null; + target.ClientCredentials = source.ClientCredentials is not null ? Copy(source.ClientCredentials) : null; + target.AuthorizationCode = source.AuthorizationCode is not null ? Copy(source.AuthorizationCode) : null; + target.DeviceAuthorization = source.DeviceAuthorization is not null ? Copy(source.DeviceAuthorization) : null; + return target; + } + + internal OpenApiOAuthFlow Copy(OpenApiOAuthFlow source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiOAuthFlow + { + AuthorizationUrl = source.AuthorizationUrl is not null ? CopyUri(source.AuthorizationUrl) : null, + TokenUrl = source.TokenUrl is not null ? CopyUri(source.TokenUrl) : null, + RefreshUrl = source.RefreshUrl is not null ? CopyUri(source.RefreshUrl) : null, + DeviceAuthorizationUrl = source.DeviceAuthorizationUrl is not null ? CopyUri(source.DeviceAuthorizationUrl) : null, + Scopes = source.Scopes != null ? new Dictionary(source.Scopes) : null, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + return target; + } + + internal IOpenApiTag Copy(IOpenApiTag source) => source switch + { + OpenApiTagReference reference => Copy(reference), + OpenApiTag tag => Copy(tag), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal OpenApiTag Copy(OpenApiTag source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiTag + { + Name = source.Name, + Description = source.Description, + Summary = source.Summary, + Kind = source.Kind, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + target.ExternalDocs = source.ExternalDocs is not null ? Copy(source.ExternalDocs) : null; + target.Parent = source.Parent is not null ? Copy(source.Parent) : null; + return target; + } + + internal IOpenApiSchema Copy(IOpenApiSchema source) => source switch + { + OpenApiSchemaReference reference => Copy(reference), + OpenApiSchema schema => Copy(schema), + IDeepCopyable deepCopyable => deepCopyable.CreateDeepCopy(), + _ => source.CreateShallowCopy() + }; + + internal OpenApiSchema Copy(OpenApiSchema source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiSchema(source) + { + Vocabulary = source.Vocabulary != null ? new Dictionary(source.Vocabulary) : null, + Default = source.Default is not null ? CopyJsonNode(source.Default) : null, + Example = source.Example is not null ? CopyJsonNode(source.Example) : null, + Examples = source.Examples?.Select(CopyJsonNode).ToList(), + Enum = source.Enum?.Select(CopyJsonNode).ToList(), + Discriminator = source.Discriminator is not null ? Copy(source.Discriminator) : null, + ExternalDocs = source.ExternalDocs is not null ? Copy(source.ExternalDocs) : null, + Xml = source.Xml is not null ? Copy(source.Xml) : null, + Extensions = CopyExtensions(source.Extensions), + Metadata = CopyMetadata(source.Metadata), + UnrecognizedKeywords = source.UnrecognizedKeywords?.ToDictionary(static x => x.Key, x => CopyJsonNode(x.Value)), + DependentRequired = source.DependentRequired?.ToDictionary(static x => x.Key, static x => new HashSet(x.Value)) + }; + Add(source, target); + target.Definitions = CopyMap(source.Definitions); + if (source is IOpenApiSchemaMissingProperties missing) + { + target.Contains = missing.Contains is not null ? Copy(missing.Contains) : null; + target.UnevaluatedPropertiesSchema = missing.UnevaluatedPropertiesSchema is not null ? Copy(missing.UnevaluatedPropertiesSchema) : null; + target.ContentSchema = missing.ContentSchema is not null ? Copy(missing.ContentSchema) : null; + target.PropertyNames = missing.PropertyNames is not null ? Copy(missing.PropertyNames) : null; + target.DependentSchemas = CopyMap(missing.DependentSchemas); + target.If = missing.If is not null ? Copy(missing.If) : null; + target.Then = missing.Then is not null ? Copy(missing.Then) : null; + target.Else = missing.Else is not null ? Copy(missing.Else) : null; + } + target.AllOf = CopyList(source.AllOf); + target.OneOf = CopyList(source.OneOf); + target.AnyOf = CopyList(source.AnyOf); + target.Not = source.Not is not null ? Copy(source.Not) : null; + target.Required = source.Required != null ? new HashSet(source.Required) : null; + target.Items = source.Items is not null ? Copy(source.Items) : null; + target.Properties = CopyMap(source.Properties); + target.PatternProperties = CopyMap(source.PatternProperties); + target.AdditionalProperties = source.AdditionalProperties is not null ? Copy(source.AdditionalProperties) : null; + return target; + } + + internal OpenApiDiscriminator Copy(OpenApiDiscriminator source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiDiscriminator + { + PropertyName = source.PropertyName, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + target.Mapping = CopyMap(source.Mapping); + target.DefaultMapping = source.DefaultMapping is not null ? Copy(source.DefaultMapping) : null; + return target; + } + + internal OpenApiXml Copy(OpenApiXml source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = new OpenApiXml + { + Name = source.Name, + Namespace = source.Namespace is not null ? CopyUri(source.Namespace) : null, + Prefix = source.Prefix, + NodeType = source.NodeType, + Extensions = CopyExtensions(source.Extensions) + }; + Add(source, target); + return target; + } + + internal OpenApiSchemaReference Copy(OpenApiSchemaReference source) + { + if (GetExisting(source) is { } existing) + { + return existing; + } + + var target = (OpenApiSchemaReference)source.CreateShallowCopy(); + Add(source, target); + CopyJsonSchemaReference(source.Reference, target.Reference); + return target; + } + + internal OpenApiCallbackReference Copy(OpenApiCallbackReference source) => CopyReference(source); + internal OpenApiExampleReference Copy(OpenApiExampleReference source) => CopyReference(source); + internal OpenApiHeaderReference Copy(OpenApiHeaderReference source) => CopyReference(source); + internal OpenApiLinkReference Copy(OpenApiLinkReference source) => CopyReference(source); + internal OpenApiMediaTypeReference Copy(OpenApiMediaTypeReference source) => CopyReference(source); + internal OpenApiParameterReference Copy(OpenApiParameterReference source) => CopyReference(source); + internal OpenApiPathItemReference Copy(OpenApiPathItemReference source) => CopyReference(source); + internal OpenApiRequestBodyReference Copy(OpenApiRequestBodyReference source) => CopyReference(source); + internal OpenApiResponseReference Copy(OpenApiResponseReference source) => CopyReference(source); + internal OpenApiSecuritySchemeReference Copy(OpenApiSecuritySchemeReference source) => CopyReference(source); + internal OpenApiTagReference Copy(OpenApiTagReference source) => CopyReference(source); + + private T CopyReference(T source) + where T : class, IOpenApiReferenceable, IShallowCopyable + where TInterface : class + { + if (GetExisting(source) is { } existing) + { + return existing; + } + var target = (T)(object)source.CreateShallowCopy(); + Add(source, target); + return target; + } + + private void CopyJsonSchemaReference(JsonSchemaReference source, JsonSchemaReference target) + { + target.Default = source.Default is not null ? CopyJsonNode(source.Default) : null; + target.Examples = source.Examples?.Select(CopyJsonNode).ToList(); + target.Extensions = CopyExtensions(source.Extensions); + target.Vocabulary = source.Vocabulary != null ? new Dictionary(source.Vocabulary) : null; + target.Definitions = CopyMap(source.Definitions); + target.AllOf = CopyList(source.AllOf); + target.OneOf = CopyList(source.OneOf); + target.AnyOf = CopyList(source.AnyOf); + target.Not = source.Not is not null ? Copy(source.Not) : null; + target.Required = source.Required != null ? new HashSet(source.Required) : null; + target.Items = source.Items is not null ? Copy(source.Items) : null; + target.Contains = source.Contains is not null ? Copy(source.Contains) : null; + target.Properties = CopyMap(source.Properties); + target.PatternProperties = CopyMap(source.PatternProperties); + target.AdditionalProperties = source.AdditionalProperties is not null ? Copy(source.AdditionalProperties) : null; + target.Discriminator = source.Discriminator is not null ? Copy(source.Discriminator) : null; + target.Example = source.Example is not null ? CopyJsonNode(source.Example) : null; + target.Enum = source.Enum?.Select(CopyJsonNode).ToList(); + target.UnevaluatedPropertiesSchema = source.UnevaluatedPropertiesSchema is not null ? Copy(source.UnevaluatedPropertiesSchema) : null; + target.ExternalDocs = source.ExternalDocs is not null ? Copy(source.ExternalDocs) : null; + target.Xml = source.Xml is not null ? Copy(source.Xml) : null; + target.UnrecognizedKeywords = source.UnrecognizedKeywords?.ToDictionary(static x => x.Key, x => CopyJsonNode(x.Value)); + target.DependentRequired = source.DependentRequired?.ToDictionary(static x => x.Key, static x => new HashSet(x.Value)); + target.ContentSchema = source.ContentSchema is not null ? Copy(source.ContentSchema) : null; + target.PropertyNames = source.PropertyNames is not null ? Copy(source.PropertyNames) : null; + target.DependentSchemas = CopyMap(source.DependentSchemas); + target.If = source.If is not null ? Copy(source.If) : null; + target.Then = source.Then is not null ? Copy(source.Then) : null; + target.Else = source.Else is not null ? Copy(source.Else) : null; + } + + private IList? CopyList(IEnumerable? source) + where T : class + => source?.Select(x => CopyDynamic(x)).ToList(); + + private Dictionary? CopyMap(IDictionary? source) + where T : class + => source?.ToDictionary(static x => x.Key, x => CopyDynamic(x.Value)); + + private ISet CopyTags(ISet source) => source switch + { + SortedSet sortedSet => new SortedSet(source.Select(Copy), sortedSet.Comparer), +#if NET + ImmutableSortedSet immutableSortedSet => ImmutableSortedSet.CreateRange(immutableSortedSet.KeyComparer, source.Select(Copy)), +#endif + HashSet hashSet => new HashSet(source.Select(Copy), hashSet.Comparer), + _ => new HashSet(source.Select(Copy), OpenApiTagComparer.Instance) + }; + + private T CopyDynamic(T source) + where T : class + => source switch + { + IOpenApiSchema schema => (T)Copy(schema), + IOpenApiResponse response => (T)Copy(response), + IOpenApiParameter parameter => (T)Copy(parameter), + IOpenApiExample example => (T)Copy(example), + IOpenApiRequestBody requestBody => (T)Copy(requestBody), + IOpenApiHeader header => (T)Copy(header), + IOpenApiMediaType mediaType => (T)Copy(mediaType), + IOpenApiSecurityScheme securityScheme => (T)Copy(securityScheme), + IOpenApiLink link => (T)Copy(link), + IOpenApiCallback callback => (T)Copy(callback), + IOpenApiPathItem pathItem => (T)Copy(pathItem), + OpenApiServer server => (T)(object)Copy(server), + OpenApiServerVariable serverVariable => (T)(object)Copy(serverVariable), + OpenApiSecurityRequirement securityRequirement => (T)(object)Copy(securityRequirement), + OpenApiEncoding encoding => (T)(object)Copy(encoding), + RuntimeExpressionAnyWrapper wrapper => (T)(object)Copy(wrapper), + OpenApiTag tag => (T)(object)Copy(tag), + OpenApiTagReference tagReference => (T)(object)Copy(tagReference), + _ => source + }; + + private static Uri CopyUri(Uri source) => new(source.OriginalString, UriKind.RelativeOrAbsolute); + + private static JsonNode CopyJsonNode(JsonNode source) => source.DeepClone(); + + private Dictionary? CopyExtensions(IDictionary? source) + => source?.ToDictionary(static x => x.Key, x => CopyExtension(x.Value)); + + private IOpenApiExtension CopyExtension(IOpenApiExtension source) => source switch + { + JsonNodeExtension jsonNodeExtension => new JsonNodeExtension(CopyJsonNode(jsonNodeExtension.Node)), + IDeepCopyable deepCopyableExtension => deepCopyableExtension.CreateDeepCopy(), + _ => source + }; + + private Dictionary? CopyMetadata(IDictionary? source) + => source != null ? new Dictionary(source) : null; + + private void SetReferenceHostDocument(OpenApiDocument hostDocument) + { + var resolver = new ReferenceHostDocumentSetter(hostDocument, overwriteExisting: true); + var walker = new OpenApiWalker(resolver); + walker.Walk(hostDocument); + } +} + +internal sealed class ReferenceEqualityComparer : IEqualityComparer +{ + internal static readonly ReferenceEqualityComparer Instance = new(); + + private ReferenceEqualityComparer() + { + } + + public new bool Equals(object? x, object? y) => ReferenceEquals(x, y); + + public int GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj); +} diff --git a/src/Microsoft.OpenApi/Models/OpenApiDiscriminator.cs b/src/Microsoft.OpenApi/Models/OpenApiDiscriminator.cs index ed7a5f4d3..e0f6b44a8 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDiscriminator.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDiscriminator.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -8,7 +8,7 @@ namespace Microsoft.OpenApi /// /// Discriminator object. /// - public class OpenApiDiscriminator : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiDiscriminator : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// REQUIRED. The name of the property in the payload that will hold the discriminator value. @@ -46,6 +46,13 @@ public OpenApiDiscriminator(OpenApiDiscriminator discriminator) DefaultMapping = discriminator?.DefaultMapping; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiDiscriminator CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open Api v3.2 /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 68259a618..456a8244f 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -19,7 +19,7 @@ namespace Microsoft.OpenApi /// /// Describes an OpenAPI object (OpenAPI document). See: https://spec.openapis.org /// - public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IMetadataContainer + public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IMetadataContainer, IDeepCopyable { /// /// Register components in the document to the workspace @@ -154,6 +154,13 @@ public OpenApiDocument(OpenApiDocument? document) BaseUri = document?.BaseUri != null ? document.BaseUri : new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid()); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiDocument CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to an Open API document using the specified version. /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs index 367107a52..d22cc3b1d 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -10,7 +10,7 @@ namespace Microsoft.OpenApi /// /// ExternalDocs object. /// - public class OpenApiEncoding : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiEncoding : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// Explode backing variable @@ -96,6 +96,13 @@ public OpenApiEncoding(OpenApiEncoding encoding) Extensions = encoding?.Extensions != null ? new Dictionary(encoding.Extensions) : null; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiEncoding CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open Api v3.2 /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiExample.cs b/src/Microsoft.OpenApi/Models/OpenApiExample.cs index b3c93a5c7..5b723ba62 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiExample.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiExample.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Example Object. /// - public class OpenApiExample : IOpenApiExtensible, IOpenApiExample + public class OpenApiExample : IOpenApiExtensible, IOpenApiExample, IDeepCopyable { /// public string? Summary { get; set; } @@ -135,5 +135,11 @@ public IOpenApiExample CreateShallowCopy() { return new OpenApiExample(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiExample CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs b/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs index e7737b011..fc2e39944 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// ExternalDocs object. /// - public class OpenApiExternalDocs : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiExternalDocs : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// A short description of the target documentation. @@ -41,6 +41,13 @@ public OpenApiExternalDocs(OpenApiExternalDocs externalDocs) Extensions = externalDocs?.Extensions != null ? new Dictionary(externalDocs.Extensions) : null; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiExternalDocs CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open Api v3.2. /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiHeader.cs b/src/Microsoft.OpenApi/Models/OpenApiHeader.cs index 7e5ea273f..fd1fa0b17 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiHeader.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiHeader.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -11,7 +11,7 @@ namespace Microsoft.OpenApi /// Header Object. /// The Header Object follows the structure of the Parameter Object. /// - public class OpenApiHeader : IOpenApiHeader, IOpenApiExtensible + public class OpenApiHeader : IOpenApiHeader, IOpenApiExtensible, IDeepCopyable { /// public string? Description { get; set; } @@ -196,5 +196,11 @@ public IOpenApiHeader CreateShallowCopy() { return new OpenApiHeader(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiHeader CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiInfo.cs b/src/Microsoft.OpenApi/Models/OpenApiInfo.cs index 9a5a7828b..f852dde54 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiInfo.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Open API Info Object, it provides the metadata about the Open API. /// - public class OpenApiInfo : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiInfo : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// REQUIRED. The title of the application. @@ -71,6 +71,13 @@ public OpenApiInfo(OpenApiInfo info) Extensions = info?.Extensions != null ? new Dictionary(info.Extensions) : null; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiInfo CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open Api v3.2 /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiLicense.cs b/src/Microsoft.OpenApi/Models/OpenApiLicense.cs index 4c9a32945..3910b3898 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiLicense.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiLicense.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// License Object. /// - public class OpenApiLicense : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiLicense : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// REQUIRED. The license name used for the API. @@ -47,6 +47,13 @@ public OpenApiLicense(OpenApiLicense license) Extensions = license?.Extensions != null ? new Dictionary(license.Extensions) : null; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiLicense CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open Api v3.2 /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiLink.cs b/src/Microsoft.OpenApi/Models/OpenApiLink.cs index 585cab160..f847a0389 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiLink.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiLink.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Link Object. /// - public class OpenApiLink : IOpenApiExtensible, IOpenApiLink + public class OpenApiLink : IOpenApiExtensible, IOpenApiLink, IDeepCopyable { /// public string? OperationRef { get; set; } @@ -110,5 +110,11 @@ public IOpenApiLink CreateShallowCopy() { return new OpenApiLink(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiLink CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs index 7adcb21a6..94042a6d1 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -11,7 +11,7 @@ namespace Microsoft.OpenApi /// /// Media Type Object. /// - public class OpenApiMediaType : IOpenApiSerializable, IOpenApiExtensible, IOpenApiMediaType + public class OpenApiMediaType : IOpenApiSerializable, IOpenApiExtensible, IOpenApiMediaType, IDeepCopyable { /// public IOpenApiSchema? Schema { get; set; } @@ -79,6 +79,12 @@ public IOpenApiMediaType CreateShallowCopy() { return new OpenApiMediaType(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiMediaType CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } /// /// Serialize to Open Api v3.2. diff --git a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs index d07d2a02a..9ec75ed6f 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// OAuth Flow Object. /// - public class OpenApiOAuthFlow : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiOAuthFlow : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// REQUIRED. The authorization URL to be used for this flow. @@ -61,6 +61,13 @@ public OpenApiOAuthFlow(OpenApiOAuthFlow oAuthFlow) Extensions = oAuthFlow?.Extensions != null ? new Dictionary(oAuthFlow.Extensions) : null; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiOAuthFlow CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open Api v3.2 /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs index a83670f39..d7bd49212 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// OAuth Flows Object. /// - public class OpenApiOAuthFlows : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiOAuthFlows : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// Configuration for the OAuth Implicit flow @@ -60,6 +60,13 @@ public OpenApiOAuthFlows(OpenApiOAuthFlows oAuthFlows) Extensions = oAuthFlows?.Extensions != null ? new Dictionary(oAuthFlows.Extensions) : null; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiOAuthFlows CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open Api v3.2 /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs index ee1afcc0a..b088892b2 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -10,7 +10,7 @@ namespace Microsoft.OpenApi /// /// Operation Object. /// - public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible, IMetadataContainer + public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible, IMetadataContainer, IDeepCopyable { private ISet? _tags; /// @@ -145,6 +145,13 @@ public OpenApiOperation(OpenApiOperation operation) Metadata = operation.Metadata != null ? new Dictionary(operation.Metadata) : null; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiOperation CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open Api v3.2. /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs index 24c8db173..0856c8ab0 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -12,7 +12,7 @@ namespace Microsoft.OpenApi /// /// Parameter Object. /// - public class OpenApiParameter : IOpenApiExtensible, IOpenApiParameter + public class OpenApiParameter : IOpenApiExtensible, IOpenApiParameter, IDeepCopyable { private bool? _explode; private ParameterStyle? _style; @@ -344,6 +344,12 @@ public IOpenApiParameter CreateShallowCopy() { return new OpenApiParameter(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiParameter CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs b/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs index 1696388c8..1f9a217c8 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -11,7 +11,7 @@ namespace Microsoft.OpenApi /// /// Path Item Object: to describe the operations available on a single path. /// - public class OpenApiPathItem : IOpenApiExtensible, IOpenApiPathItem + public class OpenApiPathItem : IOpenApiExtensible, IOpenApiPathItem, IDeepCopyable { /// public string? Summary { get; set; } @@ -218,5 +218,11 @@ public IOpenApiPathItem CreateShallowCopy() { return new OpenApiPathItem(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiPathItem CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiPaths.cs b/src/Microsoft.OpenApi/Models/OpenApiPaths.cs index 1aa163e3c..583e449f6 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiPaths.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiPaths.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. namespace Microsoft.OpenApi @@ -6,7 +6,7 @@ namespace Microsoft.OpenApi /// /// Paths object. /// - public class OpenApiPaths : OpenApiExtensibleDictionary + public class OpenApiPaths : OpenApiExtensibleDictionary, IDeepCopyable { /// /// Parameterless constructor @@ -21,5 +21,12 @@ public OpenApiPaths() { } /// This creates a shallow copy, the path items are the same reference as in the provided parameter. /// public OpenApiPaths(OpenApiPaths paths) : base(dictionary: paths) { } + + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiPaths CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs index fa29d3ea0..3ca4d3371 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -10,7 +10,7 @@ namespace Microsoft.OpenApi /// /// Request Body Object /// - public class OpenApiRequestBody : IOpenApiExtensible, IOpenApiRequestBody + public class OpenApiRequestBody : IOpenApiExtensible, IOpenApiRequestBody, IDeepCopyable { /// public string? Description { get; set; } @@ -167,5 +167,11 @@ public IOpenApiRequestBody CreateShallowCopy() { return new OpenApiRequestBody(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiRequestBody CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs index 4ee39336e..d5aa1e06a 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -10,7 +10,7 @@ namespace Microsoft.OpenApi /// /// Response object. /// - public class OpenApiResponse : IOpenApiExtensible, IOpenApiResponse + public class OpenApiResponse : IOpenApiExtensible, IOpenApiResponse, IDeepCopyable { /// public string? Summary { get; set; } @@ -195,5 +195,11 @@ public IOpenApiResponse CreateShallowCopy() { return new OpenApiResponse(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiResponse CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponses.cs b/src/Microsoft.OpenApi/Models/OpenApiResponses.cs index 63a144a2a..48848785a 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiResponses.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiResponses.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. namespace Microsoft.OpenApi @@ -6,7 +6,7 @@ namespace Microsoft.OpenApi /// /// Responses object. /// - public class OpenApiResponses : OpenApiExtensibleDictionary + public class OpenApiResponses : OpenApiExtensibleDictionary, IDeepCopyable { /// /// Parameterless constructor @@ -18,5 +18,12 @@ public OpenApiResponses() { } /// /// The public OpenApiResponses(OpenApiResponses openApiResponses) : base(dictionary: openApiResponses) { } + + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiResponses CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index 2aceda191..c24f66a5e 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -19,7 +19,7 @@ namespace Microsoft.OpenApi /// - Serialization: To produce something functionally equivalent to boolean schemas, create an empty /// for "true" behavior, or create a schema with only set to an empty schema for "false" behavior. /// - public class OpenApiSchema : IOpenApiExtensible, IOpenApiSchema, IOpenApiSchemaMissingProperties, IOpenApiSchemaWithUnevaluatedProperties, IMetadataContainer + public class OpenApiSchema : IOpenApiExtensible, IOpenApiSchema, IOpenApiSchemaMissingProperties, IOpenApiSchemaWithUnevaluatedProperties, IMetadataContainer, IDeepCopyable { private static readonly IEnumerable s_singleNullElementList = [ JsonNullSentinel.JsonNull ]; @@ -1117,5 +1117,11 @@ public IOpenApiSchema CreateShallowCopy() { return new OpenApiSchema(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiSchema CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs b/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs index 766b73f0d..05aa014d3 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -16,7 +16,8 @@ namespace Microsoft.OpenApi /// For other security scheme types, the array MUST be empty. /// public class OpenApiSecurityRequirement : Dictionary>, - IOpenApiSerializable + IOpenApiSerializable, + IDeepCopyable { /// /// Initializes the class. @@ -126,6 +127,13 @@ public virtual void SerializeAsV2(IOpenApiWriter writer) SerializeInternal(writer, (w, s) => s.SerializeAsV2(w)); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiSecurityRequirement CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Comparer for OpenApiSecurityScheme that only considers the Id in the Reference /// (i.e. the string that will actually be displayed in the written document) diff --git a/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs b/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs index 225910968..94f7db7cd 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Security Scheme Object. /// - public class OpenApiSecurityScheme : IOpenApiExtensible, IOpenApiSecurityScheme, IOAuth2MetadataProvider + public class OpenApiSecurityScheme : IOpenApiExtensible, IOpenApiSecurityScheme, IOAuth2MetadataProvider, IDeepCopyable { /// public SecuritySchemeType? Type { get; set; } @@ -307,5 +307,11 @@ public IOpenApiSecurityScheme CreateShallowCopy() { return new OpenApiSecurityScheme(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiSecurityScheme CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiServer.cs b/src/Microsoft.OpenApi/Models/OpenApiServer.cs index 0eaecd125..e87b4951d 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiServer.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiServer.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Server Object: an object representing a Server. /// - public class OpenApiServer : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiServer : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// An optional string describing the host designated by the URL. CommonMark syntax MAY be used for rich text representation. @@ -56,6 +56,13 @@ public OpenApiServer(OpenApiServer server) Extensions = server?.Extensions != null ? new Dictionary(server.Extensions) : null; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiServer CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open Api v3.2 /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs b/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs index f07990b17..d73c31617 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -8,7 +8,7 @@ namespace Microsoft.OpenApi /// /// Server Variable Object. /// - public class OpenApiServerVariable : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiServerVariable : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// An optional description for the server variable. CommonMark syntax MAY be used for rich text representation. @@ -50,6 +50,13 @@ public OpenApiServerVariable(OpenApiServerVariable serverVariable) Extensions = serverVariable?.Extensions != null ? new Dictionary(serverVariable.Extensions) : serverVariable?.Extensions; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiServerVariable CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open Api v3.2 /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiTag.cs b/src/Microsoft.OpenApi/Models/OpenApiTag.cs index a27ec135d..b0e0c3f88 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiTag.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiTag.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Tag Object. /// - public class OpenApiTag : IOpenApiExtensible, IOpenApiTag, IOpenApiDescribedElement + public class OpenApiTag : IOpenApiExtensible, IOpenApiTag, IOpenApiDescribedElement, IDeepCopyable { /// public string? Name { get; set; } @@ -159,5 +159,11 @@ public IOpenApiTag CreateShallowCopy() { return new OpenApiTag(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiTag CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiXml.cs b/src/Microsoft.OpenApi/Models/OpenApiXml.cs index 6e4e88962..c7ff62d83 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiXml.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiXml.cs @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// XML Object. /// - public class OpenApiXml : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiXml : IOpenApiSerializable, IOpenApiExtensible, IDeepCopyable { /// /// Replaces the name of the element/attribute used for the described schema property. @@ -87,6 +87,13 @@ public OpenApiXml(OpenApiXml xml) Extensions = xml?.Extensions != null ? new Dictionary(xml.Extensions) : null; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public OpenApiXml CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Serialize to Open Api v3.2 /// diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiCallbackReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiCallbackReference.cs index bdf22eb03..02820fc76 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiCallbackReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiCallbackReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -8,7 +8,7 @@ namespace Microsoft.OpenApi /// /// Callback Object Reference: A reference to a map of possible out-of band callbacks related to the parent operation. /// - public class OpenApiCallbackReference : BaseOpenApiReferenceHolder, IOpenApiCallback + public class OpenApiCallbackReference : BaseOpenApiReferenceHolder, IOpenApiCallback, IDeepCopyable { /// /// Constructor initializing the reference object. @@ -57,6 +57,12 @@ public IOpenApiCallback CreateShallowCopy() return new OpenApiCallbackReference(this); } /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiCallback CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// protected override BaseOpenApiReference CopyReference(BaseOpenApiReference sourceReference) { return new BaseOpenApiReference(sourceReference); diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiExampleReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiExampleReference.cs index 5834d4fb9..ab7aa35b8 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiExampleReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiExampleReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Example Object Reference. /// - public class OpenApiExampleReference : BaseOpenApiReferenceHolder, IOpenApiExample + public class OpenApiExampleReference : BaseOpenApiReferenceHolder, IOpenApiExample, IDeepCopyable { /// /// Constructor initializing the reference object. @@ -80,6 +80,12 @@ public IOpenApiExample CreateShallowCopy() return new OpenApiExampleReference(this); } /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiExample CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// protected override OpenApiReferenceWithDescriptionAndSummary CopyReference(OpenApiReferenceWithDescriptionAndSummary sourceReference) { return new OpenApiReferenceWithDescriptionAndSummary(sourceReference); diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiHeaderReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiHeaderReference.cs index d0c7fe04e..690068a6d 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiHeaderReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiHeaderReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Header Object Reference. /// - public class OpenApiHeaderReference : BaseOpenApiReferenceHolder, IOpenApiHeader + public class OpenApiHeaderReference : BaseOpenApiReferenceHolder, IOpenApiHeader, IDeepCopyable { /// /// Constructor initializing the reference object. @@ -85,6 +85,12 @@ public IOpenApiHeader CreateShallowCopy() return new OpenApiHeaderReference(this); } /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiHeader CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// protected override OpenApiReferenceWithDescription CopyReference(OpenApiReferenceWithDescription sourceReference) { return new OpenApiReferenceWithDescription(sourceReference); diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiLinkReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiLinkReference.cs index a11decf63..4d54a46be 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiLinkReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiLinkReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -8,7 +8,7 @@ namespace Microsoft.OpenApi /// /// Link Object Reference. /// - public class OpenApiLinkReference : BaseOpenApiReferenceHolder, IOpenApiLink + public class OpenApiLinkReference : BaseOpenApiReferenceHolder, IOpenApiLink, IDeepCopyable { /// /// Constructor initializing the reference object. @@ -74,6 +74,12 @@ public IOpenApiLink CreateShallowCopy() return new OpenApiLinkReference(this); } /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiLink CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// protected override OpenApiReferenceWithDescription CopyReference(OpenApiReferenceWithDescription sourceReference) { return new OpenApiReferenceWithDescription(sourceReference); diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiMediaTypeReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiMediaTypeReference.cs index 47816ed44..40c76bddb 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiMediaTypeReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiMediaTypeReference.cs @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Media Type Object Reference. /// - public class OpenApiMediaTypeReference : BaseOpenApiReferenceHolder, IOpenApiMediaType + public class OpenApiMediaTypeReference : BaseOpenApiReferenceHolder, IOpenApiMediaType, IDeepCopyable { /// /// Constructor initializing the reference object. @@ -68,6 +68,12 @@ public IOpenApiMediaType CreateShallowCopy() { return new OpenApiMediaTypeReference(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiMediaType CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } /// protected override BaseOpenApiReference CopyReference(BaseOpenApiReference sourceReference) diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiParameterReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiParameterReference.cs index a4e3ea4de..170d265f5 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiParameterReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiParameterReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -11,7 +11,7 @@ namespace Microsoft.OpenApi /// /// Parameter Object Reference. /// - public class OpenApiParameterReference : BaseOpenApiReferenceHolder, IOpenApiParameter + public class OpenApiParameterReference : BaseOpenApiReferenceHolder, IOpenApiParameter, IDeepCopyable { /// /// Constructor initializing the reference object. @@ -93,6 +93,12 @@ public IOpenApiParameter CreateShallowCopy() { return new OpenApiParameterReference(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiParameter CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } /// protected override OpenApiReferenceWithDescription CopyReference(OpenApiReferenceWithDescription sourceReference) diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiPathItemReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiPathItemReference.cs index 291c75308..8cae61ebc 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiPathItemReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiPathItemReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Path Item Object Reference: to describe the operations available on a single path. /// - public class OpenApiPathItemReference : BaseOpenApiReferenceHolder, IOpenApiPathItem + public class OpenApiPathItemReference : BaseOpenApiReferenceHolder, IOpenApiPathItem, IDeepCopyable { /// @@ -72,6 +72,12 @@ public IOpenApiPathItem CreateShallowCopy() { return new OpenApiPathItemReference(this); } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiPathItem CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } /// public override void SerializeAsV2(IOpenApiWriter writer) diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiRequestBodyReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiRequestBodyReference.cs index f700f1bc9..e6f56fbb9 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiRequestBodyReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiRequestBodyReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Request Body Object Reference. /// - public class OpenApiRequestBodyReference : BaseOpenApiReferenceHolder, IOpenApiRequestBody + public class OpenApiRequestBodyReference : BaseOpenApiReferenceHolder, IOpenApiRequestBody, IDeepCopyable { /// /// Constructor initializing the reference object. @@ -89,6 +89,12 @@ public IOpenApiRequestBody CreateShallowCopy() return new OpenApiRequestBodyReference(this); } /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiRequestBody CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// protected override OpenApiReferenceWithDescription CopyReference(OpenApiReferenceWithDescription sourceReference) { return new OpenApiReferenceWithDescription(sourceReference); diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiResponseReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiResponseReference.cs index b99922e79..8b4bf6ba0 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiResponseReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiResponseReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -8,7 +8,7 @@ namespace Microsoft.OpenApi /// /// Response Object Reference. /// - public class OpenApiResponseReference : BaseOpenApiReferenceHolder, IOpenApiResponse + public class OpenApiResponseReference : BaseOpenApiReferenceHolder, IOpenApiResponse, IDeepCopyable { /// /// Constructor initializing the reference object. @@ -70,6 +70,12 @@ public IOpenApiResponse CreateShallowCopy() return new OpenApiResponseReference(this); } /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiResponse CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// protected override OpenApiReferenceWithDescriptionAndSummary CopyReference(OpenApiReferenceWithDescriptionAndSummary sourceReference) { return new OpenApiReferenceWithDescriptionAndSummary(sourceReference); diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs index 532060345..2cc323d62 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -15,7 +15,7 @@ namespace Microsoft.OpenApi /// These getters are object-model conveniences and do not represent JSON Schema /// evaluation semantics. /// - public class OpenApiSchemaReference : BaseOpenApiReferenceHolder, IOpenApiSchema, IOpenApiSchemaMissingProperties, IOpenApiSchemaWithUnevaluatedProperties, IOpenApiExtensible + public class OpenApiSchemaReference : BaseOpenApiReferenceHolder, IOpenApiSchema, IOpenApiSchemaMissingProperties, IOpenApiSchemaWithUnevaluatedProperties, IOpenApiExtensible, IDeepCopyable { /// @@ -272,9 +272,15 @@ public IOpenApiSchema CreateShallowCopy() return new OpenApiSchemaReference(this); } /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiSchema CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// protected override JsonSchemaReference CopyReference(JsonSchemaReference sourceReference) { - return new JsonSchemaReference(sourceReference); + return new JsonSchemaReference(sourceReference)!; } #pragma warning restore CS0618 } diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiSecuritySchemeReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiSecuritySchemeReference.cs index 8fdd0b73a..1f9f26f7f 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiSecuritySchemeReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiSecuritySchemeReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,7 +9,7 @@ namespace Microsoft.OpenApi /// /// Security Scheme Object Reference. /// - public class OpenApiSecuritySchemeReference : BaseOpenApiReferenceHolder, IOpenApiSecurityScheme, IOAuth2MetadataProvider + public class OpenApiSecuritySchemeReference : BaseOpenApiReferenceHolder, IOpenApiSecurityScheme, IOAuth2MetadataProvider, IDeepCopyable { /// /// Constructor initializing the reference object. @@ -78,6 +78,12 @@ public IOpenApiSecurityScheme CreateShallowCopy() return new OpenApiSecuritySchemeReference(this); } /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiSecurityScheme CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// protected override OpenApiReferenceWithDescription CopyReference(OpenApiReferenceWithDescription sourceReference) { return new OpenApiReferenceWithDescription(sourceReference); diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs index 78b92638c..030c6e141 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -10,7 +10,7 @@ namespace Microsoft.OpenApi /// /// Tag Object Reference /// - public class OpenApiTagReference : BaseOpenApiReferenceHolder, IOpenApiTag + public class OpenApiTagReference : BaseOpenApiReferenceHolder, IOpenApiTag, IDeepCopyable { /// /// Resolved target of the reference. @@ -92,6 +92,12 @@ public IOpenApiTag CreateShallowCopy() return new OpenApiTagReference(this); } /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public IOpenApiTag CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// protected override BaseOpenApiReference CopyReference(BaseOpenApiReference sourceReference) { return new BaseOpenApiReference(sourceReference); diff --git a/src/Microsoft.OpenApi/Models/RuntimeExpressionAnyWrapper.cs b/src/Microsoft.OpenApi/Models/RuntimeExpressionAnyWrapper.cs index 0fdc87eaa..dbf34cfea 100644 --- a/src/Microsoft.OpenApi/Models/RuntimeExpressionAnyWrapper.cs +++ b/src/Microsoft.OpenApi/Models/RuntimeExpressionAnyWrapper.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Text.Json.Nodes; @@ -8,7 +8,7 @@ namespace Microsoft.OpenApi /// /// The wrapper either for or /// - public class RuntimeExpressionAnyWrapper : IOpenApiElement + public class RuntimeExpressionAnyWrapper : IOpenApiElement, IDeepCopyable { private JsonNode? _any; private RuntimeExpression? _expression; @@ -27,6 +27,13 @@ public RuntimeExpressionAnyWrapper(RuntimeExpressionAnyWrapper runtimeExpression Expression = runtimeExpressionAnyWrapper?.Expression; } + /// + [System.Diagnostics.CodeAnalysis.Experimental("OPENAPI001")] + public RuntimeExpressionAnyWrapper CreateDeepCopy() + { + return new OpenApiDeepCopyContext().Copy(this); + } + /// /// Gets/Sets the /// You must use the method to check whether Default was assigned a null value in the document. diff --git a/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt b/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt index 4699f67cc..0967394cb 100644 --- a/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt +++ b/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt @@ -1,3 +1,47 @@ #nullable enable const Microsoft.OpenApi.OpenApiConstants.JsonSchemaExamplesExtension = "x-jsonschema-examples" -> string! const Microsoft.OpenApi.OpenApiConstants.OaiLicenseIdentifier = "x-oai-license-identifier" -> string! +[OPENAPI001]Microsoft.OpenApi.IDeepCopyable +[OPENAPI001]Microsoft.OpenApi.IDeepCopyable.CreateDeepCopy() -> T +[OPENAPI001]Microsoft.OpenApi.OpenApiCallback.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiCallback! +[OPENAPI001]Microsoft.OpenApi.OpenApiCallbackReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiCallback! +[OPENAPI001]Microsoft.OpenApi.OpenApiComponents.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiComponents! +[OPENAPI001]Microsoft.OpenApi.OpenApiContact.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiContact! +[OPENAPI001]Microsoft.OpenApi.OpenApiDiscriminator.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiDiscriminator! +[OPENAPI001]Microsoft.OpenApi.OpenApiDocument.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiDocument! +[OPENAPI001]Microsoft.OpenApi.OpenApiEncoding.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiEncoding! +[OPENAPI001]Microsoft.OpenApi.OpenApiExample.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiExample! +[OPENAPI001]Microsoft.OpenApi.OpenApiExampleReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiExample! +[OPENAPI001]Microsoft.OpenApi.OpenApiExternalDocs.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiExternalDocs! +[OPENAPI001]Microsoft.OpenApi.OpenApiHeader.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiHeader! +[OPENAPI001]Microsoft.OpenApi.OpenApiHeaderReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiHeader! +[OPENAPI001]Microsoft.OpenApi.OpenApiInfo.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiInfo! +[OPENAPI001]Microsoft.OpenApi.OpenApiLicense.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiLicense! +[OPENAPI001]Microsoft.OpenApi.OpenApiLink.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiLink! +[OPENAPI001]Microsoft.OpenApi.OpenApiLinkReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiLink! +[OPENAPI001]Microsoft.OpenApi.OpenApiMediaType.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiMediaType! +[OPENAPI001]Microsoft.OpenApi.OpenApiMediaTypeReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiMediaType! +[OPENAPI001]Microsoft.OpenApi.OpenApiOAuthFlow.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiOAuthFlow! +[OPENAPI001]Microsoft.OpenApi.OpenApiOAuthFlows.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiOAuthFlows! +[OPENAPI001]Microsoft.OpenApi.OpenApiOperation.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiOperation! +[OPENAPI001]Microsoft.OpenApi.OpenApiParameter.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiParameter! +[OPENAPI001]Microsoft.OpenApi.OpenApiParameterReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiParameter! +[OPENAPI001]Microsoft.OpenApi.OpenApiPathItem.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiPathItem! +[OPENAPI001]Microsoft.OpenApi.OpenApiPathItemReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiPathItem! +[OPENAPI001]Microsoft.OpenApi.OpenApiPaths.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiPaths! +[OPENAPI001]Microsoft.OpenApi.OpenApiRequestBody.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiRequestBody! +[OPENAPI001]Microsoft.OpenApi.OpenApiRequestBodyReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiRequestBody! +[OPENAPI001]Microsoft.OpenApi.OpenApiResponse.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiResponse! +[OPENAPI001]Microsoft.OpenApi.OpenApiResponseReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiResponse! +[OPENAPI001]Microsoft.OpenApi.OpenApiResponses.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiResponses! +[OPENAPI001]Microsoft.OpenApi.OpenApiSchema.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiSchema! +[OPENAPI001]Microsoft.OpenApi.OpenApiSchemaReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiSchema! +[OPENAPI001]Microsoft.OpenApi.OpenApiSecurityRequirement.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiSecurityRequirement! +[OPENAPI001]Microsoft.OpenApi.OpenApiSecurityScheme.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiSecurityScheme! +[OPENAPI001]Microsoft.OpenApi.OpenApiSecuritySchemeReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiSecurityScheme! +[OPENAPI001]Microsoft.OpenApi.OpenApiServer.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiServer! +[OPENAPI001]Microsoft.OpenApi.OpenApiServerVariable.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiServerVariable! +[OPENAPI001]Microsoft.OpenApi.OpenApiTag.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiTag! +[OPENAPI001]Microsoft.OpenApi.OpenApiTagReference.CreateDeepCopy() -> Microsoft.OpenApi.IOpenApiTag! +[OPENAPI001]Microsoft.OpenApi.OpenApiXml.CreateDeepCopy() -> Microsoft.OpenApi.OpenApiXml! +[OPENAPI001]Microsoft.OpenApi.RuntimeExpressionAnyWrapper.CreateDeepCopy() -> Microsoft.OpenApi.RuntimeExpressionAnyWrapper! diff --git a/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs b/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs index eabb01c4e..646329674 100644 --- a/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs +++ b/src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs @@ -9,10 +9,12 @@ namespace Microsoft.OpenApi internal class ReferenceHostDocumentSetter : OpenApiVisitorBase { private readonly OpenApiDocument _currentDocument; + private readonly bool _overwriteExisting; - public ReferenceHostDocumentSetter(OpenApiDocument currentDocument) + public ReferenceHostDocumentSetter(OpenApiDocument currentDocument, bool overwriteExisting = false) { _currentDocument = currentDocument; + _overwriteExisting = overwriteExisting; } /// @@ -26,7 +28,14 @@ public override void Visit(IOpenApiReferenceHolder referenceHolder) IOpenApiReferenceHolder { Reference: BaseOpenApiReference baseReference } => baseReference, _ => throw new OpenApiException($"Unsupported reference holder type: {referenceHolder.GetType().FullName}") }; - reference.EnsureHostDocumentIsSet(_currentDocument); + if (_overwriteExisting) + { + reference.SetHostDocument(_currentDocument); + } + else + { + reference.EnsureHostDocumentIsSet(_currentDocument); + } } } } diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDeepCopyTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiDeepCopyTests.cs new file mode 100644 index 000000000..16c33a393 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDeepCopyTests.cs @@ -0,0 +1,244 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +#pragma warning disable OPENAPI001 + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Net.Http; +using System.Text.Json.Nodes; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Models; + +public class OpenApiDeepCopyTests +{ + [Fact] + public void CreateDeepCopyClonesDocumentObjectGraph() + { + var document = CreateDocument(); + + var copy = document.CreateDeepCopy(); + + Assert.NotSame(document, copy); + Assert.NotSame(document.Info, copy.Info); + Assert.NotSame(document.Paths, copy.Paths); + Assert.NotSame(document.Components, copy.Components); + + var originalPathItem = Assert.IsType(document.Paths["/pets"]); + var copiedPathItem = Assert.IsType(copy.Paths["/pets"]); + Assert.NotSame(originalPathItem, copiedPathItem); + Assert.NotSame(originalPathItem.Operations, copiedPathItem.Operations); + + var originalOperations = originalPathItem.Operations; + Assert.NotNull(originalOperations); + var copiedOperations = copiedPathItem.Operations; + Assert.NotNull(copiedOperations); + var originalOperation = originalOperations[HttpMethod.Get]; + var copiedOperation = copiedOperations[HttpMethod.Get]; + Assert.NotSame(originalOperation, copiedOperation); + Assert.NotSame(originalOperation.Responses, copiedOperation.Responses); + + var originalComponents = document.Components; + Assert.NotNull(originalComponents); + var originalSchemas = originalComponents.Schemas; + Assert.NotNull(originalSchemas); + + var copiedComponents = copy.Components; + Assert.NotNull(copiedComponents); + var copiedSchemas = copiedComponents.Schemas; + Assert.NotNull(copiedSchemas); + + var originalPetSchema = Assert.IsType(originalSchemas["Pet"]); + var copiedPetSchema = Assert.IsType(copiedSchemas["Pet"]); + Assert.NotSame(originalPetSchema, copiedPetSchema); + var copiedProperties = copiedPetSchema.Properties; + Assert.NotNull(copiedProperties); + var originalProperties = originalPetSchema.Properties; + Assert.NotNull(originalProperties); + Assert.NotSame(originalProperties, copiedProperties); + Assert.Same(copiedPetSchema, copiedProperties["friend"]); + + var copiedIdSchema = Assert.IsType(copiedProperties["id"]); + copiedIdSchema.Description = "updated in copy"; + var originalIdSchema = Assert.IsType(originalProperties["id"]); + Assert.Equal("original id", originalIdSchema.Description); + } + + [Fact] + public void CreateDeepCopyClonesJsonNodesAndExtensions() + { + var document = CreateDocument(); + + var copy = document.CreateDeepCopy(); + + var originalMediaType = GetJsonMediaType(document); + var copiedMediaType = GetJsonMediaType(copy); + + var copiedExample = Assert.IsType(copiedMediaType.Example); + var originalExample = Assert.IsType(originalMediaType.Example); + Assert.NotSame(originalExample, copiedExample); + copiedExample["name"] = "copy"; + var originalName = originalExample["name"]; + Assert.NotNull(originalName); + Assert.Equal("original", originalName.GetValue()); + + var originalExtensions = document.Info.Extensions; + Assert.NotNull(originalExtensions); + var copiedExtensions = copy.Info.Extensions; + Assert.NotNull(copiedExtensions); + var originalExtension = Assert.IsType(originalExtensions["x-info"]); + var copiedExtension = Assert.IsType(copiedExtensions["x-info"]); + Assert.NotSame(originalExtension, copiedExtension); + Assert.NotSame(originalExtension.Node, copiedExtension.Node); + } + + [Fact] + public void CreateDeepCopyRemapsCopiedReferencesToCopiedDocument() + { + var document = CreateDocument(); + + var copy = document.CreateDeepCopy(); + + var copiedMediaType = GetJsonMediaType(copy); + + var copiedSchemaReference = Assert.IsType(copiedMediaType.Schema); + Assert.Same(copy, copiedSchemaReference.Reference.HostDocument); + var copiedComponents = copy.Components; + Assert.NotNull(copiedComponents); + var copiedSchemas = copiedComponents.Schemas; + Assert.NotNull(copiedSchemas); + var originalComponents = document.Components; + Assert.NotNull(originalComponents); + var originalSchemas = originalComponents.Schemas; + Assert.NotNull(originalSchemas); + Assert.Same(copiedSchemas["Pet"], copiedSchemaReference.Target); + Assert.NotSame(originalSchemas["Pet"], copiedSchemaReference.Target); + } + + [Fact] + public void CreateDeepCopyPreservesOrderedDocumentTags() + { + var document = CreateDocument(); + document.Tags = new SortedSet(new DescendingOpenApiTagComparer()) + { + new() { Name = "tagB" }, + new() { Name = "tagA" }, + new() { Name = "tagC" }, + }; + + var copy = document.CreateDeepCopy(); + + var copiedTags = Assert.IsType>(copy.Tags); + Assert.Equal(["tagC", "tagB", "tagA"], copiedTags.Select(static t => t.Name)); + var originalTags = Assert.IsType>(document.Tags); + Assert.NotSame(originalTags.First(), copiedTags.First()); + } + + [Fact] + public void CreateDeepCopyPreservesImmutableOrderedDocumentTags() + { + var document = CreateDocument(); + document.Tags = ImmutableSortedSet.Create( + new DescendingOpenApiTagComparer(), + [ + new OpenApiTag { Name = "tagB" }, + new OpenApiTag { Name = "tagA" }, + new OpenApiTag { Name = "tagC" }, + ]); + + var copy = document.CreateDeepCopy(); + + var copiedTags = Assert.IsType>(copy.Tags); + Assert.Equal(["tagC", "tagB", "tagA"], copiedTags.Select(static t => t.Name)); + var originalTags = Assert.IsType>(document.Tags); + Assert.NotSame(originalTags.First(), copiedTags.First()); + } + + private static OpenApiMediaType GetJsonMediaType(OpenApiDocument document) + { + var pathItem = Assert.IsType(document.Paths["/pets"]); + var operations = pathItem.Operations; + Assert.NotNull(operations); + var operation = operations[HttpMethod.Get]; + var responses = operation.Responses; + Assert.NotNull(responses); + var response = Assert.IsType(responses["200"]); + var content = response.Content; + Assert.NotNull(content); + return Assert.IsType(content["application/json"]); + } + + private static OpenApiDocument CreateDocument() + { + var document = new OpenApiDocument + { + Info = new OpenApiInfo + { + Title = "Pets", + Version = "1.0.0", + Extensions = new Dictionary + { + ["x-info"] = new JsonNodeExtension(new JsonObject { ["value"] = "original" }) + } + }, + Components = new OpenApiComponents(), + Paths = new OpenApiPaths() + }; + + var petSchema = new OpenApiSchema + { + Type = JsonSchemaType.Object, + Properties = new Dictionary + { + ["id"] = new OpenApiSchema + { + Type = JsonSchemaType.String, + Description = "original id" + } + } + }; + petSchema.Properties["friend"] = petSchema; + document.Components.Schemas = new Dictionary + { + ["Pet"] = petSchema + }; + + document.Paths.Add("/pets", new OpenApiPathItem + { + Operations = new Dictionary + { + [HttpMethod.Get] = new OpenApiOperation + { + Responses = new OpenApiResponses + { + ["200"] = new OpenApiResponse + { + Description = "ok", + Content = new Dictionary + { + ["application/json"] = new OpenApiMediaType + { + Schema = new OpenApiSchemaReference("Pet", document), + Example = new JsonObject { ["name"] = "original" } + } + } + } + } + } + } + }); + + document.SetReferenceHostDocument(); + return document; + } + + private sealed class DescendingOpenApiTagComparer : IComparer + { + public int Compare(OpenApiTag x, OpenApiTag y) + { + return string.Compare(y?.Name, x?.Name, System.StringComparison.Ordinal); + } + } +}