コード例 #1
0
        /// <summary>
        /// Check whether there exist lenient mandatory equal in the cmdlet
        /// Lenient mandatory equal means for two parameter set, one parameter set's mandatory parameters can
        /// be found in another parameter set whether as mandatory or optional.
        /// If all these two parameter set are not defualt, it may cause confusion.
        /// </summary>
        public void ValidateParameterSetWithLenientMandatoryEqual(CmdletMetadata cmdlet, ReportLogger <SignatureIssue> issueLogger)
        {
            var defaultParameterSet = cmdlet.DefaultParameterSet;

            foreach (var parameterSet1 in cmdlet.ParameterSets)
            {
                foreach (var parameterSet2 in cmdlet.ParameterSets)
                {
                    if (!parameterSet1.Equals(parameterSet2) &&
                        cmdlet.DefaultParameterSetName != parameterSet1.Name &&
                        cmdlet.DefaultParameterSetName != parameterSet2.Name &&
                        parameterSet1.Name.CompareTo(parameterSet2.Name) > 0)
                    {
                        if (parameterSet1.AllMandatoryParemeterLenientEquals(parameterSet2) &&
                            !IsParameterSetIntersectionCoveredByDefault(parameterSet1, parameterSet2, defaultParameterSet))
                        {
                            issueLogger.LogSignatureIssue(
                                cmdlet: cmdlet,
                                severity: 1,
                                problemId: SignatureProblemId.ParameterSetWithLenientMandatoryEqual,
                                description:
                                string.Format(
                                    "Parameter set '{0}' and '{1}' of cmdlet '{2}', for all mandatory parameters in {0} " +
                                    "we can find mandatory and optional parameter in {1}, and all mandatory parameter in " +
                                    "{1} can find the corresponding mandatory parameter in {0}, " +
                                    "and both of them are not default parameter set which may cause confusion.",
                                    parameterSet1.Name, parameterSet2.Name, cmdlet.Name),
                                remediation: "Merge these parameter sets into one parameter set.");
                        }
                    }
                }
            }
        }
コード例 #2
0
        /// <summary>
        /// Checks if the type of the output is an array or a generic, and makes sure there are no breaking changes.
        /// If the type is not an array or a generic, it proceeds with the normal type checking with CompareTypeMetadata.
        /// </summary>
        /// <param name="cmdlet">The cmdlet whose output metadata is being checked for breaking changes.</param>
        /// <param name="oldTypeMetadata">The type metadata from the old (serialized) assembly.</param>
        /// <param name="newTypeMetadata">The type metadata from the new assembly.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        public void CheckOutputType(
            CmdletMetadata cmdlet,
            TypeMetadata oldTypeMetadata,
            TypeMetadata newTypeMetadata,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            int    problemId   = ProblemIds.BreakingChangeProblemId.ChangedOutputElementType;
            string description = string.Format(Properties.Resources.ChangedOutputElementTypeDescription,
                                               oldTypeMetadata.ElementType, newTypeMetadata.ElementType);
            string remediation = string.Format(Properties.Resources.ChangedOutputElementTypeRemediation,
                                               oldTypeMetadata.ElementType);

            // If the type is an array, check for any breaking changes
            if (IsElementType(cmdlet, oldTypeMetadata, newTypeMetadata, problemId, description, remediation, issueLogger))
            {
                return;
            }

            string target = "output type";

            if (IsGenericType(cmdlet, oldTypeMetadata, newTypeMetadata, target, issueLogger))
            {
                return;
            }

            CompareTypeMetadata(cmdlet, oldTypeMetadata, newTypeMetadata, issueLogger);
        }
コード例 #3
0
        /// <summary>
        /// Checks if an alias to a cmdlet has been removed.
        /// </summary>
        /// <param name="oldCmdlet">The cmdlet metadata from the old (serialized) assembly.</param>
        /// <param name="newCmdlet">The cmdlet metadata from the new assembly.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        private void CheckForRemovedCmdletAlias(
            CmdletMetadata oldCmdlet,
            CmdletMetadata newCmdlet,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            // This set will contain all of the aliases in the new metadata
            HashSet <string> aliasSet = new HashSet <string>();

            // Add each alias to the set
            foreach (var newAlias in newCmdlet.AliasList)
            {
                aliasSet.Add(newAlias);
            }

            // For each alias in the old metadata, check to see
            // if it exists in the new metadata
            foreach (var oldAlias in oldCmdlet.AliasList)
            {
                // If the alias cannot be found, log an issue
                if (!aliasSet.Contains(oldAlias))
                {
                    issueLogger.LogBreakingChangeIssue(
                        cmdlet: oldCmdlet,
                        severity: 0,
                        problemId: ProblemIds.BreakingChangeProblemId.RemovedCmdletAlias,
                        description: string.Format(Properties.Resources.RemovedCmdletAliasDescription,
                                                   oldCmdlet.Name, oldAlias),
                        remediation: string.Format(Properties.Resources.RemovedCmdletAliasRemediation,
                                                   oldAlias, oldCmdlet.Name));
                }
            }
        }
コード例 #4
0
        /// <summary>
        /// Check if the argument size for the generics are the same. If they are not, log an issue.
        /// </summary>
        /// <param name="cmdlet">The cmdlet metadata currently being checked.</param>
        /// <param name="oldTypeMetadata">The type metadata from the old (serialized) assembly.</param>
        /// <param name="newTypeMetadata">The type metadata from the new assembly.</param>
        /// <param name="target">Target of the generic type breaking change.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        /// <returns></returns>
        private bool HasSameGenericArgumentSize(
            CmdletMetadata cmdlet,
            TypeMetadata oldTypeMetadata,
            TypeMetadata newTypeMetadata,
            string target,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            // If the arguments are the same size, return true
            if (oldTypeMetadata.GenericTypeArguments.Count == newTypeMetadata.GenericTypeArguments.Count)
            {
                return(true);
            }

            // Otherwise, log an issue and return false
            issueLogger?.LogBreakingChangeIssue(
                cmdlet: cmdlet,
                severity: 0,
                problemId: ProblemIds.BreakingChangeProblemId.DifferentGenericTypeArgumentSize,
                description: string.Format(Properties.Resources.DifferentGenericTypeArgumentSizeDescription,
                                           oldTypeMetadata.Name, target, oldTypeMetadata.GenericTypeArguments.Count,
                                           newTypeMetadata.GenericTypeArguments.Count),
                remediation: string.Format(Properties.Resources.DifferentGenericTypeArgumentSizeRemediation,
                                           oldTypeMetadata.Name, oldTypeMetadata.GenericTypeArguments.Count));

            return(false);
        }
コード例 #5
0
        /// <summary>
        /// Check for breaking changes in the public API of types.
        /// </summary>
        /// <param name="oldTypeMetadataDictionary">Dictionary of type names mapped to metadata from the old module.</param>
        /// <param name="newTypeMetadataDictionary">Dictionary of type names mapped to metadata from the new module.</param>
        /// <param name="issueLogger">Temporary logger used to track the breaking changes found.</param>
        private void CheckBreakingChangesInTypes(
            Dictionary <string, TypeMetadata> oldTypeMetadataDictionary,
            Dictionary <string, TypeMetadata> newTypeMetadataDictionary,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            var typeMetadataHelper = new TypeMetadataHelper(oldTypeMetadataDictionary, newTypeMetadataDictionary);

            foreach (var type in oldTypeMetadataDictionary.Keys)
            {
                var cmdletMetadata = new CmdletMetadata()
                {
                    NounName = "Common",
                    VerbName = type
                };

                if (!newTypeMetadataDictionary.ContainsKey(type))
                {
                    issueLogger?.LogBreakingChangeIssue(
                        cmdlet: cmdletMetadata,
                        severity: 0,
                        problemId: 0,
                        description: string.Empty,
                        remediation: string.Empty);
                    continue;
                }

                var oldTypeMetadata = oldTypeMetadataDictionary[type];
                var newTypeMetadata = newTypeMetadataDictionary[type];
                typeMetadataHelper.CompareTypeMetadata(cmdletMetadata, oldTypeMetadata, newTypeMetadata, issueLogger);
                typeMetadataHelper.CompareMethodSignatures(cmdletMetadata, oldTypeMetadata.Methods, newTypeMetadata.Methods, issueLogger);
                typeMetadataHelper.CompareMethodSignatures(cmdletMetadata, oldTypeMetadata.Constructors, newTypeMetadata.Constructors, issueLogger);
            }
        }
