Skip to content
13 changes: 11 additions & 2 deletions src/Microsoft.OpenApi/Services/ComparisonContext.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.Models;
Copy link
Member Author

@Shwetap05 Shwetap05 Aug 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ft.OpenAp [](start = 13, length = 9)

Still adding more tests for comparers #Closed


namespace Microsoft.OpenApi.Services
{
Expand All @@ -13,15 +14,23 @@ public class ComparisonContext
{
private readonly IList<OpenApiDifference> _openApiDifferences = new List<OpenApiDifference>();
private readonly Stack<string> _path = new Stack<string>();
internal readonly OpenApiDocument SourceDocument;
internal readonly Stack<OpenApiSchema> SourceSchemaLoop = new Stack<OpenApiSchema>();
internal readonly OpenApiDocument TargetDocument;
internal readonly Stack<OpenApiSchema> TargetSchemaLoop = new Stack<OpenApiSchema>();
internal OpenApiComparerFactory OpenApiComparerFactory;

/// <summary>
/// Creates instance of <see cref="ComparisonContext"/>.
/// </summary>
/// <param name="openApiComparerFactory"></param>
public ComparisonContext(OpenApiComparerFactory openApiComparerFactory)
public ComparisonContext(
OpenApiComparerFactory openApiComparerFactory,
OpenApiDocument sourceDocument,
OpenApiDocument targetDocument)
{
OpenApiComparerFactory = openApiComparerFactory;
SourceDocument = sourceDocument;
TargetDocument = targetDocument;
}

/// <summary>
Expand Down
6 changes: 3 additions & 3 deletions src/Microsoft.OpenApi/Services/OpenApiComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ public static IEnumerable<OpenApiDifference> Compare(OpenApiDocument source, Ope
throw Error.ArgumentNull(nameof(target));
}

var comparisionContext = new ComparisonContext(new OpenApiComparerFactory());
var comparisonContext = new ComparisonContext(new OpenApiComparerFactory(), source, target);

new OpenApiDocumentComparer().Compare(source, target, comparisionContext);
new OpenApiDocumentComparer().Compare(source, target, comparisonContext);

return comparisionContext.OpenApiDifferences;
return comparisonContext.OpenApiDifferences;
}
}
}
75 changes: 71 additions & 4 deletions src/Microsoft.OpenApi/Services/OpenApiComparerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
// Licensed under the MIT license.

using System;
using Microsoft.OpenApi.Models;

namespace Microsoft.OpenApi.Services
{
/// <summary>
/// Defines behavior for comparing parts of <see cref="OpenAPiDocument"/> class.
/// Defines behavior for comparing parts of <see cref="OpenApiDocument"/> class.
/// </summary>
/// <typeparam name="T">Type of class to compare.</typeparam>
public abstract class OpenApiComparerBase<T>
Expand Down Expand Up @@ -63,7 +64,73 @@ internal void Compare(bool? source, bool? target, ComparisonContext comparisonCo
comparisonContext.AddOpenApiDifference(new OpenApiDifference
{
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update,
OpenApiComparedElementType = typeof(bool),
OpenApiComparedElementType = typeof(bool?),
SourceValue = source,
TargetValue = target,
Pointer = comparisonContext.PathString
});
}
}

/// <summary>
/// Compares two decimal object.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <param name="comparisonContext">The context under which to compare the objects.</param>
internal void Compare(decimal? source, decimal? target, ComparisonContext comparisonContext)
{
if (source == null && target == null)
{
return;
}

if (source != target)
{
comparisonContext.AddOpenApiDifference(new OpenApiDifference
{
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update,
OpenApiComparedElementType = typeof(decimal?),
SourceValue = source,
TargetValue = target,
Pointer = comparisonContext.PathString
});
}
}

