Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/Microsoft.OpenApi/Helpers/DictionaryCloneHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.OpenApi.Helpers
{
/// <summary>
/// Helper class for deep cloning dictionaries.
/// </summary>
internal class DictionaryCloneHelper
{
/// <summary>
/// Deep clone key value pairs in a dictionary.
/// </summary>
/// <typeparam name="T">The type of the key of the dictionary.</typeparam>
/// <typeparam name="U">The type of the value of the dictionary.</typeparam>
/// <param name="dictionary">The target dictionary to clone.</param>
/// <returns>The cloned dictionary.</returns>
internal static Dictionary<T, U> Clone<T, U>(IDictionary<T, U> dictionary)
{
if (dictionary is null) return null;
var clonedDictionary = new Dictionary<T, U>(dictionary.Keys.Count);

foreach (var kvp in dictionary)
{
// Create instance of the specified type using the constructor matching the specified parameter types.
clonedDictionary[kvp.Key] = (U)Activator.CreateInstance(kvp.Value.GetType(), kvp.Value);
}


return clonedDictionary;
}
}
}
1 change: 1 addition & 0 deletions src/Microsoft.OpenApi/Models/OpenApiCallback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Expressions;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down
21 changes: 10 additions & 11 deletions src/Microsoft.OpenApi/Models/OpenApiComponents.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -78,15 +77,15 @@ public OpenApiComponents() { }
/// </summary>
public OpenApiComponents(OpenApiComponents components)
{
Schemas = components?.Schemas != null ? new Dictionary<string, OpenApiSchema>(components.Schemas) : null;
Responses = components?.Responses != null ? new Dictionary<string, OpenApiResponse>(components.Responses) : null;
Parameters = components?.Parameters != null ? new Dictionary<string, OpenApiParameter>(components.Parameters) : null;
Examples = components?.Examples != null ? new Dictionary<string, OpenApiExample>(components.Examples) : null;
RequestBodies = components?.RequestBodies != null ? new Dictionary<string, OpenApiRequestBody>(components.RequestBodies) : null;
Headers = components?.Headers != null ? new Dictionary<string, OpenApiHeader>(components.Headers) : null;
SecuritySchemes = components?.SecuritySchemes != null ? new Dictionary<string, OpenApiSecurityScheme>(components.SecuritySchemes) : null;
Links = components?.Links != null ? new Dictionary<string, OpenApiLink>(components.Links) : null;
Callbacks = components?.Callbacks != null ? new Dictionary<string, OpenApiCallback>(components.Callbacks) : null;
Schemas = DictionaryCloneHelper.Clone(components?.Schemas);
Responses = DictionaryCloneHelper.Clone(components?.Responses);
Parameters = DictionaryCloneHelper.Clone(components?.Parameters);
Examples = DictionaryCloneHelper.Clone(components?.Examples);
RequestBodies = DictionaryCloneHelper.Clone(components?.RequestBodies);
Headers = DictionaryCloneHelper.Clone(components?.Headers);
SecuritySchemes = DictionaryCloneHelper.Clone(components?.SecuritySchemes);
Links = DictionaryCloneHelper.Clone(components?.Links);
Callbacks = DictionaryCloneHelper.Clone(components?.Callbacks);
Extensions = components?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(components.Extensions) : null;
}

Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Security.Cryptography;
using System.Text;
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Writers;
Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiEncoding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -64,7 +65,7 @@ public OpenApiEncoding() {}
public OpenApiEncoding(OpenApiEncoding encoding)
{
ContentType = encoding?.ContentType ?? ContentType;
Headers = encoding?.Headers != null ? new Dictionary<string, OpenApiHeader>(encoding.Headers) : null;
Headers = DictionaryCloneHelper.Clone(encoding?.Headers);
Style = encoding?.Style ?? Style;
Explode = encoding?.Explode ?? Explode;
AllowReserved = encoding?.AllowReserved ?? AllowReserved;
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.OpenApi/Models/OpenApiExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down
5 changes: 3 additions & 2 deletions src/Microsoft.OpenApi/Models/OpenApiHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -107,8 +108,8 @@ public OpenApiHeader(OpenApiHeader header)
AllowReserved = header?.AllowReserved ?? AllowReserved;
Schema = header?.Schema != null ? new(header?.Schema) : null;
Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(header?.Example);
Examples = header?.Examples != null ? new Dictionary<string, OpenApiExample>(header.Examples) : null;
Content = header?.Content != null ? new Dictionary<string, OpenApiMediaType>(header.Content) : null;
Examples = DictionaryCloneHelper.Clone(header?.Examples);
Content = DictionaryCloneHelper.Clone(header?.Content);
Extensions = header?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(header.Extensions) : null;
}

