Ejemplo n.º 1
0
        /// <summary>
        /// Compare a modified document node (this) to a previous one and look for breaking as well as non-breaking changes.
        /// </summary>
        /// <param name="context">The modified document context.</param>
        /// <param name="previous">The original document model.</param>
        /// <returns>A list of messages from the comparison.</returns>
        public override IEnumerable<ComparisonMessage> Compare(ComparisonContext context, SwaggerBase previous)
        {
            var priorResponse = previous as OperationResponse;

            if (priorResponse == null)
            {
                throw new ArgumentNullException("previous");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            context.Direction = DataDirection.Response;

            base.Compare(context, previous);

            var headers = Headers != null ? Headers : new Dictionary<string, Header>();
            var priorHeaders = priorResponse.Headers != null ? priorResponse.Headers : new Dictionary<string, Header>();

            foreach (var header in headers)
            {
                context.Push(header.Key);

                Header oldHeader = null;
                if (!priorHeaders.TryGetValue(header.Key, out oldHeader))
                {
                    context.LogInfo(ComparisonMessages.AddingHeader, header.Key);
                }
                else
                {
                    header.Value.Compare(context, oldHeader);
                }

                context.Pop();
            }

            foreach (var header in priorHeaders)
            {
                context.Push(header.Key);

                Header newHeader = null;
                if (!headers.TryGetValue(header.Key, out newHeader))
                {
                    context.LogBreakingChange(ComparisonMessages.RemovingHeader, header.Key);
                }

                context.Pop();
            }

            if (Schema != null && priorResponse.Schema != null)
            {
                Schema.Compare(context, priorResponse.Schema);
            }

            context.Direction = DataDirection.None;

            return context.Messages;
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Compare a modified document node (this) to a previous one and look for breaking as well as non-breaking changes.
        /// </summary>
        /// <param name="context">The modified document context.</param>
        /// <param name="previous">The original document model.</param>
        /// <returns>A list of messages from the comparison.</returns>
        public virtual IEnumerable<ComparisonMessage> Compare(ComparisonContext context, SwaggerBase previous)
        {
            if (previous == null)
                throw new ArgumentNullException("previous");
            if (context == null)
                throw new ArgumentNullException("context");

            yield break;
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Compare a modified document node (this) to a previous one and look for breaking as well as non-breaking changes.
        /// </summary>
        /// <param name="context">The modified document context.</param>
        /// <param name="previous">The original document model.</param>
        /// <returns>A list of messages from the comparison.</returns>
        public override IEnumerable<ComparisonMessage> Compare(ComparisonContext context, SwaggerBase previous)
        {
            var priorParameter = previous as SwaggerParameter;

            if (priorParameter == null)
            {
                throw new ArgumentNullException("priorVersion");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            context.Direction = DataDirection.Request;

            base.Compare(context, previous);

            if (In != priorParameter.In)
            {
                context.LogBreakingChange(ComparisonMessages.ParameterInHasChanged, priorParameter.In.ToString().ToLower(CultureInfo.CurrentCulture), In.ToString().ToLower(CultureInfo.CurrentCulture));
            }

            if (this.IsConstant != priorParameter.IsConstant)
            {
                context.LogBreakingChange(ComparisonMessages.ConstantStatusHasChanged);
            }

            if (Reference != null && !Reference.Equals(priorParameter.Reference))
            {
                context.LogBreakingChange(ComparisonMessages.ReferenceRedirection);
            }

            if (Schema != null && priorParameter.Schema != null)
            {
                Schema.Compare(context, priorParameter.Schema);
            }

            context.Direction = DataDirection.None;

            return context.Messages;
        }
Ejemplo n.º 4
0
        private void CompareProperties(ComparisonContext context, SwaggerObject prior)
        {
            // Additional properties

            if (prior.AdditionalProperties == null && AdditionalProperties != null)
            {
                context.LogBreakingChange(ComparisonMessages.AddedAdditionalProperties);
            }
            else if (prior.AdditionalProperties != null && AdditionalProperties == null)
            {
                context.LogBreakingChange(ComparisonMessages.RemovedAdditionalProperties);
            }
            else if (AdditionalProperties != null)
            {
                context.Push("additionalProperties");
                AdditionalProperties.Compare(context, prior.AdditionalProperties);
                context.Pop();
            }
        }
Ejemplo n.º 5
0
        private void CompareEnums(ComparisonContext context, SwaggerObject prior)
        {
            if (prior.Enum == null && this.Enum == null) return;

            bool relaxes = (prior.Enum != null && this.Enum == null);
            bool constrains = (prior.Enum == null && this.Enum != null);

            if (!relaxes && !constrains)
            {
                // 1. Look for removed elements (constraining).

                constrains = prior.Enum.Any(str => !this.Enum.Contains(str));

                // 2. Look for added elements (relaxing).

                relaxes = this.Enum.Any(str => !prior.Enum.Contains(str));
            }

            if (context.Direction == DataDirection.Request)
            {
                if (constrains)
                {
                    context.LogBreakingChange(relaxes ? ComparisonMessages.ConstraintChanged : ComparisonMessages.ConstraintIsStronger, "enum");
                    return;
                }
            }
            else if (context.Direction == DataDirection.Response)
            {
                if (relaxes)
                {
                    context.LogBreakingChange(constrains ? ComparisonMessages.ConstraintChanged : ComparisonMessages.ConstraintIsWeaker, "enum");
                    return;
                }
            }

            if (relaxes && constrains)
                context.LogInfo(ComparisonMessages.ConstraintChanged, "enum");
            else if (relaxes || constrains)
                context.LogInfo(relaxes ? ComparisonMessages.ConstraintIsWeaker : ComparisonMessages.ConstraintIsStronger, "enum");
        }
Ejemplo n.º 6
0
        protected void CompareItems(ComparisonContext context, SwaggerObject prior)
        {
            if (prior == null)
            {
                throw new ArgumentNullException("prior");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if (prior.Items != null && Items != null)
            {
                context.Push("items");
                Items.Compare(context, prior.Items);
                context.Pop();
            }
        }
Ejemplo n.º 7
0
        protected void CompareFormats(ComparisonContext context, SwaggerObject prior)
        {
            if (prior == null)
            {
                throw new ArgumentNullException("prior");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if (prior.Format == null && Format != null ||
                prior.Format != null && Format == null ||
                prior.Format != null && Format != null && !prior.Format.Equals(Format))
            {
                context.LogBreakingChange(ComparisonMessages.TypeFormatChanged);
            }
        }
Ejemplo n.º 8
0
        protected void CompareConstraints(ComparisonContext context, SwaggerObject prior)
        {
            if (prior == null)
            {
                throw new ArgumentNullException("prior");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if ((prior.MultipleOf == null && MultipleOf != null) ||
                (prior.MultipleOf != null && !prior.MultipleOf.Equals(MultipleOf)))
            {
                context.LogBreakingChange(ComparisonMessages.ConstraintChanged, "multipleOf");
            }
            if ((prior.Maximum == null && Maximum != null) ||
                (prior.Maximum != null && !prior.Maximum.Equals(Maximum)) ||
                prior.ExclusiveMaximum != ExclusiveMaximum)
            {
                // Flag stricter constraints for requests and relaxed constraints for responses.
                if (prior.ExclusiveMaximum != ExclusiveMaximum || context.Direction == DataDirection.None)
                    context.LogBreakingChange(ComparisonMessages.ConstraintChanged, "maximum");
                else if (context.Direction == DataDirection.Request && Narrows(prior.Maximum, Maximum, true))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsStronger, "maximum");
                else if (context.Direction == DataDirection.Response && Widens(prior.Maximum, Maximum, true))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsWeaker, "maximum");
                else if (Narrows(prior.Maximum, Maximum, false))
                    context.LogInfo(ComparisonMessages.ConstraintIsStronger, "maximum");
                else if (Widens(prior.Maximum, Maximum, false))
                    context.LogInfo(ComparisonMessages.ConstraintIsWeaker, "maximum");
            }
            if ((prior.Minimum == null && Minimum != null) ||
                (prior.Minimum != null && !prior.Minimum.Equals(Minimum)) ||
                prior.ExclusiveMinimum != ExclusiveMinimum)
            {
                // Flag stricter constraints for requests and relaxed constraints for responses.
                if (prior.ExclusiveMinimum != ExclusiveMinimum || context.Direction == DataDirection.None)
                    context.LogBreakingChange(ComparisonMessages.ConstraintChanged, "minimum");
                else if (context.Direction == DataDirection.Request && Narrows(prior.Minimum, Minimum, true))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsStronger, "minimum");
                else if (context.Direction == DataDirection.Response && Widens(prior.Minimum, Minimum, true))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsWeaker, "minimum");
                else if (Narrows(prior.Minimum, Minimum, false))
                    context.LogInfo(ComparisonMessages.ConstraintIsStronger, "minimum");
                else if (Widens(prior.Minimum, Minimum, false))
                    context.LogInfo(ComparisonMessages.ConstraintIsWeaker, "minimum");
            }
            if ((prior.MaxLength == null && MaxLength != null) ||
                (prior.MaxLength != null && !prior.MaxLength.Equals(MaxLength)))
            {
                // Flag stricter constraints for requests and relaxed constraints for responses.
                if (context.Direction == DataDirection.None)
                    context.LogBreakingChange(ComparisonMessages.ConstraintChanged, "maxLength");
                else if (context.Direction == DataDirection.Request && Narrows(prior.MaxLength, MaxLength, false))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsStronger, "maxLength");
                else if (context.Direction == DataDirection.Response && Widens(prior.MaxLength, MaxLength, false))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsWeaker, "maxLength");
                else if (Narrows(prior.MaxLength, MaxLength, false))
                    context.LogInfo(ComparisonMessages.ConstraintIsStronger, "maxLength");
                else if (Widens(prior.MaxLength, MaxLength, false))
                    context.LogInfo(ComparisonMessages.ConstraintIsWeaker, "maxLength");
            }
            if ((prior.MinLength == null && MinLength != null) ||
                (prior.MinLength != null && !prior.MinLength.Equals(MinLength)))
            {
                // Flag stricter constraints for requests and relaxed constraints for responses.
                if (context.Direction == DataDirection.None)
                    context.LogBreakingChange(ComparisonMessages.ConstraintChanged, "minLength");
                else if (context.Direction == DataDirection.Request && Narrows(prior.MinLength, MinLength, true))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsStronger, "minimum");
                else if (context.Direction == DataDirection.Response && Widens(prior.MinLength, MinLength, true))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsWeaker, "minimum");
                else if (Narrows(prior.MinLength, MinLength, false))
                    context.LogInfo(ComparisonMessages.ConstraintIsStronger, "minLength");
                else if (Widens(prior.MinLength, MinLength, false))
                    context.LogInfo(ComparisonMessages.ConstraintIsWeaker, "minLength");
            }
            if ((prior.Pattern == null && Pattern != null) ||
                (prior.Pattern != null && !prior.Pattern.Equals(Pattern)))
            {
                context.LogBreakingChange(ComparisonMessages.ConstraintChanged, "pattern");
            }
            if ((prior.MaxItems == null && MaxItems != null) ||
                (prior.MaxItems != null && !prior.MaxItems.Equals(MaxItems)))
            {
                // Flag stricter constraints for requests and relaxed constraints for responses.
                if (context.Direction == DataDirection.None)
                    context.LogBreakingChange(ComparisonMessages.ConstraintChanged, "maxItems");
                else if (context.Direction == DataDirection.Request && Narrows(prior.MaxItems, MaxItems, false))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsStronger, "maxItems");
                else if (context.Direction == DataDirection.Response && Widens(prior.MaxItems, MaxItems, false))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsWeaker, "maxItems");
                else if (Narrows(prior.MaxItems, MaxItems, false))
                    context.LogInfo(ComparisonMessages.ConstraintIsStronger, "maxItems");
                else if (Widens(prior.MaxItems, MaxItems, false))
                    context.LogInfo(ComparisonMessages.ConstraintIsWeaker, "maxItems");
            }
            if ((prior.MinItems == null && MinItems != null) ||
                (prior.MinItems != null && !prior.MinItems.Equals(MinItems)))
            {
                // Flag stricter constraints for requests and relaxed constraints for responses.
                if (context.Direction == DataDirection.None)
                    context.LogBreakingChange(ComparisonMessages.ConstraintChanged, "minItems");
                else if (context.Direction == DataDirection.Request && Narrows(prior.MinItems, MinItems, true))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsStronger, "minItems");
                else if (context.Direction == DataDirection.Response && Widens(prior.MinItems, MinItems, true))
                    context.LogBreakingChange(ComparisonMessages.ConstraintIsWeaker, "minItems");
                else if (Narrows(prior.MinItems, MinItems, true))
                    context.LogInfo(ComparisonMessages.ConstraintIsStronger, "minItems");
                else if (Widens(prior.MinItems, MinItems, true))
                    context.LogInfo(ComparisonMessages.ConstraintIsWeaker, "minItems");
            }
            if (prior.UniqueItems != UniqueItems)
            {
                context.LogBreakingChange(ComparisonMessages.ConstraintChanged, "uniqueItems");
            }
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Compare a modified document node (this) to a previous one and look for breaking as well as non-breaking changes.
        /// </summary>
        /// <param name="context">The modified document context.</param>
        /// <param name="previous">The original document model.</param>
        /// <returns>A list of messages from the comparison.</returns>
        public override IEnumerable<ComparisonMessage> Compare(ComparisonContext context, SwaggerBase previous)
        {
            var priorSchema = previous as Schema;

            if (priorSchema == null)
            {
                throw new ArgumentNullException("priorVersion");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int referenced = 0;

            var thisSchema = this;

            if (!string.IsNullOrWhiteSpace(thisSchema.Reference))
            {
                thisSchema = FindReferencedSchema(thisSchema.Reference, (context.CurrentRoot as ServiceDefinition).Definitions);
                referenced += 1;
            }
            if (!string.IsNullOrWhiteSpace(priorSchema.Reference))
            {
                priorSchema = FindReferencedSchema(priorSchema.Reference, (context.PreviousRoot as ServiceDefinition).Definitions);
                referenced += 1;
            }

            // Avoid doing the comparison repeatedly by marking for which direction it's already been done.

            if (context.Direction != DataDirection.None && referenced == 2)
            {
                // Comparing two referenced schemas in the context of a parameter or response -- did we already do this?

                if (thisSchema._compareDirection == context.Direction || thisSchema._compareDirection == DataDirection.Both)
                {
                    return new ComparisonMessage[0];
                }
                _compareDirection |= context.Direction;
            }

            if (thisSchema != this || priorSchema != previous)
            {
                return thisSchema.Compare(context, priorSchema);
            }

            base.Compare(context, previous);

            if (priorSchema.ReadOnly != ReadOnly)
            {
                context.LogBreakingChange(ComparisonMessages.ReadonlyPropertyChanged2, priorSchema.ReadOnly.ToString().ToLower(CultureInfo.CurrentCulture), ReadOnly.ToString().ToLower(CultureInfo.CurrentCulture));
            }

            if ((priorSchema.Discriminator == null && Discriminator != null) ||
                (priorSchema.Discriminator != null && !priorSchema.Discriminator.Equals(Discriminator)))
            {
                context.LogBreakingChange(ComparisonMessages.DifferentDiscriminator);
            }

            if ((priorSchema.Extends == null && Extends != null) ||
                (priorSchema.Extends != null && !priorSchema.Extends.Equals(Extends)))
            {
                context.LogBreakingChange(ComparisonMessages.DifferentExtends);
            }

            if ((priorSchema.AllOf == null && AllOf != null) ||
                (priorSchema.AllOf != null && AllOf == null))
            {
                context.LogBreakingChange(ComparisonMessages.DifferentAllOf);
            }
            else if (priorSchema.AllOf != null)
            {
                CompareAllOfs(context, priorSchema);
            }

            context.Push("properties");
            CompareProperties(context, priorSchema);
            context.Pop();

            return context.Messages;
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Since some services may rely on semantic versioning, comparing versions is fairly complex.
        /// </summary>
        /// <param name="context">A comparison context.</param>
        /// <param name="newVer">The new version string.</param>
        /// <param name="oldVer">The old version string</param>
        /// <remarks>
        /// In semantic versioning schemes, only the major and minor version numbers are considered when comparing versions.
        /// Build numbers are ignored.
        /// </remarks>
        private void CompareVersions(ComparisonContext context, string newVer, string oldVer)
        {
            var oldVersion = oldVer.Split('.');
            var newVersion = newVer.Split('.');

            // If the version consists only of numbers separated by '.', we'll consider it semantic versioning.

            if (!context.Strict && oldVersion.Length > 0 && newVersion.Length > 0)
            {
                bool versionChanged = false;

                // Situation 1: The versioning scheme is semantic, i.e. it uses a major.minr.build-number scheme, where each component is an integer.
                //              In this case, we care about the major/minor numbers, but not the build number. In othe words, ifall that is different
                //              is the build number, it will not be treated as a version change.

                int oldMajor = 0, newMajor = 0;
                bool integers = int.TryParse(oldVersion[0], out oldMajor) && int.TryParse(newVersion[0], out newMajor);

                if (integers && oldMajor != newMajor)
                {
                    versionChanged = true;

                    if (oldMajor > newMajor)
                    {
                        context.LogError(ComparisonMessages.VersionsReversed, oldVer, newVer);
                    }
                }

                if (!versionChanged && integers && oldVersion.Length > 1 && newVersion.Length > 1)
                {
                    int oldMinor = 0, newMinor = 0;
                    integers = int.TryParse(oldVersion[1], out oldMinor) && int.TryParse(newVersion[1], out newMinor);

                    if (integers && oldMinor != newMinor)
                    {
                        versionChanged = true;

                        if (oldMinor > newMinor)
                        {
                            context.LogError(ComparisonMessages.VersionsReversed, oldVer, newVer);
                        }
                    }
                }

                // Situation 2: The versioning scheme is something else, maybe a date or just a label?
                //              Regardless of what it is, we just check whether the two strings are equal or not.

                if (!versionChanged && !integers)
                {
                    versionChanged = !oldVer.ToLower(CultureInfo.CurrentCulture).Equals(newVer.ToLower(CultureInfo.CurrentCulture));
                }

                context.Strict = !versionChanged;
            }
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Compare a modified document node (this) to a previous one and look for breaking as well as non-breaking changes.
        /// </summary>
        /// <param name="context">The modified document context.</param>
        /// <param name="previous">The original document model.</param>
        /// <returns>A list of messages from the comparison.</returns>
        public override IEnumerable<ComparisonMessage> Compare(ComparisonContext context, SwaggerBase previous)
        {
            if (previous == null)
                throw new ArgumentNullException("previous");

            context.CurrentRoot = this;
            context.PreviousRoot = previous;

            context.Push("#");

            base.Compare(context, previous);

            var previousDefinition = previous as ServiceDefinition;

            if (previousDefinition == null)
                throw new ArgumentException("Comparing a service definition with something else.");

            if (Info != null && previousDefinition.Info != null)
            {
                context.Push("info/version");

               CompareVersions(context, Info.Version, previousDefinition.Info.Version);

                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.Push("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.Push("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.Push("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.Push("paths");
            foreach (var path in previousDefinition.Paths.Keys)
            {
                var p = Regex.Replace(path, @"\{\w*\}", @"{}");

                context.Push(path);

                Dictionary<string, Operation> operations = null;
                if (!newPaths.TryGetValue(p, out operations))
                {
                    context.LogBreakingChange(ComparisonMessages.RemovedPath, path);
                }
                else
                {
                    Dictionary<string, Operation> previousOperations = previousDefinition.Paths[path];
                    foreach (var previousOperation in previousOperations)
                    {
                        Operation newOperation = null;
                        if (!operations.TryGetValue(previousOperation.Key, out newOperation))
                        {
                            context.LogBreakingChange(ComparisonMessages.RemovedOperation, previousOperation.Value.OperationId);
                        }
                    }

                    foreach (var operation in operations)
                    {
                        Operation previousOperation = null;
                        if (previousDefinition.Paths[path].TryGetValue(operation.Key, out previousOperation))
                        {
                            context.Push(operation.Key);
                            operation.Value.Compare(context, previousOperation);
                            context.Pop();
                        }
                    }
                }
                context.Pop();
            }
            context.Pop();

            newPaths = RemovePathVariables(CustomPaths);

            context.Push("x-ms-paths");
            foreach (var path in previousDefinition.CustomPaths.Keys)
            {
                var p = Regex.Replace(path, @"\{\w*\}", @"{}");

                context.Push(path);

                Dictionary<string, Operation> operations = null;
                if (!newPaths.TryGetValue(p, out operations))
                {
                    context.LogBreakingChange(ComparisonMessages.RemovedPath, path);
                }
                else
                {
                    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);
                        }
                    }

                    foreach (var operation in operations)
                    {
                        Operation previousOperation = null;
                        if (previousDefinition.CustomPaths[path].TryGetValue(operation.Key, out previousOperation))
                        {
                            context.Push(operation.Key);
                            operation.Value.Compare(context, previousOperation);
                            context.Pop();
                        }
                    }
                }
                context.Pop();
            }
            context.Pop();

            ReferenceTrackSchemas(this);
            ReferenceTrackSchemas(previousDefinition);

            context.Push("parameters");
            foreach (var def in previousDefinition.Parameters.Keys)
            {
                SwaggerParameter parameter = null;
                if (!Parameters.TryGetValue(def, out parameter))
                {
                    context.LogBreakingChange(ComparisonMessages.RemovedClientParameter, def);
                }
                else
                {
                    context.Push(def);
                    parameter.Compare(context, previousDefinition.Parameters[def]);
                    context.Pop();
                }
            }
            context.Pop();

            context.Push("responses");
            foreach (var def in previousDefinition.Responses.Keys)
            {
                OperationResponse response = null;
                if (!Responses.TryGetValue(def, out response))
                {
                    context.LogBreakingChange(ComparisonMessages.RemovedDefinition, def);
                }
                else
                {
                    context.Push(def);
                    response.Compare(context, previousDefinition.Responses[def]);
                    context.Pop();
                }
            }
            context.Pop();

            context.Push("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 if (schema.IsReferenced && oldSchema.IsReferenced)
                {
                    context.Push(def);
                    schema.Compare(context, previousDefinition.Definitions[def]);
                    context.Pop();
                }
            }
            context.Pop();

            context.Pop();

            return context.Messages;
        }
Ejemplo n.º 12
0
        private void CheckParameters(ComparisonContext context, Operation priorOperation)
        {
            // Check that no parameters were removed or reordered, and compare them if it's not the case.

            var currentRoot = (context.CurrentRoot as ServiceDefinition);
            var previousRoot = (context.PreviousRoot as ServiceDefinition);

            foreach (var oldParam in priorOperation.Parameters
                .Select(p => string.IsNullOrEmpty(p.Reference) ? p : FindReferencedParameter(p.Reference, previousRoot.Parameters)))
            {
                SwaggerParameter newParam = FindParameter(oldParam.Name, Parameters, currentRoot.Parameters);

                context.Push(oldParam.Name);

                if (newParam != null)
                {
                    newParam.Compare(context, oldParam);
                }
                else if (oldParam.IsRequired)
                {
                    context.LogBreakingChange(ComparisonMessages.RemovedRequiredParameter, oldParam.Name);
                }

                context.Pop();
            }

            // Check that no required parameters were added.

            foreach (var newParam in Parameters
                .Select(p => string.IsNullOrEmpty(p.Reference) ? p : FindReferencedParameter(p.Reference, currentRoot.Parameters))
                .Where(p => p != null && p.IsRequired))
            {
                if (newParam == null) continue;

                SwaggerParameter oldParam = FindParameter(newParam.Name, priorOperation.Parameters, previousRoot.Parameters);

                if (oldParam == null)
                {
                    context.Push(newParam.Name);
                    context.LogBreakingChange(ComparisonMessages.AddingRequiredParameter, newParam.Name);
                    context.Pop();
                }
            }
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Compare a modified document node (this) to a previous one and look for breaking as well as non-breaking changes.
        /// </summary>
        /// <param name="context">The modified document context.</param>
        /// <param name="previous">The original document model.</param>
        /// <returns>A list of messages from the comparison.</returns>
        public override IEnumerable<ComparisonMessage> Compare(ComparisonContext context, SwaggerBase previous)
        {
            var priorOperation = previous as Operation;

            var currentRoot = (context.CurrentRoot as ServiceDefinition);
            var previousRoot = (context.PreviousRoot as ServiceDefinition);

            if (priorOperation == null)
            {
                throw new ArgumentException("previous");
            }

            base.Compare(context, previous);

            if (!OperationId.Equals(priorOperation.OperationId))
            {
                context.LogBreakingChange(ComparisonMessages.ModifiedOperationId);
            }

            CheckParameters(context, priorOperation);

            if (Responses != null && priorOperation.Responses != null)
            {
                foreach (var response in Responses)
                {
                    var oldResponse = priorOperation.FindResponse(response.Key, priorOperation.Responses);

                    context.Push(response.Key);

                    if (oldResponse == null)
                    {
                        context.LogBreakingChange(ComparisonMessages.AddingResponseCode, response.Key);
                    }
                    else
                    {
                        response.Value.Compare(context, oldResponse);
                    }

                    context.Pop();
                }

                foreach (var response in priorOperation.Responses)
                {
                    var newResponse = this.FindResponse(response.Key, this.Responses);

                    if (newResponse == null)
                    {
                        context.Push(response.Key);
                        context.LogBreakingChange(ComparisonMessages.RemovedResponseCode, response.Key);
                        context.Pop();
                    }
                }
            }

            return context.Messages;
        }
Ejemplo n.º 14
0
        private void CompareProperties(ComparisonContext context, Schema priorSchema)
        {
            // Were any properties removed?

            if (priorSchema.Properties != null)
            {
                foreach (var def in priorSchema.Properties)
                {
                    Schema model = null;
                    if (Properties == null || !Properties.TryGetValue(def.Key, out model))
                    {
                        context.LogBreakingChange(ComparisonMessages.RemovedProperty1, def.Key);
                    }
                    else
                    {
                        context.Push(def.Key);
                        model.Compare(context, def.Value);
                        context.Pop();
                    }
                }
            }

            // Were any required properties added?

            if (Properties != null)
            {
                foreach (var def in Properties.Keys)
                {
                    Schema model = null;
                    if (priorSchema.Properties == null || !priorSchema.Properties.TryGetValue(def, out model) && Required.Contains(def))
                    {
                        context.LogBreakingChange(ComparisonMessages.AddedRequiredProperty1, def);
                    }
                }
            }
        }
Ejemplo n.º 15
0
        private void CompareAllOfs(ComparisonContext context, Schema priorSchema)
        {
            var different = 0;
            foreach (var schema in priorSchema.AllOf)
            {
                if (!AllOf.Select(s => s.Reference).ToArray().Contains(schema.Reference))
                {
                    different += 1;
                }
            }
            foreach (var schema in AllOf)
            {
                if (!priorSchema.AllOf.Select(s => s.Reference).ToArray().Contains(schema.Reference))
                {
                    different += 1;
                }
            }

            if (different > 0)
            {
                context.LogBreakingChange(ComparisonMessages.DifferentAllOf);
            }
        }
Ejemplo n.º 16
0
        public override IEnumerable<ComparisonMessage> Compare()
        {
            Logger.LogInfo(Resources.ParsingSwagger);
            if (string.IsNullOrWhiteSpace(Settings.Input) || string.IsNullOrWhiteSpace(Settings.Previous))
            {
                throw ErrorManager.CreateError(Resources.InputRequired);
            }

            var oldDefintion = SwaggerParser.Load(Settings.Previous, Settings.FileSystem);
            var newDefintion = SwaggerParser.Load(Settings.Input, Settings.FileSystem);

            var context = new ComparisonContext(oldDefintion, newDefintion);

            // Look for semantic errors and warnings in the new document.
            var validator = new RecursiveObjectValidator(PropertyNameResolver.JsonName);
            var validationMessages = validator.GetValidationExceptions(newDefintion).ToList();

            // Only compare versions if the new version is correct.
            var comparisonMessages =
                !validationMessages.Any(m => m.Severity > LogEntrySeverity.Error) ?
                newDefintion.Compare(context, oldDefintion) :
                Enumerable.Empty<ComparisonMessage>();

            return validationMessages
                .Select(msg =>
                    new ComparisonMessage(new MessageTemplate { Id = 0, Message = msg.Message }, string.Join("/", msg.Path), msg.Severity))
                .Concat(comparisonMessages);
        }
Ejemplo n.º 17
0
        /// <summary>
        /// Compare a modified document node (this) to a previous one and look for breaking as well as non-breaking changes.
        /// </summary>
        /// <param name="context">The modified document context.</param>
        /// <param name="previous">The original document model.</param>
        /// <returns>A list of messages from the comparison.</returns>
        public override IEnumerable<ComparisonMessage> Compare(ComparisonContext context, SwaggerBase previous)
        {
            var prior = previous as SwaggerObject;

            if (prior == null)
            {
                throw new ArgumentNullException("priorVersion");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            base.Compare(context, previous);

            if (Reference != null && !Reference.Equals(prior.Reference))
            {
                context.LogBreakingChange(ComparisonMessages.ReferenceRedirection);
            }

            if (IsRequired != prior.IsRequired)
            {
                if (context.Direction != DataDirection.Response)
                {
                    if (IsRequired && !prior.IsRequired)
                    {
                        context.LogBreakingChange(ComparisonMessages.RequiredStatusChange);
                    }
                    else
                    {
                        context.LogInfo(ComparisonMessages.RequiredStatusChange);
                    }
                }
            }

            // Are the types the same?

            if (prior.Type.HasValue != Type.HasValue || (Type.HasValue && prior.Type.Value != Type.Value))
            {
                context.LogBreakingChange(ComparisonMessages.TypeChanged);
            }

            // What about the formats?

            CompareFormats(context, prior);

            CompareItems(context, prior);

            if (Default != null && !Default.Equals(prior.Default) || (Default == null && !string.IsNullOrEmpty(prior.Default)))
            {
                context.LogBreakingChange(ComparisonMessages.DefaultValueChanged);
            }

            if (Type.HasValue && Type.Value == DataType.Array && prior.CollectionFormat != CollectionFormat)
            {
                context.LogBreakingChange(ComparisonMessages.ArrayCollectionFormatChanged);
            }

            CompareConstraints(context, prior);

            CompareProperties(context, prior);

            CompareEnums(context, prior);

            return context.Messages;
        }