コード例 #6
0
        /// <summary>
        /// Check if the given types are generics, and if so, see if their generic properties are the same. If not, log an issue.
        /// </summary>
        /// <param name="cmdlet">The cmdlet metadata currently being checked.</param>
        /// <param name="oldTypeMetadata">The type metadata from the old (serialized) assembly.</param>
        /// <param name="newTypeMetadata">The type metadata from the new assembly.</param>
        /// <param name="target">Target of the generic type breaking change.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        /// <returns></returns>
        private bool IsGenericType(
            CmdletMetadata cmdlet,
            TypeMetadata oldTypeMetadata,
            TypeMetadata newTypeMetadata,
            string target,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            // If the type is a generic, check for any breaking changes
            if (oldTypeMetadata.GenericTypeArguments.Count > 0 && newTypeMetadata.GenericTypeArguments.Count > 0)
            {
                // Check if the generic type has changed
                if (HasSameGenericType(cmdlet, oldTypeMetadata, newTypeMetadata, target, issueLogger))
                {
                    // Check if the number of generic type arguments is the same
                    if (HasSameGenericArgumentSize(cmdlet, oldTypeMetadata, newTypeMetadata, target, issueLogger))
                    {
                        // For each element in the generic type arguments list,
                        // make sure that the types are the same
                        for (int idx = 0; idx < oldTypeMetadata.GenericTypeArguments.Count; idx++)
                        {
                            var oldArgument = oldTypeMetadata.GenericTypeArguments[idx];
                            var newArgument = newTypeMetadata.GenericTypeArguments[idx];

                            var oldElementType = _oldTypeDictionary[oldArgument];
                            var newElementType = _newTypeDictionary[newArgument];
                            if (string.Equals(oldArgument, newArgument, StringComparison.OrdinalIgnoreCase))
                            {
                                // If we have not previously seen this generic type argument,
                                // run this method on the type
                                if (!_typeSet.Contains(oldArgument))
                                {
                                    _typeSet.Add(oldArgument);
                                    CompareTypeMetadata(cmdlet, oldElementType, newElementType, issueLogger);
                                }
                            }
                            else
                            {
                                // If the generic type arguments have changed, throw a specific exception for generics
                                if (!CompareTypeMetadata(cmdlet, oldElementType, newElementType, issueLogger))
                                {
                                    // If the generic type arguments aren't the same, log an issue
                                    issueLogger?.LogBreakingChangeIssue(
                                        cmdlet: cmdlet,
                                        severity: 0,
                                        problemId: ProblemIds.BreakingChangeProblemId.ChangedGenericTypeArgument,
                                        description: string.Format(Properties.Resources.ChangedGenericTypeArgumentDescription,
                                                                   target, oldArgument, newArgument),
                                        remediation: string.Format(Properties.Resources.ChangedGenericTypeArgumentRemediation,
                                                                   target, oldArgument));
                                }
                            }
                        }
                    }
                }

                return(true);
            }

            return(false);
        }
コード例 #7
0
        /// <summary>
        /// Check if an alias to a parameter has been removed.
        /// </summary>
        /// <param name="cmdlet">The cmdlet whose parameter metadata is currently being checked.</param>
        /// <param name="oldParameter">The parameter metadata from the old (serialized) assembly.</param>
        /// <param name="newParameter">The parameter metadata from new assembly</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        private void CheckForRemovedParameterAlias(
            CmdletMetadata cmdlet,
            ParameterMetadata oldParameter,
            ParameterMetadata newParameter,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            // This set will contain all of the aliases in the new metadata
            HashSet <string> aliasSet = new HashSet <string>();

            // Add each of the aliases from the parameter in the new metadata
            foreach (var newAlias in newParameter.AliasList)
            {
                aliasSet.Add(newAlias);
            }

            // For each alias from the parameter in the old metadata,
            // see if it exists in the new metadata
            foreach (var oldAlias in oldParameter.AliasList)
            {
                // If the alias cannot be found, log an issue
                if (!aliasSet.Contains(oldAlias))
                {
                    issueLogger?.LogBreakingChangeIssue(
                        cmdlet: cmdlet,
                        severity: 0,
                        problemId: ProblemIds.BreakingChangeProblemId.RemovedParameterAlias,
                        description: string.Format(Resources.RemovedParameterAliasDescription,
                                                   cmdlet.Name, oldAlias, oldParameter.Name),
                        remediation: string.Format(Resources.RemovedParameterAliasRemediation,
                                                   oldAlias, oldParameter.Name));
                }
            }
        }
コード例 #8
0
        /// <summary>
        /// Check if the type of the generic is the same for two generics. If not, log an issue.
        /// </summary>
        /// <param name="cmdlet">The cmdlet metadata currently being checked.</param>
        /// <param name="oldTypeMetadata">The type metadata from the old (serialized) assembly.</param>
        /// <param name="newTypeMetadata">The type metadata from the new assembly.</param>
        /// <param name="target">Target of the generic type breaking change..</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        private bool HasSameGenericType(
            CmdletMetadata cmdlet,
            TypeMetadata oldTypeMetadata,
            TypeMetadata newTypeMetadata,
            string target,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            // If the type of the generics are the same, return true
            if (oldTypeMetadata.Name.Equals(newTypeMetadata.Name, StringComparison.OrdinalIgnoreCase))
            {
                return(true);
            }

            // Otherwise, log an issue and return false
            issueLogger?.LogBreakingChangeIssue(
                cmdlet: cmdlet,
                severity: 0,
                problemId: ProblemIds.BreakingChangeProblemId.ChangedGenericType,
                description: string.Format(Properties.Resources.ChangedGenericTypeDescription,
                                           target, oldTypeMetadata.Name, newTypeMetadata.Name),
                remediation: string.Format(Properties.Resources.ChangedGenericTypeRemediation,
                                           target, oldTypeMetadata.Name));

            return(false);
        }
コード例 #9
0
        /// <summary>
        /// Check for any values that were removed from a validation set of a parameter.
        /// </summary>
        /// <param name="cmdlet">The cmdlet whose parameter metadata is currently being checked.</param>
        /// <param name="oldParameter">The parameter metadata from the old (serialized) assembly.</param>
        /// <param name="newParameter">The parameter metadata from new assembly</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        private void CheckParameterValidationSets(
            CmdletMetadata cmdlet,
            ParameterMetadata oldParameter,
            ParameterMetadata newParameter,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            var oldValidateSet = oldParameter.ValidateSet;
            var newValidateSet = newParameter.ValidateSet;

            // If there is no validate set in the new assembly, return
            if (newValidateSet.Count == 0)
            {
                return;
            }

            // If there was no validate set in the old assembly, but there is
            // one in the new assembly, log an issue
            if (oldValidateSet.Count == 0 && newValidateSet.Count > 0)
            {
                issueLogger?.LogBreakingChangeIssue(
                    cmdlet: cmdlet,
                    severity: 0,
                    problemId: ProblemIds.BreakingChangeProblemId.AddedValidateSet,
                    description: string.Format(Properties.Resources.AddedValidateSetDescription,
                                               oldParameter.Name, cmdlet.Name),
                    remediation: string.Format(Properties.Resources.AddedValidateSetRemediation,
                                               oldParameter.Name));

                return;
            }

            // This set will contain all of the values in the validate set from the new metadata
            HashSet <string> valueSet = new HashSet <string>();

            // Add each value in the validate set from the new metadata to the set
            foreach (var newValue in newValidateSet)
            {
                valueSet.Add(newValue);
            }

            // For each value in the validate set from the old metadata, check if
            // it exists in the new metadata
            foreach (var oldValue in oldValidateSet)
            {
                // If the value cannot be found, log an issue
                if (!valueSet.Contains(oldValue))
                {
                    issueLogger?.LogBreakingChangeIssue(
                        cmdlet: cmdlet,
                        severity: 0,
                        problemId: ProblemIds.BreakingChangeProblemId.RemovedValidateSetValue,
                        description: string.Format(Properties.Resources.RemovedValidateSetValueDescription,
                                                   oldParameter.Name, cmdlet.Name, oldValue),
                        remediation: string.Format(Properties.Resources.RemovedValidateSetValueRemediation,
                                                   oldValue, oldParameter.Name));
                }
            }
        }