Expand Down
5 changes: 3 additions & 2 deletions src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -55,8 +56,8 @@ public OpenApiMediaType(OpenApiMediaType mediaType)
{
Schema = mediaType?.Schema != null ? new(mediaType?.Schema) : null;
Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(mediaType?.Example);
Examples = mediaType?.Examples != null ? new Dictionary<string, OpenApiExample>(mediaType.Examples) : null;
Encoding = mediaType?.Encoding != null ? new Dictionary<string, OpenApiEncoding>(mediaType.Encoding) : null;
Examples = DictionaryCloneHelper.Clone(mediaType?.Examples);
Encoding = DictionaryCloneHelper.Clone(mediaType?.Encoding);
Extensions = mediaType?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(mediaType.Extensions) : null;
}

Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -124,7 +125,7 @@ public OpenApiOperation(OpenApiOperation operation)
Parameters = operation?.Parameters != null ? new List<OpenApiParameter>(operation.Parameters) : null;
RequestBody = new(operation?.RequestBody);
Responses = operation?.Responses != null ? new(operation?.Responses) : null;
Callbacks = operation?.Callbacks != null ? new Dictionary<string, OpenApiCallback>(operation.Callbacks) : null;
Callbacks = DictionaryCloneHelper.Clone(operation?.Callbacks);
Deprecated = operation?.Deprecated ?? Deprecated;
Security = operation?.Security != null ? new List<OpenApiSecurityRequirement>(operation.Security) : null;
Servers = operation?.Servers != null ? new List<OpenApiServer>(operation.Servers) : null;
Expand Down
5 changes: 3 additions & 2 deletions src/Microsoft.OpenApi/Models/OpenApiParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Runtime;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -162,9 +163,9 @@ public OpenApiParameter(OpenApiParameter parameter)
Explode = parameter?.Explode ?? Explode;
AllowReserved = parameter?.AllowReserved ?? AllowReserved;
Schema = parameter?.Schema != null ? new(parameter?.Schema) : null;
Examples = parameter?.Examples != null ? new Dictionary<string, OpenApiExample>(parameter.Examples) : null;
Examples = DictionaryCloneHelper.Clone(parameter?.Examples);
Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(parameter?.Example);
Content = parameter?.Content != null ? new Dictionary<string, OpenApiMediaType>(parameter.Content) : null;
Content = DictionaryCloneHelper.Clone(parameter?.Content);
Extensions = parameter?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(parameter.Extensions) : null;
AllowEmptyValue = parameter?.AllowEmptyValue ?? AllowEmptyValue;
Deprecated = parameter?.Deprecated ?? Deprecated;
Expand Down
4 changes: 3 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiPathItem.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Collections.Generic;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -77,7 +79,7 @@ public OpenApiPathItem(OpenApiPathItem pathItem)
{
Summary = pathItem?.Summary ?? Summary;
Description = pathItem?.Description ?? Description;
Operations = pathItem?.Operations != null ? new Dictionary<OperationType, OpenApiOperation>(pathItem.Operations) : null;
Operations = DictionaryCloneHelper.Clone(pathItem?.Operations);
Servers = pathItem?.Servers != null ? new List<OpenApiServer>(pathItem.Servers) : null;
Parameters = pathItem?.Parameters != null ? new List<OpenApiParameter>(pathItem.Parameters) : null;
Extensions = pathItem?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(pathItem.Extensions) : null;
Expand Down
7 changes: 6 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiPaths.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Collections;
using System.Collections.Generic;
using Microsoft.OpenApi.Helpers;

namespace Microsoft.OpenApi.Models
{
/// <summary>
Expand All @@ -17,6 +22,6 @@ public OpenApiPaths() {}
/// Initializes a copy of <see cref="OpenApiPaths"/> object
/// </summary>
/// <param name="paths">The <see cref="OpenApiPaths"/>.</param>
public OpenApiPaths(OpenApiPaths paths) : base(dictionary: paths) {}
public OpenApiPaths(OpenApiPaths paths) : base(DictionaryCloneHelper.Clone(paths)) { }
}
}
3 changes: 2 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -61,7 +62,7 @@ public OpenApiRequestBody(OpenApiRequestBody requestBody)
Reference = requestBody?.Reference != null ? new(requestBody?.Reference) : null;
Description = requestBody?.Description ?? Description;
Required = requestBody?.Required ?? Required;
Content = requestBody?.Content != null ? new Dictionary<string, OpenApiMediaType>(requestBody.Content) : null;
Content = DictionaryCloneHelper.Clone(requestBody?.Content);
Extensions = requestBody?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(requestBody.Extensions) : null;
}

