Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OpenAPI Diff to compare two OpenAPI specification files #546

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
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
14 changes: 14 additions & 0 deletions Microsoft.OpenApi.sln
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Hidi", "s
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Hidi.Tests", "test\Microsoft.OpenApi.Hidi.Tests\Microsoft.OpenApi.Hidi.Tests.csproj", "{D8F799DD-04AC-4A13-B344-45A5B944450A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Diff", "src\Microsoft.OpenApi.Diff\Microsoft.OpenApi.Diff.csproj", "{35F25729-19A7-43BA-AAB5-0859EC524F6F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Diff.Tests", "test\Microsoft.OpenApi.Diff.Tests\Microsoft.OpenApi.Diff.Tests.csproj", "{8491A9E6-4F63-4F55-9DFB-AEFE60602351}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -64,6 +68,14 @@ Global
{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}.Release|Any CPU.Build.0 = Release|Any CPU
{35F25729-19A7-43BA-AAB5-0859EC524F6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{35F25729-19A7-43BA-AAB5-0859EC524F6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{35F25729-19A7-43BA-AAB5-0859EC524F6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{35F25729-19A7-43BA-AAB5-0859EC524F6F}.Release|Any CPU.Build.0 = Release|Any CPU
{8491A9E6-4F63-4F55-9DFB-AEFE60602351}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8491A9E6-4F63-4F55-9DFB-AEFE60602351}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8491A9E6-4F63-4F55-9DFB-AEFE60602351}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8491A9E6-4F63-4F55-9DFB-AEFE60602351}.Release|Any CPU.Build.0 = Release|Any CPU
{D8F799DD-04AC-4A13-B344-45A5B944450A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8F799DD-04AC-4A13-B344-45A5B944450A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8F799DD-04AC-4A13-B344-45A5B944450A}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -80,6 +92,8 @@ Global
{1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
{AD79B61D-88CF-497C-9ED5-41AE3867C5AC} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE} = {E546B92F-20A8-49C3-8323-4B25BB78F3E1}
{35F25729-19A7-43BA-AAB5-0859EC524F6F} = {E546B92F-20A8-49C3-8323-4B25BB78F3E1}
{8491A9E6-4F63-4F55-9DFB-AEFE60602351} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
{D8F799DD-04AC-4A13-B344-45A5B944450A} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
Expand Down
34 changes: 34 additions & 0 deletions src/Microsoft.OpenApi.Diff/BusinessObjects/ChangeBO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Microsoft.OpenApi.Diff.Enums;

namespace Microsoft.OpenApi.Diff.BusinessObjects
{
public class ChangeBO<T>
where T : class
{
public T OldValue { get; }
public T NewValue { get; }
public TypeEnum Type { get; }

private ChangeBO(T oldValue, T newValue, TypeEnum type)
{
OldValue = oldValue;
NewValue = newValue;
Type = type;
}

public static ChangeBO<T> Changed(T oldValue, T newValue)
{
return new ChangeBO<T>(oldValue, newValue, TypeEnum.Changed);
}

public static ChangeBO<T> Added(T newValue)
{
return new ChangeBO<T>(null, newValue, TypeEnum.Added);
}

public static ChangeBO<T> Removed(T oldValue)
{
return new ChangeBO<T>(oldValue, null, TypeEnum.Removed);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Diff.Enums;
using Microsoft.OpenApi.Diff.Extensions;
using Microsoft.OpenApi.Models;

namespace Microsoft.OpenApi.Diff.BusinessObjects
{
public class ChangedAPIResponseBO : ComposedChangedBO
{
protected override ChangedElementTypeEnum GetElementType() => ChangedElementTypeEnum.Response;

private readonly OpenApiResponses _oldApiResponses;
private readonly OpenApiResponses _newApiResponses;
private readonly DiffContextBO _context;

public Dictionary<string, OpenApiResponse> Increased { get; set; }
public Dictionary<string, OpenApiResponse> Missing { get; set; }
public Dictionary<string, ChangedResponseBO> Changed { get; set; }
public ChangedExtensionsBO Extensions { get; set; }

public ChangedAPIResponseBO(OpenApiResponses oldApiResponses, OpenApiResponses newApiResponses, DiffContextBO context)
{
_oldApiResponses = oldApiResponses;
_newApiResponses = newApiResponses;
_context = context;
Increased = new Dictionary<string, OpenApiResponse>();
Missing = new Dictionary<string, OpenApiResponse>();
Changed = new Dictionary<string, ChangedResponseBO>();
}

public override List<(string Identifier, ChangedBO Change)> GetChangedElements()
{
return new List<(string Identifier, ChangedBO Change)>(
Changed.Select(x => (x.Key, (ChangedBO)x.Value))
)
{
(null, Extensions)
}
.Where(x => x.Change != null).ToList();
}

public override DiffResultBO IsCoreChanged()
{
if (Increased.IsNullOrEmpty() && Missing.IsNullOrEmpty())
{
return new DiffResultBO(DiffResultEnum.NoChanges);
}
if (!Increased.IsNullOrEmpty() && Missing.IsNullOrEmpty())
{
return new DiffResultBO(DiffResultEnum.Compatible);
}
return new DiffResultBO(DiffResultEnum.Incompatible);
}

protected override List<ChangedInfoBO> GetCoreChanges() =>
GetCoreChangeInfosOfComposed(Increased.Keys.ToList(), Missing.Keys.ToList(), x => x);
}
}
78 changes: 78 additions & 0 deletions src/Microsoft.OpenApi.Diff/BusinessObjects/ChangedBO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Diff.Enums;
using Microsoft.OpenApi.Diff.Extensions;
using Microsoft.OpenApi.Extensions;

namespace Microsoft.OpenApi.Diff.BusinessObjects
{
public abstract class ChangedBO
{
protected ChangedBO()
{
}

protected abstract ChangedElementTypeEnum GetElementType();
protected string GetIdentifier(string identifier) => identifier ?? GetElementType().GetDisplayName();

public abstract DiffResultBO IsChanged();

public virtual DiffResultBO IsCoreChanged() => IsChanged();

protected abstract List<ChangedInfoBO> GetCoreChanges();

public ChangedInfosBO GetCoreChangeInfo(string identifier, List<string> parentPath = null)
{
var isChanged = IsCoreChanged();
var newPath = new List<string>();

if (!parentPath.IsNullOrEmpty())
newPath = new List<string>(parentPath);

newPath.Add(GetIdentifier(identifier));

var result = new ChangedInfosBO
{
Path = newPath,
ChangeType = isChanged
};

if (isChanged.IsUnchanged())
return result;

result.Changes = GetCoreChanges();
return result;
}

public virtual List<ChangedInfosBO> GetAllChangeInfoFlat(string identifier, List<string> parentPath = null)
{
return new List<ChangedInfosBO>
{
GetCoreChangeInfo(identifier, parentPath)
};
}

public static DiffResultBO Result(ChangedBO changed)
{
return changed?.IsChanged() ?? new DiffResultBO(DiffResultEnum.NoChanges);
}
public bool IsCompatible()
{
return IsChanged().IsCompatible();
}

public bool IsIncompatible()
{
return IsChanged().IsIncompatible();
}

public bool IsUnchanged()
{
return IsChanged().IsUnchanged();
}

public bool IsDifferent()
{
return IsChanged().IsDifferent();
}
}
}
55 changes: 55 additions & 0 deletions src/Microsoft.OpenApi.Diff/BusinessObjects/ChangedContentBO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Diff.Enums;
using Microsoft.OpenApi.Diff.Extensions;
using Microsoft.OpenApi.Models;

namespace Microsoft.OpenApi.Diff.BusinessObjects
{
public class ChangedContentBO : ComposedChangedBO
{
protected override ChangedElementTypeEnum GetElementType() => ChangedElementTypeEnum.Content;

private readonly Dictionary<string, OpenApiMediaType> _oldContent;
private readonly Dictionary<string, OpenApiMediaType> _newContent;
private readonly DiffContextBO _context;

public Dictionary<string, OpenApiMediaType> Increased { get; set; }
public Dictionary<string, OpenApiMediaType> Missing { get; set; }
public Dictionary<string, ChangedMediaTypeBO> Changed { get; set; }

public ChangedContentBO(Dictionary<string, OpenApiMediaType> oldContent, Dictionary<string, OpenApiMediaType> newContent, DiffContextBO context)
{
_oldContent = oldContent;
_newContent = newContent;
_context = context;
Increased = new Dictionary<string, OpenApiMediaType>();
Missing = new Dictionary<string, OpenApiMediaType>();
Changed = new Dictionary<string, ChangedMediaTypeBO>();
}

public override List<(string Identifier, ChangedBO Change)> GetChangedElements()
{
return new List<(string Identifier, ChangedBO Change)>(
Changed.Select(x => (x.Key, (ChangedBO)x.Value))
)
.Where(x => x.Change != null).ToList();
}

public override DiffResultBO IsCoreChanged()
{
if (Increased.IsNullOrEmpty() && Missing.IsNullOrEmpty())
{
return new DiffResultBO(DiffResultEnum.NoChanges);
}
if (_context.IsRequest && Missing.IsNullOrEmpty() || _context.IsResponse && Increased.IsNullOrEmpty())
{
return new DiffResultBO(DiffResultEnum.Compatible);
}
return new DiffResultBO(DiffResultEnum.Incompatible);
}

protected override List<ChangedInfoBO> GetCoreChanges() =>
GetCoreChangeInfosOfComposed(Increased.Keys.ToList(), Missing.Keys.ToList(), x => x);
}
}
25 changes: 25 additions & 0 deletions src/Microsoft.OpenApi.Diff/BusinessObjects/ChangedEnumBO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Diff.Enums;
using Microsoft.OpenApi.Diff.Extensions;

namespace Microsoft.OpenApi.Diff.BusinessObjects
{
public class ChangedEnumBO : ChangedListBO<string>
{
protected override ChangedElementTypeEnum GetElementType() => ChangedElementTypeEnum.Enum;

public ChangedEnumBO(IList<string> oldValue, IList<string> newValue, DiffContextBO context) : base(oldValue, newValue, context)
{
}

public override DiffResultBO IsItemsChanged()
{
if (Context.IsRequest && Missing.IsNullOrEmpty()
|| Context.IsResponse && Increased.IsNullOrEmpty())
{
return new DiffResultBO(DiffResultEnum.Compatible);
}
return new DiffResultBO(DiffResultEnum.Incompatible);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Diff.Enums;
using Microsoft.OpenApi.Interfaces;

namespace Microsoft.OpenApi.Diff.BusinessObjects
{
public class ChangedExtensionsBO : ComposedChangedBO
{
protected override ChangedElementTypeEnum GetElementType() => ChangedElementTypeEnum.Extension;

private readonly Dictionary<string, IOpenApiExtension> _oldExtensions;
private readonly Dictionary<string, IOpenApiExtension> _newExtensions;
private readonly DiffContextBO _context;

public Dictionary<string, ChangedBO> Increased { get; set; }
public Dictionary<string, ChangedBO> Missing { get; set; }
public Dictionary<string, ChangedBO> Changed { get; set; }

public ChangedExtensionsBO(Dictionary<string, IOpenApiExtension> oldExtensions, Dictionary<string, IOpenApiExtension> newExtensions, DiffContextBO context)
{
_oldExtensions = oldExtensions;
_newExtensions = newExtensions;
_context = context;
Increased = new Dictionary<string, ChangedBO>();
Missing = new Dictionary<string, ChangedBO>();
Changed = new Dictionary<string, ChangedBO>();
}

public override List<(string Identifier, ChangedBO Change)> GetChangedElements()
{
return new List<(string Identifier, ChangedBO Change)>()
.Concat(Increased.Select(x => (x.Key, (ChangedBO)x.Value)))
.Concat(Missing.Select(x => (x.Key, (ChangedBO)x.Value)))
.Concat(Changed.Select(x => (x.Key, (ChangedBO)x.Value)))
.Where(x => x.Item2 != null).ToList();
}

public override DiffResultBO IsCoreChanged()
{
return new DiffResultBO(DiffResultEnum.NoChanges);
}

protected override List<ChangedInfoBO> GetCoreChanges()
{
return new List<ChangedInfoBO>();
}
}
}
Loading
Loading