コード例 #10
0
        /// <summary>
        /// Check if the default parameter set has changed, and if so, make sure
        /// that the parameters are the same
        /// </summary>
        /// <param name="oldCmdlet">The cmdlet metadata from the old (serialized) assembly.</param>
        /// <param name="newCmdlet">The cmdlet metadata from the new assembly.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        private void CheckDefaultParameterName(
            CmdletMetadata oldCmdlet,
            CmdletMetadata newCmdlet,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            // If the default parameter name hasn't changed, or if there wasn't a
            // default parameter set defined before, return
            if (oldCmdlet.DefaultParameterSetName.Equals(newCmdlet.DefaultParameterSetName) ||
                oldCmdlet.DefaultParameterSetName.Equals("__AllParameterSets"))
            {
                return;
            }

            // Get the metadata for the old default parameter set
            ParameterSetMetadata oldDefaultParameterSet = oldCmdlet.ParameterSets
                                                          .FirstOrDefault(p => p.Name.Equals(oldCmdlet.DefaultParameterSetName, StringComparison.OrdinalIgnoreCase));
            // Get the metadata for the new default parameter set
            ParameterSetMetadata newDefaultParameterSet = newCmdlet.ParameterSets
                                                          .FirstOrDefault(p => p.Name.Equals(newCmdlet.DefaultParameterSetName, StringComparison.OrdinalIgnoreCase));

            if (oldDefaultParameterSet == null || newDefaultParameterSet == null)
            {
                return;
            }
            // This dictionary will map a parameter name and aliases to the corresponding Parameter object
            Dictionary <string, Parameter> parameterDictionary = new Dictionary <string, Parameter>();

            // Add each parameter name and aliases to the dictionary
            foreach (var newParameter in newDefaultParameterSet.Parameters)
            {
                parameterDictionary.Add(newParameter.ParameterMetadata.Name, newParameter);

                foreach (var alias in newParameter.ParameterMetadata.AliasList)
                {
                    parameterDictionary.Add(alias, newParameter);
                }
            }

            // For each parameter in the old default parameter set, check if it
            // exists in the new default parameter set
            foreach (var oldParameter in oldDefaultParameterSet.Parameters)
            {
                // If the parameter cannot be found, log an issue
                if (!parameterDictionary.ContainsKey(oldParameter.ParameterMetadata.Name))
                {
                    issueLogger?.LogBreakingChangeIssue(
                        cmdlet: oldCmdlet,
                        severity: 0,
                        problemId: ProblemIds.BreakingChangeProblemId.ChangeDefaultParameter,
                        description: string.Format(Resources.ChangeDefaultParameterDescription,
                                                   oldCmdlet.DefaultParameterSetName, oldCmdlet.Name),
                        remediation: string.Format(Resources.ChangeDefaultParameterRemediation,
                                                   oldCmdlet.Name, oldCmdlet.DefaultParameterSetName));
                }
            }
        }
コード例 #11
0
 /// <summary>
 /// Check if the type for a parameter has been changed, or if any of the
 /// type's properties have been removed or changed.
 /// </summary>
 /// <param name="cmdlet">The cmdlet whose parameter metadata is currently being checked.</param>
 /// <param name="oldParameter">The parameter metadata from the old (serialized) assembly.</param>
 /// <param name="newParameter">The parameter metadata from new assembly</param>
 /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
 private void CheckForChangedParameterType(
     CmdletMetadata cmdlet,
     ParameterMetadata oldParameter,
     ParameterMetadata newParameter,
     ReportLogger <BreakingChangeIssue> issueLogger)
 {
     // Recursively look at the properties of each type and their
     // types to see if there are any breaking changes
     _typeMetadataHelper.CheckParameterType(cmdlet, oldParameter, oldParameter.Type, newParameter.Type, issueLogger);
 }
コード例 #12
0
        public void CompareMethodSignatures(
            CmdletMetadata cmdlet,
            List <TypeMetadata.MethodSignature> oldMethods,
            List <TypeMetadata.MethodSignature> newMethods,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            foreach (var oldMethod in oldMethods)
            {
                bool found            = false;
                var  candidateMethods = newMethods.Where(m => string.Equals(m.Name, oldMethod.Name)).ToList();
                foreach (var newMethod in candidateMethods)
                {
                    var oldParameters = oldMethod.Parameters;
                    var newParameters = newMethod.Parameters;
                    if (oldParameters.Count != newParameters.Count)
                    {
                        continue;
                    }

                    bool match = true;
                    for (int idx = 0; idx < oldParameters.Count; idx++)
                    {
                        var oldParameter = oldParameters[idx];
                        var newParameter = newParameters[idx];
                        if (oldParameter.Name != newParameter.Name || oldParameter.Type != newParameter.Type)
                        {
                            match = false;
                            break;
                        }
                    }

                    if (oldMethod.ReturnType != null && newMethod.ReturnType != null && !string.Equals(oldMethod.ReturnType, newMethod.ReturnType))
                    {
                        match = false;
                    }

                    if (match)
                    {
                        found = true;
                        break;
                    }
                }

                if (!found)
                {
                    issueLogger?.LogBreakingChangeIssue(
                        cmdlet: cmdlet,
                        severity: 0,
                        problemId: 0,
                        description: string.Empty,
                        remediation: string.Empty);
                }
            }
        }
コード例 #13
0
        /// <summary>
        /// Check if the OutputType of the cmdlet has been removed, or if any property
        /// of the OutputType has been removed or changed
        /// </summary>
        /// <param name="oldCmdlet">The cmdlet metadata from the old (serialized) assembly.</param>
        /// <param name="newCmdlet">The cmdlet metadata from the new assembly.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        private void CheckForChangedOutputType(
            CmdletMetadata oldCmdlet,
            CmdletMetadata newCmdlet,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            // This dictionary will map an output type name to the corresponding type metadata
            Dictionary <string, TypeMetadata> outputDictionary = new Dictionary <string, TypeMetadata>(new TypeNameComparer());

            // Add each output in the new metadata to the dictionary
            if (newCmdlet != null && newCmdlet.OutputTypes != null)
            {
                foreach (var newOutput in newCmdlet.OutputTypes)
                {
                    if (!outputDictionary.ContainsKey(newOutput.Type.Name))
                    {
                        outputDictionary.Add(newOutput.Type.Name, newOutput.Type);
                    }
                }
            }

            // For each output in the old metadata, see if it
            // exists in the new metadata
            foreach (var oldOutput in oldCmdlet.OutputTypes)
            {
                // If the output can be found, use the TypeMetadataHelper to
                // check the type for any breaking changes
                if (outputDictionary.ContainsKey(oldOutput.Type.Name))
                {
                    var newOutputType = outputDictionary[oldOutput.Type.Name];

                    _typeMetadataHelper.CheckOutputType(oldCmdlet, oldOutput.Type, newOutputType, issueLogger);
                }
                // If the output cannot be found by name, check if the old output can be mapped
                // to any of the new output types
                else
                {
                    var foundOutput = outputDictionary.Values.Any(o => _typeMetadataHelper.CompareTypeMetadata(oldCmdlet, oldOutput.Type, o, null));
                    if (!foundOutput)
                    {
                        issueLogger?.LogBreakingChangeIssue(
                            cmdlet: oldCmdlet,
                            severity: 0,
                            problemId: ProblemIds.BreakingChangeProblemId.ChangedOutputType,
                            description: string.Format(Resources.ChangedOutputTypeDescription,
                                                       oldCmdlet.Name, oldOutput.Type.Name),
                            remediation: string.Format(Resources.ChangedOutputTypeRemediation,
                                                       oldCmdlet.Name, oldOutput.Type.Name));
                    }
                }
            }
        }