Expand Down
7 changes: 4 additions & 3 deletions src/Microsoft.OpenApi/Models/OpenApiResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -62,9 +63,9 @@ public OpenApiResponse() {}
public OpenApiResponse(OpenApiResponse response)
{
Description = response?.Description ?? Description;
Headers = response?.Headers != null ? new Dictionary<string, OpenApiHeader>(response.Headers) : null;
Content = response?.Content != null ? new Dictionary<string, OpenApiMediaType>(response.Content) : null;
Links = response?.Links != null ? new Dictionary<string, OpenApiLink>(response.Links) : null;
Headers = DictionaryCloneHelper.Clone(response?.Headers);
Content = DictionaryCloneHelper.Clone(response?.Content);
Links = DictionaryCloneHelper.Clone(response?.Links);
Extensions = response?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(response.Extensions) : null;
UnresolvedReference = response?.UnresolvedReference ?? UnresolvedReference;
Reference = response?.Reference != null ? new(response?.Reference) : null;
Expand Down
4 changes: 3 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiResponses.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using Microsoft.OpenApi.Helpers;

namespace Microsoft.OpenApi.Models
{
/// <summary>
Expand All @@ -17,6 +19,6 @@ public OpenApiResponses() { }
/// Initializes a copy of <see cref="OpenApiResponses"/> object
/// </summary>
/// <param name="openApiResponses">The <see cref="OpenApiResponses"/></param>
public OpenApiResponses(OpenApiResponses openApiResponses) : base(dictionary: openApiResponses) {}
public OpenApiResponses(OpenApiResponses openApiResponses) : base(DictionaryCloneHelper.Clone(openApiResponses)) {}
}
}
3 changes: 2 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -276,7 +277,7 @@ public OpenApiSchema(OpenApiSchema schema)
MaxItems = schema?.MaxItems ?? MaxItems;
MinItems = schema?.MinItems ?? MinItems;
UniqueItems = schema?.UniqueItems ?? UniqueItems;
Properties = schema?.Properties != null ? new Dictionary<string, OpenApiSchema>(schema.Properties) : null;
Properties = DictionaryCloneHelper.Clone(schema?.Properties);
MaxProperties = schema?.MaxProperties ?? MaxProperties;
MinProperties = schema?.MinProperties ?? MinProperties;
AdditionalPropertiesAllowed = schema?.AdditionalPropertiesAllowed ?? AdditionalPropertiesAllowed;
Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -48,7 +49,7 @@ public OpenApiServer(OpenApiServer server)
{
Description = server?.Description ?? Description;
Url = server?.Url ?? Url;
Variables = server?.Variables != null ? new Dictionary<string, OpenApiServerVariable>(server.Variables) : null;
Variables = DictionaryCloneHelper.Clone(server?.Variables);
Extensions = server?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(server.Extensions) : null;
}

Expand Down
8 changes: 8 additions & 0 deletions test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Writers;
using VerifyXunit;
using Xunit;
Expand Down Expand Up @@ -1344,12 +1345,19 @@ public void CopyConstructorForAdvancedDocumentWorks()
// Arrange & Act
var doc = new OpenApiDocument(AdvancedDocument);

var docOpId = doc.Paths["/pets"].Operations[OperationType.Get].OperationId = "findAllMyPets";
var advancedDocOpId = AdvancedDocument.Paths["/pets"].Operations[OperationType.Get].OperationId;
var responseSchemaTypeCopy = doc.Paths["/pets"].Operations[OperationType.Get].Responses["200"].Content["application/json"].Schema.Type = "object";
var advancedDocResponseSchemaType = AdvancedDocument.Paths["/pets"].Operations[OperationType.Get].Responses["200"].Content["application/json"].Schema.Type;

// Assert
Assert.NotNull(doc.Info);
Assert.NotNull(doc.Servers);
Assert.NotNull(doc.Paths);
Assert.Equal(2, doc.Paths.Count);
Assert.NotNull(doc.Components);
Assert.NotEqual(docOpId, advancedDocOpId);
Assert.NotEqual(responseSchemaTypeCopy, advancedDocResponseSchemaType);
}

[Fact]
Expand Down