/// <summary>
/// Compares Enum.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <param name="comparisonContext">The context under which to compare the objects.</param>
internal void Compare<TEnum>(Enum source, Enum target, ComparisonContext comparisonContext)
{
if (source == null && target == null)
{
return;
}

if (source == null || target == null)
{
comparisonContext.AddOpenApiDifference(new OpenApiDifference
{
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update,
OpenApiComparedElementType = typeof(TEnum),
SourceValue = source,
TargetValue = target,
Pointer = comparisonContext.PathString
});

return;
}

if (!source.Equals(target))
{
comparisonContext.AddOpenApiDifference(new OpenApiDifference
{
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update,
OpenApiComparedElementType = typeof(TEnum),
SourceValue = source,
TargetValue = target,
Pointer = comparisonContext.PathString
Expand All @@ -82,7 +149,7 @@ internal void WalkAndAddOpenApiDifference(
string segment,
OpenApiDifference openApiDifference)
{
comparisonContext.Enter(segment.Replace("/", "~1"));
comparisonContext.Enter(segment.Replace("~", "~0").Replace("/", "~1"));
openApiDifference.Pointer = comparisonContext.PathString;
comparisonContext.AddOpenApiDifference(openApiDifference);
comparisonContext.Exit();
Expand All @@ -99,7 +166,7 @@ protected virtual void WalkAndCompare(
string segment,
Action compare)
{
comparisonContext.Enter(segment.Replace("/", "~1"));
comparisonContext.Enter(segment.Replace("~", "~0").Replace("/", "~1"));
compare();
comparisonContext.Exit();
}
Expand Down
23 changes: 22 additions & 1 deletion src/Microsoft.OpenApi/Services/OpenApiComparerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,28 @@ public class OpenApiComparerFactory
{typeof(OpenApiOperation), new OpenApiOperationComparer()},
{typeof(IDictionary<OperationType, OpenApiOperation>), new OpenApiOperationsComparer()},
{typeof(IList<OpenApiParameter>), new OpenApiParametersComparer()},
{typeof(OpenApiParameter), new OpenApiParameterComparer()}
{typeof(OpenApiParameter), new OpenApiParameterComparer()},
{typeof(OpenApiSchema), new OpenApiSchemaComparer()},
{typeof(OpenApiMediaType), new OpenApiMediaTypeComparer()},
{typeof(IDictionary<string, OpenApiMediaType>), new OpenApiDictionaryComparer<OpenApiMediaType>()},
{typeof(IDictionary<string, OpenApiResponse>), new OpenApiDictionaryComparer<OpenApiResponse>()},
{typeof(IDictionary<string, OpenApiHeader>), new OpenApiDictionaryComparer<OpenApiHeader>()},
{typeof(IDictionary<string, OpenApiEncoding>), new OpenApiDictionaryComparer<OpenApiEncoding>()},
{
typeof(IDictionary<string, OpenApiServerVariable>),
new OpenApiDictionaryComparer<OpenApiServerVariable>()
},
{typeof(IDictionary<string, OpenApiParameter>), new OpenApiDictionaryComparer<OpenApiParameter>()},
{typeof(IDictionary<string, OpenApiRequestBody>), new OpenApiDictionaryComparer<OpenApiRequestBody>()},
{typeof(IDictionary<string, OpenApiSchema>), new OpenApiDictionaryComparer<OpenApiSchema>()},
{typeof(OpenApiHeader), new OpenApiHeaderComparer()},
{typeof(OpenApiRequestBody), new OpenApiRequestBodyComparer()},
{typeof(OpenApiResponse), new OpenApiResponseComparer()},
{typeof(OpenApiComponents), new OpenApiComponentsComparer()},
{typeof(OpenApiEncoding), new OpenApiEncodingComparer()},
{typeof(IList<OpenApiServer>), new OpenApiServersComparer()},
{typeof(OpenApiServer), new OpenApiServerComparer()},
{typeof(OpenApiServerVariable), new OpenApiServerVariableComparer()}
};

private readonly Dictionary<Type, object> _typeToComparerMap = new Dictionary<Type, object>();
Expand Down
87 changes: 87 additions & 0 deletions src/Microsoft.OpenApi/Services/OpenApiComponentsComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

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

namespace Microsoft.OpenApi.Services
{
/// <summary>
/// Defines behavior for comparing properties of <see cref="OpenApiComponents"/>.
/// </summary>
public class OpenApiComponentsComparer : OpenApiComparerBase<OpenApiComponents>
{
/// <summary>
/// Executes comparision against source and target <see cref="OpenApiComponents"/>.
/// </summary>
/// <param name="sourceComponents">The source.</param>
/// <param name="targetComponents">The target.</param>
/// <param name="comparisonContext">Context under which to compare the source and target.</param>
public override void Compare(
OpenApiComponents sourceComponents,
OpenApiComponents targetComponents,
ComparisonContext comparisonContext)
{
if (sourceComponents == null && targetComponents == null)
{
return;
}

if (sourceComponents == null || targetComponents == null)
{
comparisonContext.AddOpenApiDifference(
new OpenApiDifference
{
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update,
SourceValue = sourceComponents,
TargetValue = targetComponents,
OpenApiComparedElementType = typeof(OpenApiComponents),
Pointer = comparisonContext.PathString
});

return;
}

WalkAndCompare(
comparisonContext,
OpenApiConstants.Parameters,
() => comparisonContext
.GetComparer<IDictionary<string, OpenApiParameter>>()
.Compare(sourceComponents.Parameters, targetComponents.Parameters, comparisonContext));

WalkAndCompare(
comparisonContext,
OpenApiConstants.RequestBodies,
() => comparisonContext
.GetComparer<IDictionary<string, OpenApiRequestBody>>()
.Compare(sourceComponents.RequestBodies, targetComponents.RequestBodies, comparisonContext));

WalkAndCompare(
comparisonContext,
OpenApiConstants.Responses,
() => comparisonContext
.GetComparer<IDictionary<string, OpenApiResponse>>()
.Compare(sourceComponents.Responses, targetComponents.Responses, comparisonContext));

WalkAndCompare(
comparisonContext,
OpenApiConstants.Schemas,
() => comparisonContext
.GetComparer<IDictionary<string, OpenApiSchema>>()
.Compare(sourceComponents.Schemas, targetComponents.Schemas, comparisonContext));

WalkAndCompare(
comparisonContext,
OpenApiConstants.Headers,
() => comparisonContext
.GetComparer<IDictionary<string, OpenApiHeader>>()
.Compare(sourceComponents.Headers, targetComponents.Headers, comparisonContext));

// To Do compare Examples
// To Do compare SecuritySchemes
// To Do compare Links
// To Do compare Callbacks
// To Do compare Extensions
}
}
}
90 changes: 90 additions & 0 deletions src/Microsoft.OpenApi/Services/OpenApiDictionaryComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

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

namespace Microsoft.OpenApi.Services
{
/// <summary>
/// Defines behavior for comparing <see cref="IDictionary{TKey,TValue}"/> where TKey is <see cref="string"/>
/// and TValue is <see cref="IOpenApiSerializable"/>.
/// </summary>
public class OpenApiDictionaryComparer<T> : OpenApiComparerBase<IDictionary<string, T>>
where T : IOpenApiSerializable
{
/// <summary>
/// Executes comparision against source and target <see cref="IDictionary{TKey, TValue}"/>
/// where TKey is <see cref="string"/> and TValue is <see cref="IOpenApiSerializable"/>.
/// </summary>
/// <param name="sourceFragment">The source.</param>
/// <param name="targetFragment">The target.</param>
/// <param name="comparisonContext">Context under which to compare the source and target.</param>
public override void Compare(
IDictionary<string, T> sourceFragment,
IDictionary<string, T> targetFragment,
ComparisonContext comparisonContext)
{
if (sourceFragment == null && targetFragment == null)
{
return;
}

if (sourceFragment == null || targetFragment == null)
{
comparisonContext.AddOpenApiDifference(
new OpenApiDifference
{
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update,
SourceValue = sourceFragment,
TargetValue = targetFragment,
OpenApiComparedElementType = typeof(IDictionary<string, T>),
Pointer = comparisonContext.PathString
});

return;
}

var newKeysInTarget = targetFragment.Keys.Except(sourceFragment.Keys).ToList();

foreach (var newKeyInTarget in newKeysInTarget)
{
WalkAndAddOpenApiDifference(
comparisonContext,
newKeyInTarget,
new OpenApiDifference
{
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add,
TargetValue = new KeyValuePair<string, T>(
newKeyInTarget,
targetFragment[newKeyInTarget]),
OpenApiComparedElementType = typeof(KeyValuePair<string, T>)
});
}

foreach (var source in sourceFragment)
{
if (targetFragment.Keys.Contains(source.Key))
{
WalkAndCompare(comparisonContext, source.Key,
() => comparisonContext
.GetComparer<T>()
.Compare(source.Value, targetFragment[source.Key], comparisonContext));
}
else
{
WalkAndAddOpenApiDifference(
comparisonContext,
source.Key,
new OpenApiDifference
{
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove,
SourceValue = source,
OpenApiComparedElementType = typeof(KeyValuePair<string, T>)
});
}
}
}
}
}
Loading