コード例 #14
0
 public static void LogBreakingChangeIssue(
     this ReportLogger <BreakingChangeIssue> issueLogger, CmdletMetadata cmdlet,
     string description, string remediation, int severity, int problemId)
 {
     issueLogger.LogRecord(new BreakingChangeIssue
     {
         ClassName   = cmdlet.ClassName,
         Target      = cmdlet.Name,
         Description = description,
         Remediation = remediation,
         Severity    = severity,
         ProblemId   = problemId
     });
 }
コード例 #15
0
        /// <summary>
        /// Check if the parameter gained a validation range for values, or if the
        /// existing validation range for values excludes values previously accepted.
        /// </summary>
        /// <param name="cmdlet">The cmdlet whose parameter metadata is currently being checked.</param>
        /// <param name="oldParameter">The parameter metadata from the old (serialized) assembly.</param>
        /// <param name="newParameter">The parameter metadata from new assembly.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of the issues found.</param>
        private void CheckParameterValidateRange(
            CmdletMetadata cmdlet,
            ParameterMetadata oldParameter,
            ParameterMetadata newParameter,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            if (newParameter.ValidateRangeMin != null && newParameter.ValidateRangeMax != null)
            {
                // If the old parameter had no validation range, but the new parameter does, log an issue
                if (oldParameter.ValidateRangeMin == null && oldParameter.ValidateRangeMax == null)
                {
                    issueLogger?.LogBreakingChangeIssue(
                        cmdlet: cmdlet,
                        severity: 0,
                        problemId: ProblemIds.BreakingChangeProblemId.AddedValidateRange,
                        description: string.Format(Properties.Resources.AddedValidateRangeDescription,
                                                   oldParameter.Name, cmdlet.Name),
                        remediation: string.Format(Properties.Resources.AddedValidateRangeRemediation,
                                                   oldParameter.Name));
                }
                else
                {
                    // If the minimum value of the range has increased, log an issue
                    if (oldParameter.ValidateRangeMin < newParameter.ValidateRangeMin)
                    {
                        issueLogger?.LogBreakingChangeIssue(
                            cmdlet: cmdlet,
                            severity: 0,
                            problemId: ProblemIds.BreakingChangeProblemId.ChangedValidateRangeMinimum,
                            description: string.Format(Properties.Resources.ChangedValidateRangeMinimumDescription,
                                                       oldParameter.Name, oldParameter.ValidateRangeMin, newParameter.ValidateRangeMin),
                            remediation: string.Format(Properties.Resources.ChangedValidateRangeMinimumRemediation,
                                                       oldParameter.Name, oldParameter.ValidateRangeMin));
                    }

                    // If the maximum value of the range has decreased, log an issue
                    if (oldParameter.ValidateRangeMax > newParameter.ValidateRangeMax)
                    {
                        issueLogger?.LogBreakingChangeIssue(
                            cmdlet: cmdlet,
                            severity: 0,
                            problemId: ProblemIds.BreakingChangeProblemId.ChangedValidateRangeMaximum,
                            description: string.Format(Properties.Resources.ChangedValidateRangeMaximumDescription,
                                                       oldParameter.Name, oldParameter.ValidateRangeMax, newParameter.ValidateRangeMax),
                            remediation: string.Format(Properties.Resources.ChangedValidateRangeMaximumRemediation,
                                                       oldParameter.Name, oldParameter.ValidateRangeMax));
                    }
                }
            }
        }
コード例 #16
0
        /// <summary>
        /// Compares the metadata of parameters with the same name (or alias) for any breaking changes.
        ///
        /// Breaking changes for parameters include
        ///   - Removing a parameter
        ///   - Removing an alias to a parameter
        ///   - Type (or any of its properties) of parameter has changed
        ///   - A value in the validate set of a parameter has been removed
        ///   - ValidateNotNullOrEmpty attribute has been added to a parameter
        /// </summary>
        /// <param name="cmdlet">Reference to the cmdlet whose parameters are being checked.</param>
        /// <param name="oldParameters">The list of parameters from the old cmdlet metadata.</param>
        /// <param name="newParameters">The list of parameters from the new cmdlet metadata.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of the issues found.</param>
        public void CompareParameterMetadata(
            CmdletMetadata cmdlet,
            List <ParameterMetadata> oldParameters,
            List <ParameterMetadata> newParameters,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            // This dictionary will map a parameter name (or alias) to the corresponding metadata
            Dictionary <string, ParameterMetadata> parameterDictionary = new Dictionary <string, ParameterMetadata>();

            // For each parameter, add its name (and aliases) to the dictionary
            foreach (var newParameter in newParameters)
            {
                parameterDictionary.Add(newParameter.Name, newParameter);

                foreach (var alias in newParameter.AliasList)
                {
                    parameterDictionary.Add(alias, newParameter);
                }
            }

            // For each parameter in the old metadata, see if it has been
            // added to the dictionary (exists in the new metadata)
            foreach (var oldParameter in oldParameters)
            {
                if (parameterDictionary.ContainsKey(oldParameter.Name))
                {
                    var newParameter = parameterDictionary[oldParameter.Name];

                    // Go through all of the breaking change checks for parameters
                    CheckForChangedParameterType(cmdlet, oldParameter, newParameter, issueLogger);
                    CheckForRemovedParameterAlias(cmdlet, oldParameter, newParameter, issueLogger);
                    CheckParameterValidationSets(cmdlet, oldParameter, newParameter, issueLogger);
                    CheckForValidateNotNullOrEmpty(cmdlet, oldParameter, newParameter, issueLogger);
                    CheckParameterValidateRange(cmdlet, oldParameter, newParameter, issueLogger);
                }
                // If the parameter cannot be found, log an issue
                else
                {
                    issueLogger?.LogBreakingChangeIssue(
                        cmdlet: cmdlet,
                        severity: 0,
                        problemId: ProblemIds.BreakingChangeProblemId.RemovedParameter,
                        description: string.Format(Properties.Resources.RemovedParameterDescription,
                                                   cmdlet.Name, oldParameter.Name),
                        remediation: string.Format(Properties.Resources.RemovedParameterRemediation,
                                                   oldParameter.Name, cmdlet.Name));
                }
            }
        }
コード例 #17
0
 /// <summary>
 /// Check if a cmdlet no longer implements SupportsPaging.
 /// </summary>
 /// <param name="oldCmdlet">The cmdlet metadata from the old (serialized) assembly.</param>
 /// <param name="newCmdlet">The cmdlet metadata from the new assembly.</param>
 /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
 private void CheckForRemovedSupportsPaging(
     CmdletMetadata oldCmdlet,
     CmdletMetadata newCmdlet,
     ReportLogger <BreakingChangeIssue> issueLogger)
 {
     // If the old cmdlet implements SupportsPaging and the new cmdlet does not, log an issue
     if (oldCmdlet.SupportsPaging && !newCmdlet.SupportsPaging)
     {
         issueLogger.LogBreakingChangeIssue(
             cmdlet: oldCmdlet,
             severity: 0,
             problemId: ProblemIds.BreakingChangeProblemId.RemovedPaging,
             description: string.Format(Properties.Resources.RemovedPagingDescription, oldCmdlet.Name),
             remediation: string.Format(Properties.Resources.RemovedPagingRemediation, oldCmdlet.Name));
     }
 }
