in openapi-diff/src/modeler/AutoRest.Swagger/Model/ServiceDefinition.cs [113:382]
public override IEnumerable<ComparisonMessage> Compare(
ComparisonContext<ServiceDefinition> context,
ServiceDefinition previousDefinition
)
{
if (context.CurrentRoot != this)
{
throw new ArgumentException("context.CurrentRoot != this");
}
if (context.PreviousRoot != previousDefinition)
{
throw new ArgumentException("context.PreviousRoot != previousDefinition");
}
if (previousDefinition == null)
throw new ArgumentException("Comparing a service definition with something else.");
base.Compare(context, previousDefinition);
if (Info?.Version != null &&
previousDefinition.Info?.Version != null)
{
context.PushProperty("info");
context.PushProperty("version");
CompareVersions(context, Info.Version, previousDefinition.Info.Version);
context.Pop();
context.Pop();
}
if (context.Strict)
{
// There was no version change between the documents. This is not an error, but noteworthy.
context.LogInfo(ComparisonMessages.NoVersionChange);
}
// Check that all the protocols of the old version are supported by the new version.
context.PushProperty("schemes");
foreach (var scheme in previousDefinition.Schemes)
{
if (!Schemes.Contains(scheme))
{
context.LogBreakingChange(ComparisonMessages.ProtocolNoLongerSupported, scheme);
}
}
context.Pop();
// Check that all the request body formats that were accepted still are.
context.PushProperty("consumes");
foreach (var format in previousDefinition.Consumes)
{
if (!Consumes.Contains(format))
{
context.LogBreakingChange(ComparisonMessages.RequestBodyFormatNoLongerSupported, format);
}
}
context.Pop();
// Check that all the response body formats were also supported by the old version.
context.PushProperty("produces");
foreach (var format in Produces)
{
if (!previousDefinition.Produces.Contains(format))
{
context.LogBreakingChange(ComparisonMessages.ResponseBodyFormatNowSupported, format);
}
}
context.Pop();
// Check that no paths were removed, and compare the paths that are still there.
var newPaths = RemovePathVariables(Paths);
context.PushProperty("paths");
foreach (var path in previousDefinition.Paths.Keys)
{
var p = ObjectPath.OpenApiPathName(path);
context.PushPathProperty(path);
if (!newPaths.TryGetValue(p, out var operations))
{
// Entrie path was removeed
context.LogBreakingChange(ComparisonMessages.RemovedPath, path);
}
else
{
// 1. Remove this path from the current list to find the added paths
newPaths.Remove(p);
var copyOfOperations = operations.ToDictionary(e => e.Key, e => e.Value);
// 2. look for operation match inside this path
var previousOperations = previousDefinition.Paths[path];
foreach (var previousOperation in previousOperations)
{
if (!operations.TryGetValue(previousOperation.Key, out var newOperation))
{
// Operation was removed from the path
context.LogBreakingChange(ComparisonMessages.RemovedOperation, previousOperation.Value.OperationId);
}
else
{
copyOfOperations.Remove(previousOperation.Key);
}
}
// Look for added operations
foreach (var copyOfOperation in copyOfOperations)
{
context.PushProperty(copyOfOperation.Key);
context.LogInfo(ComparisonMessages.AddedOperation);
context.Pop();
}
// Compare operations
foreach (var operation in operations)
{
if (previousDefinition.Paths[path].TryGetValue(operation.Key, out var previousOperation))
{
context.PushProperty(operation.Key);
operation.Value.Compare(context, previousOperation);
context.Pop();
}
}
}
context.Pop();
}
// Check wether any new paths are being added
foreach (var path in newPaths.Keys)
{
context.PushPathProperty(path);
context.LogInfo(ComparisonMessages.AddedPath);
context.Pop();
}
context.Pop();
// Check for custom paths : x-ms-paths
var newCustomPaths = RemovePathVariables(CustomPaths);
context.PushProperty("x-ms-paths");
foreach (var path in previousDefinition.CustomPaths.Keys)
{
var p = ObjectPath.OpenApiPathName(path);
context.PushPathProperty(path);
Dictionary<string, Operation> operations = null;
if (!newCustomPaths.TryGetValue(p, out operations))
{
context.LogBreakingChange(ComparisonMessages.RemovedPath, path);
}
else
{
// 1. Remove this custom path from the current list to find the added paths
newCustomPaths.Remove(p);
Dictionary<string, Operation> copyOfOperations = operations.ToDictionary(e => e.Key, e => e.Value);
// 2. look for operation match inside this path
Dictionary<string, Operation> previousOperations = previousDefinition.CustomPaths[path];
foreach (var previousOperation in previousOperations)
{
Operation newOperation = null;
if (!operations.TryGetValue(previousOperation.Key, out newOperation))
{
context.LogBreakingChange(ComparisonMessages.RemovedOperation, previousOperation.Value.OperationId);
}
}
// Look for added operations
foreach (var copyOfOperation in copyOfOperations)
{
context.PushProperty(copyOfOperation.Key);
context.LogInfo(ComparisonMessages.AddedOperation);
context.Pop();
}
// Compare operations
foreach (var operation in operations)
{
Operation previousOperation = null;
if (previousDefinition.CustomPaths[path].TryGetValue(operation.Key, out previousOperation))
{
context.PushProperty(operation.Key);
operation.Value.Compare(context, previousOperation);
context.Pop();
}
}
}
context.Pop();
}
// Check wether any new paths are being added into x-ms-paths
foreach (var path in newCustomPaths.Keys)
{
context.PushPathProperty(path);
context.LogInfo(ComparisonMessages.AddedPath);
context.Pop();
}
context.Pop();
ReferenceTrackSchemas(this);
ReferenceTrackSchemas(previousDefinition);
context.PushProperty("parameters");
foreach (var def in previousDefinition.Parameters.Keys)
{
SwaggerParameter parameter = null;
if (!Parameters.TryGetValue(def, out parameter))
{
context.LogBreakingChange(ComparisonMessages.RemovedClientParameter, def);
}
else
{
context.PushProperty(def);
parameter.Compare(context, previousDefinition.Parameters[def]);
context.Pop();
}
}
context.Pop();
context.PushProperty("responses");
foreach (var def in previousDefinition.Responses.Keys)
{
OperationResponse response = null;
if (!Responses.TryGetValue(def, out response))
{
context.LogBreakingChange(ComparisonMessages.RemovedDefinition, def);
}
else
{
context.PushProperty(def);
response.Compare(context, previousDefinition.Responses[def]);
context.Pop();
}
}
context.Pop();
context.PushProperty("definitions");
foreach (var def in previousDefinition.Definitions.Keys)
{
Schema schema = null;
Schema oldSchema = previousDefinition.Definitions[def];
if (!Definitions.TryGetValue(def, out schema))
{
if (oldSchema.IsReferenced)
// It's only an error if the definition is referenced in the old service.
context.LogBreakingChange(ComparisonMessages.RemovedDefinition, def);
}
else
{
context.PushProperty(def);
schema.Compare(context, previousDefinition.Definitions[def]);
context.Pop();
}
}
context.Pop();
context.Pop();
return context.Messages;
}