コード例 #18
0
        /// <summary>
        /// Checks if the type of the parameter is an array or a generic, and makes sure there are no breaking changes.
        /// If the type is not an array or a generic, it proceeds with the normal type checking with CompareTypeMetadata.
        /// </summary>
        /// <param name="cmdlet">The cmdlet whose parameter metadata is being checked for breaking changes.</param>
        /// <param name="parameter">The parameter whose type metadata is being checked for breaking changes.</param>
        /// <param name="oldTypeMetadata">The type metadata from the old (serialized) assembly.</param>
        /// <param name="newTypeMetadata">The type metadata from the new assembly.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        public void CheckParameterType(
            CmdletMetadata cmdlet,
            ParameterMetadata parameter,
            TypeMetadata oldTypeMetadata,
            TypeMetadata newTypeMetadata,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            int    problemId   = ProblemIds.BreakingChangeProblemId.ChangedParameterElementType;
            string description = string.Format(Resources.ChangedParameterElementTypeDescription,
                                               parameter.Name, oldTypeMetadata.ElementType, newTypeMetadata.ElementType);
            string remediation = string.Format(Resources.ChangedParameterElementTypeRemediation,
                                               parameter.Name, oldTypeMetadata.ElementType);

            // If the type is an array, check for any breaking changes
            if (IsElementType(cmdlet, oldTypeMetadata, newTypeMetadata, problemId, description, remediation, issueLogger))
            {
                return;
            }

            string target = string.Format("parameter {0}", parameter.Name);

            // If the type is a generic, check for any breaking changes
            if (IsGenericType(cmdlet, oldTypeMetadata, newTypeMetadata, target, issueLogger))
            {
                return;
            }

            // If the types are different, log an issue
            string oldTypeName = oldTypeMetadata.GetClassNameWithoutApiVersion(oldTypeMetadata.Name);
            string newTypeName = newTypeMetadata.GetClassNameWithoutApiVersion(newTypeMetadata.Name);

            if (!oldTypeName.Equals(newTypeName, StringComparison.OrdinalIgnoreCase))
            {
                issueLogger?.LogBreakingChangeIssue(
                    cmdlet: cmdlet,
                    severity: 0,
                    problemId: ProblemIds.BreakingChangeProblemId.ChangedParameterType,
                    description: string.Format(Resources.ChangedParameterTypeDescription,
                                               cmdlet.Name, oldTypeMetadata.Name, parameter.Name),
                    remediation: string.Format(Resources.ChangedParameterTypeRemediation,
                                               parameter.Name, oldTypeMetadata.Name));
            }
            else
            {
                CompareTypeMetadata(cmdlet, oldTypeMetadata, newTypeMetadata, issueLogger);
            }
        }
コード例 #19
0
        /// <summary>
        /// Check if the given types are generics, and if so, see if their generic properties are the same. If not, log an issue.
        /// </summary>
        /// <param name="cmdlet">The cmdlet metadata currently being checked.</param>
        /// <param name="oldTypeMetadata">The type metadata from the old (serialized) assembly.</param>
        /// <param name="newTypeMetadata">The type metadata from the new assembly.</param>
        /// <param name="target">Target of the generic type breaking change.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        /// <returns></returns>
        private bool IsGenericType(
            CmdletMetadata cmdlet,
            TypeMetadata oldTypeMetadata,
            TypeMetadata newTypeMetadata,
            string target,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            // If the type is a generic, check for any breaking changes
            if (oldTypeMetadata.GenericTypeArguments.Count > 0 && newTypeMetadata.GenericTypeArguments.Count > 0)
            {
                // Check if the generic type has changed
                if (HasSameGenericType(cmdlet, oldTypeMetadata, newTypeMetadata, target, issueLogger))
                {
                    // Check if the number of generic type arguments is the same
                    if (HasSameGenericArgumentSize(cmdlet, oldTypeMetadata, newTypeMetadata, target, issueLogger))
                    {
                        // For each element in the generic type arguments list,
                        // make sure that the types are the same
                        for (int idx = 0; idx < oldTypeMetadata.GenericTypeArguments.Count; idx++)
                        {
                            var oldArgument = oldTypeMetadata.GenericTypeArguments[idx];
                            var newArgument = newTypeMetadata.GenericTypeArguments[idx];

                            if (CheckGenericTypeArguments(cmdlet, oldArgument, newArgument, target, issueLogger))
                            {
                                // If we have not previously seen this generic type argument,
                                // run this method on the type
                                if (!_typeSet.Contains(oldArgument))
                                {
                                    _typeSet.Add(oldArgument);

                                    var oldElementType = _oldTypeDictionary[oldArgument];
                                    var newElementType = _newTypeDictionary[newArgument];

                                    CompareTypeMetadata(cmdlet, oldElementType, newElementType, issueLogger);
                                }
                            }
                        }
                    }
                }

                return(true);
            }

            return(false);
        }
コード例 #20
0
        /// <summary>
        /// Check if the given types are arrays, and if so, see if their element types are the same. If not, log an issue.
        /// </summary>
        /// <param name="cmdlet">The cmdlet metadata currently being checked.</param>
        /// <param name="oldTypeMetadata">The type metadata from the old (serialized) assembly.</param>
        /// <param name="newTypeMetadata">The type metadata from the new assembly.</param>
        /// <param name="problemId">The problemId used for logging if there is an issue.</param>
        /// <param name="description">The description used for logging if there is an issue.</param>
        /// <param name="remediation">The remediation used for logging if there is an issue.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        private bool IsElementType(
            CmdletMetadata cmdlet,
            TypeMetadata oldTypeMetadata,
            TypeMetadata newTypeMetadata,
            int problemId,
            string description,
            string remediation,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            // Check if the type is an array
            if (oldTypeMetadata.ElementType != null && newTypeMetadata.ElementType != null)
            {
                string oldTypeName = oldTypeMetadata.GetClassNameWithoutApiVersion(oldTypeMetadata.ElementType);
                string newTypeName = newTypeMetadata.GetClassNameWithoutApiVersion(newTypeMetadata.ElementType);
                // Check if the element of the array is the same in the old and new metadata
                if (oldTypeName.Equals(newTypeName, StringComparison.OrdinalIgnoreCase))
                {
                    // If we have not previously seen this element type,
                    // run this method on the type
                    if (!_typeSet.Contains(oldTypeMetadata.ElementType))
                    {
                        _typeSet.Add(oldTypeMetadata.ElementType);

                        var oldElementType = _oldTypeDictionary[oldTypeMetadata.ElementType];
                        var newElementType = _newTypeDictionary[newTypeMetadata.ElementType];

                        CompareTypeMetadata(cmdlet, oldElementType, newElementType, issueLogger);
                    }
                }
                // If the element type has changed, log an issue
                else
                {
                    issueLogger?.LogBreakingChangeIssue(
                        cmdlet: cmdlet,
                        severity: 0,
                        problemId: problemId,
                        description: description,
                        remediation: remediation);
                }

                return(true);
            }

            return(false);
        }
コード例 #21
0
 /// <summary>
 /// Check if the parameter now supports the ValidateNotNullOrEmpty attribute
 /// </summary>
 /// <param name="cmdlet">The cmdlet whose parameter metadata is currently being checked.</param>
 /// <param name="oldParameter">The parameter metadata from the old (serialized) assembly.</param>
 /// <param name="newParameter">The parameter metadata from new assembly</param>
 /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
 private void CheckForValidateNotNullOrEmpty(
     CmdletMetadata cmdlet,
     ParameterMetadata oldParameter,
     ParameterMetadata newParameter,
     ReportLogger <BreakingChangeIssue> issueLogger)
 {
     // If the parameter didn't have the ValidateNotNullOrEmpty attribute in the
     // old assembly, but has it in the new assembly, log an issue
     if (!oldParameter.ValidateNotNullOrEmpty && newParameter.ValidateNotNullOrEmpty)
     {
         issueLogger?.LogBreakingChangeIssue(
             cmdlet: cmdlet,
             severity: 0,
             problemId: ProblemIds.BreakingChangeProblemId.AddedValidateNotNullOrEmpty,
             description: string.Format(Properties.Resources.AddedValidateNotNullOrEmptyDescription,
                                        oldParameter.Name, cmdlet.Name),
             remediation: string.Format(Properties.Resources.AddedValidateNotNullOrEmptyRemediation,
                                        oldParameter.Name));
     }
 }
コード例 #22
0
        /// <summary>
        /// Check if the arguments of a generic type are the same. If they are not, log an issue.
        /// </summary>
        /// <param name="cmdlet">The cmdlet metadata currently being checked.</param>
        /// <param name="oldArgument">The old argument from the generic.</param>
        /// <param name="newArgument">The new argument from the generic</param>
        /// <param name="target">Target of the generic type breaking change.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        private bool CheckGenericTypeArguments(
            CmdletMetadata cmdlet,
            string oldArgument,
            string newArgument,
            string target,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            if (oldArgument.Equals(newArgument, StringComparison.OrdinalIgnoreCase))
            {
                return(true);
            }

            // If the generic type arguments aren't the same, log an issue
            issueLogger.LogBreakingChangeIssue(
                cmdlet: cmdlet,
                severity: 0,
                problemId: ProblemIds.BreakingChangeProblemId.ChangedGenericTypeArgument,
                description: string.Format(Properties.Resources.ChangedGenericTypeArgumentDescription,
                                           target, oldArgument, newArgument),
                remediation: string.Format(Properties.Resources.ChangedGenericTypeArgumentRemediation,
                                           target, oldArgument));

            return(false);
        }
コード例 #23
0
        private void AnalyzeMarkdownHelp(
            IEnumerable <string> scopes,
            string directory,
            ReportLogger <HelpIssue> helpLogger,
            List <string> processedHelpFiles,
            string savedDirectory)
        {
            var helpFolder = Directory.EnumerateDirectories(directory, "help").FirstOrDefault();
            var service    = Path.GetFileName(directory);

            if (helpFolder == null)
            {
                helpLogger.LogRecord(new HelpIssue()
                {
                    Assembly    = service,
                    Description = string.Format("{0} has no matching help folder", service),
                    Severity    = 0,
                    Remediation = string.Format("Make sure a help folder for {0} exists and it is " +
                                                "being copied to the output directory.", service),
                    Target    = service,
                    HelpFile  = service + "/folder",
                    ProblemId = MissingHelpFile
                });

                return;
            }

            var helpFiles = Directory.EnumerateFiles(helpFolder, "*.md").Select(Path.GetFileNameWithoutExtension).ToList();

            // Assume all cmdlets markdown file following format of VERB-AzResource. Dash is required.
            helpFiles = helpFiles.Where(c => c.Contains("-")).ToList();
            if (!helpFiles.Any())
            {
                return;
            }

            Directory.SetCurrentDirectory(directory);
            var manifestFiles = Directory.EnumerateFiles(directory, "*.psd1").ToList();

            if (manifestFiles.Count > 1)
            {
                manifestFiles = manifestFiles.Where(f => Path.GetFileName(f).IndexOf(service) >= 0).ToList();
            }

            if (manifestFiles.Count == 0)
            {
                return;
            }

            var psd1            = manifestFiles.FirstOrDefault();
            var parentDirectory = Directory.GetParent(psd1).FullName;
            var psd1FileName    = Path.GetFileName(psd1);
            var powershell      = PowerShell.Create();
            var script          = $"Import-LocalizedData -BaseDirectory {parentDirectory} -FileName {psd1FileName} -BindingVariable ModuleMetadata; $ModuleMetadata.NestedModules";

            powershell.AddScript(script);
            var cmdletResult  = powershell.Invoke();
            var nestedModules = new List <string>();

            foreach (var module in cmdletResult)
            {
                if (module != null && module.ToString().StartsWith("."))
                {
                    nestedModules.Add(module.ToString().Substring(2));
                }
                else if (module != null)
                {
                    nestedModules.Add(module.ToString());
                }
            }

            script = $"Import-LocalizedData -BaseDirectory {parentDirectory} -FileName {psd1FileName}" +
                     " -BindingVariable ModuleMetadata; $ModuleMetadata.RequiredModules | % { $_[\"ModuleName\"] };";
            cmdletResult = PowerShell.Create().AddScript(script).Invoke();
            var requiredModules = cmdletResult.Where(c => c != null && !c.ToString().StartsWith(".")).Select(c => c.ToString()).ToList();
            var allCmdlets      = new List <CmdletMetadata>();

            if (nestedModules.Any())
            {
                Directory.SetCurrentDirectory(directory);

                requiredModules = requiredModules.Join(scopes,
                                                       module => 1,
                                                       dir => 1,
                                                       (module, dir) => Path.Combine(dir, module))
                                  .Where(Directory.Exists)
                                  .ToList();

                requiredModules.Add(directory);
                foreach (var nestedModule in nestedModules)
                {
                    var assemblyFile = Directory.GetFiles(parentDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault();
                    if (!File.Exists(assemblyFile))
                    {
                        continue;
                    }

                    var assemblyFileName = Path.GetFileName(assemblyFile);
                    helpLogger.Decorator.AddDecorator(h =>
                    {
                        h.HelpFile = assemblyFileName;
                        h.Assembly = assemblyFileName;
                    }, "Cmdlet");
                    processedHelpFiles.Add(assemblyFileName);
                    // TODO: Remove IfDef
#if NETSTANDARD
                    var proxy = new CmdletLoader();
#else
                    var proxy = EnvironmentHelpers.CreateProxy <CmdletLoader>(directory, out _appDomain);
#endif
                    var module  = proxy.GetModuleMetadata(assemblyFile, requiredModules);
                    var cmdlets = module.Cmdlets;
                    allCmdlets.AddRange(cmdlets);
                    helpLogger.Decorator.Remove("Cmdlet");
                    // TODO: Remove IfDef code
#if !NETSTANDARD
                    AppDomain.Unload(_appDomain);
#endif
                }
            }

            script       = $"Import-LocalizedData -BaseDirectory {parentDirectory} -FileName {psd1FileName} -BindingVariable ModuleMetadata; $ModuleMetadata.FunctionsToExport;";
            cmdletResult = PowerShell.Create().AddScript(script).Invoke();
            var functionCmdlets = cmdletResult.Select(c => c.ToString()).ToList();
            foreach (var cmdlet in functionCmdlets)
            {
                var metadata = new CmdletMetadata();
                metadata.VerbName = cmdlet.Split("-")[0];
                metadata.NounName = cmdlet.Split("-")[1];
                allCmdlets.Add(metadata);
            }

            ValidateHelpRecords(allCmdlets, helpFiles, helpLogger);
            ValidateHelpMarkdown(helpFolder, helpFiles, helpLogger);

            Directory.SetCurrentDirectory(savedDirectory);
        }
コード例 #24
0
        /// <summary>
        /// Get the ModuleMetadata from a cmdlet assembly.
        /// </summary>
        /// <param name="assmeblyPath">Path to the cmdlet assembly.</param>
        /// <returns>ModuleMetadata containing information about the cmdlets found in the given assembly.</returns>
        public ModuleMetadata GetModuleMetadata(string assemblyPath)
        {
            List <CmdletMetadata> results = new List <CmdletMetadata>();

            ModuleMetadata = new ModuleMetadata();
            try
            {
                var assembly = Assembly.LoadFrom(assemblyPath);
                foreach (var type in assembly.GetCmdletTypes())
                {
                    var cmdlet     = type.GetAttribute <CmdletAttribute>();
                    var outputs    = type.GetAttributes <OutputTypeAttribute>();
                    var parameters = type.GetParameters();

                    var cmdletMetadata = new CmdletMetadata
                    {
                        VerbName                = cmdlet.VerbName,
                        NounName                = cmdlet.NounName,
                        ConfirmImpact           = cmdlet.ConfirmImpact,
                        SupportsPaging          = cmdlet.SupportsPaging,
                        SupportsShouldProcess   = cmdlet.SupportsShouldProcess,
                        ClassName               = type.FullName,
                        DefaultParameterSetName = cmdlet.DefaultParameterSetName ?? "__AllParameterSets"
                    };

                    if (type.HasAttribute <AliasAttribute>())
                    {
                        var aliases = type.GetAttributes <AliasAttribute>();
                        cmdletMetadata.AliasList.AddRange(
                            aliases.SelectMany(a => a.AliasNames));
                    }

                    foreach (var output in outputs)
                    {
                        foreach (var outputType in output.Type)
                        {
                            var outputMetadata = new OutputMetadata
                            {
                                Type = outputType.Type
                            };
                            outputMetadata.ParameterSets.AddRange(output.ParameterSetName);
                            cmdletMetadata.OutputTypes.Add(outputMetadata);
                        }
                    }

                    List <Parameter> globalParameters = new List <Parameter>();

                    foreach (var parameter in parameters)
                    {
                        if (string.Equals(parameter.Name, "Force", StringComparison.OrdinalIgnoreCase) && parameter.PropertyType == typeof(SwitchParameter))
                        {
                            cmdletMetadata.HasForceSwitch = true;
                        }

                        var parameterData = new Models.ParameterMetadata
                        {
                            Type = parameter.PropertyType,
                            Name = parameter.Name
                        };

                        if (parameter.HasAttribute <AliasAttribute>())
                        {
                            var aliases = parameter.GetAttributes <AliasAttribute>();
                            parameterData.AliasList.AddRange(
                                aliases.SelectMany(a => a.AliasNames));
                        }

                        if (parameter.HasAttribute <ValidateSetAttribute>())
                        {
                            var validateSet = parameter.GetAttribute <ValidateSetAttribute>();
                            parameterData.ValidateSet.AddRange(validateSet.ValidValues);
                        }

                        if (parameter.HasAttribute <ValidateRangeAttribute>())
                        {
                            var validateRange = parameter.GetAttribute <ValidateRangeAttribute>();
                            parameterData.ValidateRangeMin = Convert.ToInt64(validateRange.MinRange);
                            parameterData.ValidateRangeMax = Convert.ToInt64(validateRange.MaxRange);
                        }

                        parameterData.ValidateNotNullOrEmpty = parameter.HasAttribute <ValidateNotNullOrEmptyAttribute>();

                        cmdletMetadata.Parameters.Add(parameterData);

                        foreach (var parameterSet in parameter.GetAttributes <ParameterAttribute>())
                        {
                            var parameterSetMetadata = cmdletMetadata.ParameterSets.FirstOrDefault(s => s.Name.Equals(parameterSet.ParameterSetName));

                            if (parameterSetMetadata == null)
                            {
                                parameterSetMetadata = new Models.ParameterSetMetadata()
                                {
                                    Name = parameterSet.ParameterSetName ?? "__AllParameterSets"
                                };
                            }

                            Parameter param = new Parameter
                            {
                                ParameterMetadata = parameterData,
                                Mandatory         = parameterSet.Mandatory,
                                Position          = parameterSet.Position,
                                ValueFromPipeline = parameterSet.ValueFromPipeline,
                                ValueFromPipelineByPropertyName = parameterSet.ValueFromPipelineByPropertyName
                            };

                            if (parameterSet.ParameterSetName.Equals("__AllParameterSets"))
                            {
                                globalParameters.Add(param);
                            }

                            parameterSetMetadata.Parameters.Add(param);

                            if (parameterSetMetadata.Parameters.Count == 1)
                            {
                                cmdletMetadata.ParameterSets.Add(parameterSetMetadata);
                            }
                        }
                    }

                    foreach (var parameterSet in cmdletMetadata.ParameterSets)
                    {
                        if (parameterSet.Name.Equals("__AllParameterSets"))
                        {
                            continue;
                        }

                        foreach (var parameter in globalParameters)
                        {
                            var param = parameterSet.Parameters.FirstOrDefault(p => p.ParameterMetadata.Name.Equals(parameter.ParameterMetadata.Name));
                            if (param == null)
                            {
                                parameterSet.Parameters.Add(parameter);
                            }
                        }
                    }

                    if (!cmdletMetadata.ParameterSets
                        .Where(p => p.Name.Equals(cmdletMetadata.DefaultParameterSetName, StringComparison.OrdinalIgnoreCase))
                        .Any())
                    {
                        var defaultSet = new Models.ParameterSetMetadata()
                        {
                            Name = cmdletMetadata.DefaultParameterSetName
                        };
                        cmdletMetadata.ParameterSets.Add(defaultSet);
                    }

                    results.Add(cmdletMetadata);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            ModuleMetadata.Cmdlets = results;
            return(ModuleMetadata);
        }
コード例 #25
0
        /// <summary>
        /// Check whether there exist mandatory equal in the cmdlet.
        /// Mandatory equal means two parameter set has exactly the same mandatory parameters.
        /// If all these two parameter set are not defualt, it may cause confusion.
        /// An example: https://github.com/Azure/azure-powershell/issues/10954
        /// </summary>
        public void ValidateParameterSetWithMandatoryEqual(CmdletMetadata cmdlet, ReportLogger <SignatureIssue> issueLogger)
        {
            var defaultParameterSet = cmdlet.DefaultParameterSet;
            List <HashSet <string> > mandatoryEqualSetList = new List <HashSet <string> >();

            foreach (var parameterSet1 in cmdlet.ParameterSets)
            {
                foreach (var parameterSet2 in cmdlet.ParameterSets)
                {
                    if (!parameterSet1.Equals(parameterSet2) &&
                        cmdlet.DefaultParameterSetName != parameterSet1.Name &&
                        cmdlet.DefaultParameterSetName != parameterSet2.Name)
                    {
                        if (parameterSet1.AllMandatoryParemeterEquals(parameterSet2) &&
                            !IsParameterSetIntersectionCoveredByDefault(parameterSet1, parameterSet2, defaultParameterSet))
                        {
                            var isExistInSet = false;
                            foreach (var mandatoryEqualSet in mandatoryEqualSetList)
                            {
                                if (mandatoryEqualSet.Contains(parameterSet1.Name) || mandatoryEqualSet.Contains(parameterSet2.Name))
                                {
                                    mandatoryEqualSet.Add(parameterSet1.Name);
                                    mandatoryEqualSet.Add(parameterSet2.Name);
                                    isExistInSet = true;
                                    break;
                                }
                            }
                            if (!isExistInSet)
                            {
                                HashSet <string> newSet = new HashSet <string>();
                                newSet.Add(parameterSet1.Name);
                                newSet.Add(parameterSet2.Name);
                                mandatoryEqualSetList.Add(newSet);
                            }
                        }
                    }
                }
            }

            if (mandatoryEqualSetList.Count > 0)
            {
                foreach (var mandatoryEqualSet in mandatoryEqualSetList)
                {
                    string mandatoryEqualSetNames = "";
                    foreach (var mandatoryEqualSetName in mandatoryEqualSet)
                    {
                        if (mandatoryEqualSetNames != "")
                        {
                            mandatoryEqualSetNames += ", ";
                        }
                        mandatoryEqualSetNames += "'" + mandatoryEqualSetName + "'";
                    }
                    issueLogger.LogSignatureIssue(
                        cmdlet: cmdlet,
                        severity: 1,
                        problemId: SignatureProblemId.ParameterSetWithStrictMandatoryEqual,
                        description:
                        string.Format(
                            "Parameter set {0} of cmdlet '{1}' have the same mandatory parameters, " +
                            "and both of them are not default parameter set which may cause confusion.",
                            mandatoryEqualSetNames, cmdlet.Name),
                        remediation: "Merge these parameter sets into one parameter set.");
                }
            }
        }
コード例 #26
0
        /// <summary>
        /// Compares the metadata of parameter sets with the same name for any breaking changes.
        ///
        /// Breaking changes for parameter sets include
        ///   - Removing a parameter set
        ///   - Making an optional parameter mandatory
        ///   - Changing the position of a parameter
        ///   - A parameter that previously could get its value from the pipeline no longer does
        ///   - A parameter that previously could get its value from the pipeline by property name no longer does
        ///   - A parameter has been removed from the parameter set
        /// </summary>
        /// <param name="cmdlet">Reference to the cmdlet whose parameter sets are being checked.</param>
        /// <param name="oldParameterSets">The list of parameter sets from the old (serialized) metadata.</param>
        /// <param name="newParameterSets">The list of parameter sets from the new metadata</param>
        /// <param name="issueLogger">ReportLogger that will keep track of the issues found.</param>
        public void CompareParameterSetMetadata(
            CmdletMetadata cmdlet,
            List <ParameterSetMetadata> oldParameterSets,
            List <ParameterSetMetadata> newParameterSets,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            // This dictionary will map a parameter set name to the corresponding metadata
            Dictionary <string, ParameterSetMetadata> parameterSetDictionary = new Dictionary <string, ParameterSetMetadata>();

            // Add each parameter set to the dictionary
            foreach (var newParameterSet in newParameterSets)
            {
                parameterSetDictionary.Add(newParameterSet.Name, newParameterSet);
            }

            // For each parameter set in the old metadata, see if it has been
            // added to the dictionary (exists in the new metadata)
            foreach (var oldParameterSet in oldParameterSets)
            {
                bool foundMatch = false;

                // Find matching parameter set
                foreach (var newParameterSet in newParameterSets)
                {
                    Dictionary <string, Parameter> parameterDictionary = new Dictionary <string, Parameter>();
                    foreach (var parameter in newParameterSet.Parameters)
                    {
                        parameterDictionary.Add(parameter.ParameterMetadata.Name, parameter);
                        foreach (var alias in parameter.ParameterMetadata.AliasList)
                        {
                            parameterDictionary.Add(alias, parameter);
                        }
                    }

                    // Check if set has minimum parameters required to match
                    bool minimumRequired = true;
                    foreach (var parameter in oldParameterSet.Parameters)
                    {
                        if (!parameterDictionary.ContainsKey(parameter.ParameterMetadata.Name))
                        {
                            minimumRequired = false;
                            break;
                        }
                        else
                        {
                            var newParameter = parameterDictionary[parameter.ParameterMetadata.Name];
                            if (!parameter.Mandatory && newParameter.Mandatory ||
                                parameter.Position >= 0 && parameter.Position != newParameter.Position ||
                                parameter.ValueFromPipeline && !newParameter.ValueFromPipeline ||
                                parameter.ValueFromPipelineByPropertyName && !newParameter.ValueFromPipelineByPropertyName)
                            {
                                minimumRequired = false;
                                break;
                            }
                        }
                    }

                    if (!minimumRequired)
                    {
                        continue;
                    }

                    parameterDictionary = new Dictionary <string, Parameter>();
                    foreach (var parameter in oldParameterSet.Parameters)
                    {
                        parameterDictionary.Add(parameter.ParameterMetadata.Name, parameter);
                        foreach (var alias in parameter.ParameterMetadata.AliasList)
                        {
                            parameterDictionary.Add(alias, parameter);
                        }
                    }

                    // Check if set has any additional mandatory parameters
                    bool foundAdditional = false;
                    foreach (var parameter in newParameterSet.Parameters)
                    {
                        if (parameterDictionary.ContainsKey(parameter.ParameterMetadata.Name))
                        {
                            continue;
                        }

                        if (parameter.Mandatory)
                        {
                            foundAdditional = true;
                            break;
                        }
                    }

                    if (!foundAdditional)
                    {
                        foundMatch = true;
                        break;
                    }
                }

                if (!foundMatch)
                {
                    issueLogger.LogBreakingChangeIssue(
                        cmdlet: cmdlet,
                        severity: 0,
                        problemId: ProblemIds.BreakingChangeProblemId.RemovedParameterSet,
                        description: string.Format(Properties.Resources.RemovedParameterSetDescription,
                                                   oldParameterSet.Name, cmdlet.Name),
                        remediation: string.Format(Properties.Resources.RemovedParameterSetRemediation,
                                                   oldParameterSet.Name, cmdlet.Name));
                }
            }
        }
コード例 #27
0
        /// <summary>
        /// Compare two types by recursively checking their properties and property
        /// types, making sure that nothing has been removed or changed.
        /// </summary>
        /// <param name="cmdlet">The cmdlet metadata currently being checked.</param>
        /// <param name="oldType">The type metadata from the old (serialized) assembly.</param>
        /// <param name="newType">The type metadata from the new assembly.</param>
        /// <param name="issueLogger">ReportLogger that will keep track of issues found.</param>
        public bool CompareTypeMetadata(
            CmdletMetadata cmdlet,
            TypeMetadata oldType,
            TypeMetadata newType,
            ReportLogger <BreakingChangeIssue> issueLogger)
        {
            var result = true;

            // For each property in the old assembly type, find the corresponding
            // property in the new assembly type
            foreach (var oldProperty in oldType.Properties.Keys)
            {
                if (newType.Properties.ContainsKey(oldProperty))
                {
                    var oldPropertyType = oldType.Properties[oldProperty];
                    var newPropertyType = newType.Properties[oldProperty];

                    var oldTypeMetadata = _oldTypeDictionary[oldPropertyType];
                    var newTypeMetadata = _newTypeDictionary[newPropertyType];

                    // Define variables for logging
                    int    problemId   = ProblemIds.BreakingChangeProblemId.ChangedElementType;
                    string description = string.Format(Properties.Resources.ChangedElementTypeDescription,
                                                       oldProperty, oldTypeMetadata.ElementType, newTypeMetadata.ElementType);
                    string remediation = string.Format(Properties.Resources.ChangedElementTypeRemediation,
                                                       oldProperty, oldTypeMetadata.ElementType);

                    // If the type is an array, check for any breaking changes
                    if (IsElementType(cmdlet, oldTypeMetadata, newTypeMetadata, problemId, description, remediation, issueLogger))
                    {
                        continue;
                    }

                    string target = string.Format("property {0}", oldProperty);

                    // If the type is a generic, check for any breaking changes
                    if (IsGenericType(cmdlet, oldTypeMetadata, newTypeMetadata, target, issueLogger))
                    {
                        continue;
                    }

                    // If the types are the same, compare their properties
                    if (oldPropertyType.Equals(newPropertyType, StringComparison.OrdinalIgnoreCase))
                    {
                        // If we have not previously seen this type, run this
                        // method on the type
                        if (!_typeSet.Contains(oldPropertyType))
                        {
                            _typeSet.Add(oldPropertyType);

                            CompareTypeMetadata(cmdlet, oldTypeMetadata, newTypeMetadata, issueLogger);
                        }
                    }
                    else
                    {
                        // If the type of the property has been changed, log an issue
                        issueLogger?.LogBreakingChangeIssue(
                            cmdlet: cmdlet,
                            severity: 0,
                            problemId: ProblemIds.BreakingChangeProblemId.ChangedPropertyType,
                            description: string.Format(Properties.Resources.ChangedPropertyTypeDescription,
                                                       oldProperty, oldType.Name, oldPropertyType, newPropertyType),
                            remediation: string.Format(Properties.Resources.ChangedPropertyTypeRemediation,
                                                       oldProperty, oldPropertyType));
                        result = false;
                    }
                }
                else
                {
                    // If the property has been removed, log an issue
                    issueLogger?.LogBreakingChangeIssue(
                        cmdlet: cmdlet,
                        severity: 0,
                        problemId: ProblemIds.BreakingChangeProblemId.RemovedProperty,
                        description: string.Format(Properties.Resources.RemovedPropertyDescription,
                                                   oldProperty, oldType.Name),
                        remediation: string.Format(Properties.Resources.RemovedPropertyRemediation,
                                                   oldProperty, oldType.Name));
                    result = false;
                }
            }

            return(result);
        }