diff --git a/src/InterAppConnector/Argument.cs b/src/InterAppConnector/Argument.cs index e85c204..7b2bbf7 100644 --- a/src/InterAppConnector/Argument.cs +++ b/src/InterAppConnector/Argument.cs @@ -2,6 +2,7 @@ using System.Reflection; using InterAppConnector.Attributes; using InterAppConnector.DataModels; +using System.Linq; namespace InterAppConnector { @@ -97,31 +98,6 @@ public bool HasParameterAValue(string name) return hasValue; } - /// - /// Get the first entry of the custom attribute with the type in , if exists, - /// otherwise return - /// - /// The type of the attribute to find - /// The name of the parameter - /// The first entry of the attribute with type , otherwise - public ParameterType GetCustomAttribute(string parameterName) where ParameterType : Attribute - { - ParameterType parameter = null; - - if (IsParameterDefined(parameterName)) - { - foreach (object item in _parameters[parameterName].Attributes) - { - if (item.GetType() == typeof(ParameterType) && parameter == null) - { - parameter = (ParameterType)item; - } - } - } - - return parameter; - } - /// /// Get the first alias assigned to the property or the property name if no aliases were found.
/// It returns if the property does not belong to the argument defined in
@@ -136,7 +112,7 @@ public ParameterType GetCustomAttribute(string parameterName) whe PropertyInfo? propertyFound = typeof(ArgumentType).GetProperty(propertyName); if (propertyFound != null) { - if (propertyFound.GetCustomAttributes().Count() > 0) + if (propertyFound.GetCustomAttributes().Any()) { argumentName = propertyFound.GetCustomAttributes().First().Name; } @@ -180,7 +156,7 @@ protected Argument ParseInternal(string[] arguments, string argumentPrefixes) { string argument = argumentList.Dequeue(); - /** + /* * An argument must not be a number. * However, there may be cases where you want to pass a negative number in an argument * In this case, the string is considered a value of the latest argument declared at that time @@ -189,7 +165,7 @@ protected Argument ParseInternal(string[] arguments, string argumentPrefixes) if (IsArgumentWithPrefix(argument, argumentPrefixes)) { double number; - + if (double.TryParse(argument, out number)) { if (descriptor != null && descriptor.Value == null) @@ -257,17 +233,17 @@ public static Argument Parse(dynamic arguments, string argumentPrefix) Argument result = new Argument(); List argumentList = new List(); ExpandoObject args = arguments; - foreach (KeyValuePair item in args) + foreach (var item in from KeyValuePair item in args + where item.Value is not null + select item) { - if (item.Value is not null) + argumentList.Add(argumentPrefix + item.Key); + if (item.Value is not bool) { - argumentList.Add(argumentPrefix + item.Key); - if (item.Value is not bool) - { - argumentList.Add(item.Value.ToString()); - } + argumentList.Add(item.Value.ToString()); } } + return result.ParseInternal(argumentList.ToArray(), argumentPrefix); } @@ -275,13 +251,13 @@ protected static bool IsArgumentWithPrefix(string argument, string argumentPrefi { bool isArgumentWithPrefix = false; string[] argumentPrefixList = argumentPrefixes.Split(","); - foreach (string argumentPrefix in argumentPrefixList) + foreach (var _ in from string argumentPrefix in argumentPrefixList + where argument.ToLower().StartsWith(argumentPrefix.ToLower()) + select new { }) { - if (argument.ToLower().StartsWith(argumentPrefix.ToLower())) - { - isArgumentWithPrefix = true; - } + isArgumentWithPrefix = true; } + return isArgumentWithPrefix; } } diff --git a/src/InterAppConnector/CommandManager.cs b/src/InterAppConnector/CommandManager.cs index 7986b34..fa07646 100644 --- a/src/InterAppConnector/CommandManager.cs +++ b/src/InterAppConnector/CommandManager.cs @@ -2,11 +2,10 @@ using InterAppConnector.DataModels; using InterAppConnector.Exceptions; using InterAppConnector.Interfaces; -using System.ComponentModel; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; -using System.Text.RegularExpressions; +using System.Linq; [assembly: InternalsVisibleTo("InterAppConnector.Test.Library")] namespace InterAppConnector @@ -20,6 +19,16 @@ public class CommandManager internal Dictionary _arguments = new Dictionary(); internal Dictionary _parameterObject = new Dictionary(); internal string _result = ""; + + private static ICommand? _currentCommand; + + internal static ICommand? CurrentCommand + { + get + { + return _currentCommand; + } + } internal string? GetActionKeyByActions(List actions) { @@ -29,12 +38,18 @@ where item.Value.Action.Intersect(actions).Any() return commandName; } + internal static void SetCommand(ICommand command) + { + _currentCommand = command; + } + internal CommandManager SetArguments(List actions, List argument) { string? selectedCommand = GetActionKeyByActions(actions); - if (selectedCommand != default) { + SetCommand(_commands[selectedCommand]); + object currentParameterObject = _parameterObject[selectedCommand]; foreach (ParameterDescriptor item in argument) { ParameterDescriptor? findArgument = (from value in _arguments[selectedCommand].Arguments.Values @@ -44,213 +59,42 @@ where value.Name.ToLower().Trim() == item.Name.ToLower().Trim() if (findArgument != default(ParameterDescriptor)) { - ParameterDescriptor modifiedValues = _arguments[selectedCommand].Arguments[findArgument.Name]; - Type? parameterType = Nullable.GetUnderlyingType(modifiedValues.ParameterType); - if (parameterType == null) + List distinctAttributes = RuleManager.GetDistinctAttributes(findArgument.Attributes); + List rulesToExecute = RuleManager.GetAssemblyRules(typeof(CommandManager)); + + foreach (Attribute attribute in distinctAttributes) { - parameterType = modifiedValues.ParameterType; + rulesToExecute = RuleManager.MergeRules(rulesToExecute, RuleManager.GetAssemblyRules(findArgument.ParameterType)); } - if (parameterType.IsEnum) + foreach (var rule in from IArgumentSettingRule rule in rulesToExecute + where rule.IsRuleEnabledInArgumentSetting(currentParameterObject.GetType().GetProperty(findArgument.OriginalPropertyName)!) + select rule) { /* - * Remember that a value in an enumeration may have different aliases that works - * like aliases in properties, with the main difference that refers to a value instead of - * the property. - * Aliases can indentify a value uniquely and could mask the original value name, but be careful for batch execution, where the value - * passed to the property is also the name of the property + * A default rule does not have an interface with an argument type, + * so you have to consider also this case */ - try + if (RuleManager.IsSpecializedRule(rule)) { - _arguments[selectedCommand].Arguments[findArgument.Name].Value = EnumHelper.GetEnumerationFieldByValue(parameterType, item.Value.ToString()); - _parameterObject[selectedCommand].GetType().GetProperty(findArgument.OriginalPropertyName)!.SetValue(_parameterObject[selectedCommand], EnumHelper.GetEnumerationFieldByValue(parameterType, item.Value.ToString())); - _arguments[selectedCommand].Arguments[findArgument.Name].IsSetByUser = true; - } - catch (ArgumentException exc) - { - /* - * If something went wrong with the parse of the value, throw an exception. - * You may need to change this exception with a more specialized exception, - * but the InnerException property should contain the exception raised by - * EnumHelper.GetFieldName() - */ - throw new ArgumentException("The value provided in parameter " + item.Name + " is not acceptable. Please provide a new value", item.Name, exc); - } - } - else if ((parameterType.IsClass || StructHelper.IsStruct(parameterType)) && parameterType != typeof(string)) - { - /* - * If the parameter is a value, the value is directly assigned to the parameter. - * But what about a class that receives a custom InputString as his parameter? - * Generally speaking, the command class may have only one method between the constructor that - * receive a string as argument and the Parse method - * Don't forget that there is also the case when CustomInputStringAttribute is not assigned - * or there is a custom method that needs to be used - * There should be only one and one method with this attribute, and if more methods are found that have this attribute - * raise a DuplicateObjectException exception - * A string is a class, so you have to treat it as a special object. - * What about parameters that are structs, for example in the case of Guid? Actually check only if it is a struct, - * so check if the type is a value type and it is not an enumeration and a primitive - */ + Type argumentType = rule.GetType().GetInterface(typeof(IArgumentSettingRule<>).FullName!)!.GetGenericArguments()[0]; - if (_arguments[selectedCommand].GetCustomAttribute(findArgument.Name) == null) - { - List methodsWithCustomInputStringAttribute = (from method in parameterType.GetMethods() - where method.GetCustomAttribute() != null - select method).ToList(); - if (methodsWithCustomInputStringAttribute.Count < 2) - { - if (methodsWithCustomInputStringAttribute.Count == 0) - { - // Check if there is a constructor that accepts a string as parameter - ConstructorInfo? constructor = parameterType.GetConstructor(new[] { typeof(string) }); - if (constructor != null) - { - try - { - _arguments[selectedCommand].Arguments[findArgument.Name].Value = constructor.Invoke(new[] { item.Value }); - _parameterObject[selectedCommand].GetType().GetProperty(findArgument.OriginalPropertyName)!.SetValue(_parameterObject[selectedCommand], constructor.Invoke(new[] { item.Value })); - _arguments[selectedCommand].Arguments[findArgument.Name].IsSetByUser = true; - } - catch (Exception exc) - { - throw new ArgumentException("The value provided to argument " + item.Name + " is not acceptable. Reason: " + exc.GetBaseException().Message, item.Name, exc.InnerException); - } - } - else - { - throw new MethodNotFoundException(parameterType.Name, "Cannot find a public constructor that accepts a string as parameter in class " + parameterType.Name); - } - } - else - { - if (methodsWithCustomInputStringAttribute[0].GetParameters().Length == 1) - { - if (methodsWithCustomInputStringAttribute[0].GetParameters()[0].ParameterType == typeof(string)) - { - try - { - _arguments[selectedCommand].Arguments[findArgument.Name].Value = methodsWithCustomInputStringAttribute[0].Invoke(null, new[] { item.Value })!; - _parameterObject[selectedCommand].GetType().GetProperty(findArgument.OriginalPropertyName)!.SetValue(_parameterObject[selectedCommand], methodsWithCustomInputStringAttribute[0].Invoke(null, new[] { item.Value })); - _arguments[selectedCommand].Arguments[findArgument.Name].IsSetByUser = true; - } - catch (Exception exc) - { - throw new ArgumentException("The value provided to argument " + item.Name + " is not acceptable. Reason: " + exc.GetBaseException().Message, item.Name, exc.InnerException); - } - } - else - { - throw new TypeMismatchException(typeof(string).FullName!, methodsWithCustomInputStringAttribute[0].GetParameters()[0].ParameterType.FullName!, "", "Error in argument definition. It is expected a string, but the actual type is " + methodsWithCustomInputStringAttribute[0].GetParameters()[0].ParameterType.FullName); - } - } - else - { - throw new TypeMismatchException(typeof(string).FullName!, "More than 1 parameter", "", "Error in argument definition. It is expected one parameter of type string, but the method has multiple parameters."); - } - } - } - else - { - throw new DuplicateObjectException(methodsWithCustomInputStringAttribute[1].Name, new List { "[CustomInputString] attribute" }, methodsWithCustomInputStringAttribute[0].Name, "Error in class " + parameterType.Name + ". It is expected that only one method may have the [CustomInputString] attribute, but there are " + methodsWithCustomInputStringAttribute.Count + " methods with this attribute"); - } - } - else - { - if (string.IsNullOrEmpty(_arguments[selectedCommand].GetCustomAttribute(findArgument.Name).StringFormat)) - { - try - { - // Do not use the ParseExact method. Use the constructor with the parameter as value - _arguments[selectedCommand].Arguments[findArgument.Name].Value = parameterType.GetConstructor(new[] { typeof(string) })!.Invoke(new[] { item.Value }); - _parameterObject[selectedCommand].GetType().GetProperty(findArgument.OriginalPropertyName)!.SetValue(_parameterObject[selectedCommand], parameterType.GetConstructor(new[] { typeof(string) })!.Invoke(new[] { item.Value })); - _arguments[selectedCommand].Arguments[findArgument.Name].IsSetByUser = true; - } - catch (Exception exc) - { - throw new ArgumentException("The value provided to argument " + item.Name + " is not acceptable. Reason: " + exc.GetBaseException().Message, item.Name, exc.InnerException); - } - } - else - { - try - { - // Use ParseExact method - _arguments[selectedCommand].Arguments[findArgument.Name].Value = parameterType.GetMethod("ParseExact", new[] {typeof(string), typeof(string), typeof(IFormatProvider)})!.Invoke(null, new[] { item.Value, _arguments[selectedCommand].GetCustomAttribute(findArgument.Name).StringFormat, CultureInfo.InvariantCulture })!; - _parameterObject[selectedCommand].GetType().GetProperty(findArgument.OriginalPropertyName)!.SetValue(_parameterObject[selectedCommand], parameterType.GetMethod("ParseExact", new[] { typeof(string), typeof(string), typeof(IFormatProvider) })!.Invoke(null, new[] { item.Value, _parameterObject[selectedCommand].GetType().GetProperty(findArgument.OriginalPropertyName)!.GetCustomAttribute()!.StringFormat, CultureInfo.InvariantCulture })); - _arguments[selectedCommand].Arguments[findArgument.Name].IsSetByUser = true; - } - catch (Exception exc) - { - throw new ArgumentException("The value provided to argument " + item.Name + " is not acceptable. Reason: " + exc.GetBaseException().Message, item.Name, exc.InnerException); - } - } - } - } - else - { - try - { - /* - * During the parse of the arguments, if an argument is passed without a value is considered a switch - * This is not always true, especially if the argument is placed as last token in the argument definition - * So if the type of the argument is not a boolean, you have to specify a value explicitly - */ - if (parameterType != typeof(bool) && item.Value.GetType() != typeof(bool)) - { - _arguments[selectedCommand].Arguments[findArgument.Name].Value = Convert.ChangeType(item.Value, parameterType); - _parameterObject[selectedCommand].GetType().GetProperty(findArgument.OriginalPropertyName)!.SetValue(_parameterObject[selectedCommand], Convert.ChangeType(item.Value, parameterType)); - _arguments[selectedCommand].Arguments[findArgument.Name].IsSetByUser = true; - } - else - { - if (parameterType == typeof(bool)) - { - _arguments[selectedCommand].Arguments[findArgument.Name].Value = Convert.ChangeType(item.Value, parameterType); - _parameterObject[selectedCommand].GetType().GetProperty(findArgument.OriginalPropertyName)!.SetValue(_parameterObject[selectedCommand], Convert.ChangeType(item.Value, parameterType)); - _arguments[selectedCommand].Arguments[findArgument.Name].IsSetByUser = true; - } - else - { - throw new ArgumentException(); - } - } - } - catch (Exception exc) - { - throw new ArgumentException("The value provided to argument " + item.Name + " is not acceptable. Please provide a valid value", item.Name, exc); - } - } - - if (_arguments[selectedCommand].GetCustomAttribute(findArgument.Name) != null) - { - ValueValidatorAttribute validator = _arguments[selectedCommand].GetCustomAttribute(findArgument.Name); - - if (validator.ValueValidatorType.GetInterface(typeof(IValueValidator).Name) != null) - { - IValueValidator valueValidator = (IValueValidator) Activator.CreateInstance(validator.ValueValidatorType)!; - bool customValidationErrorMessageMissing = false; + Attribute? attributeExists = (from distinctAttribute in distinctAttributes + where distinctAttribute.GetType() == argumentType + select distinctAttribute).FirstOrDefault(); - try + if (attributeExists != default(Attribute)) { - if (!valueValidator.ValidateValue(_arguments[selectedCommand].Arguments[findArgument.Name].Value)) - { - customValidationErrorMessageMissing = true; - } + findArgument = rule.SetArgumentValueIfTypeExists(currentParameterObject, currentParameterObject.GetType().GetProperty(findArgument.OriginalPropertyName)!, findArgument, item); } - catch(Exception exc) - { - throw new ArgumentException("The value provided to argument " + item.Name + " is not acceptable. Reason: " + exc.GetBaseException().Message, item.Name, exc.InnerException); - } - - if (customValidationErrorMessageMissing) + else { - throw new ArgumentException("The value provided to argument " + item.Name + " is not valid according to the validation procedure"); + findArgument = rule.SetArgumentValueIfTypeDoesNotExist(currentParameterObject, currentParameterObject.GetType().GetProperty(findArgument.OriginalPropertyName)!, findArgument, item); } } else { - throw new TypeMismatchException(typeof(IValueValidator).Name, "", "", validator.ValueValidatorType.Name + " doesn't have an interface of type " + typeof(IValueValidator).Name); + findArgument = rule.SetArgumentValueIfTypeExists(currentParameterObject, currentParameterObject.GetType().GetProperty(findArgument.OriginalPropertyName)!, findArgument, item); } } } @@ -287,12 +131,12 @@ where method.GetCustomAttribute() != null throw new MultipleCommandNotAllowedException(command.GetType().FullName!, command.GetType().FullName + " has multiple ICommand class implemented. An action may have only one ICommand implementation"); } - string className = command.GetType().FullName; + string className = command.GetType().FullName!; if (!_commands.ContainsKey(className)) { _commands.Add(className, command); _parameterObject.Add(className, parameterObject); - + SetCommand(command); Argument argument = new Argument(); if (command.GetType().GetCustomAttribute() != null) @@ -315,112 +159,62 @@ where string.Join(" ", item.Value.Action) == string.Join(" ", argument.Action) foreach (PropertyInfo parameterProperty in parameterObject.GetType().GetProperties()) { - if (parameterProperty.GetCustomAttribute() == null) - { - ParameterDescriptor descriptor = new ParameterDescriptor(); - descriptor.OriginalPropertyName = parameterProperty.Name; + List distinctAttributes = RuleManager.GetDistinctAttributes(parameterProperty); + ParameterDescriptor descriptor = new ParameterDescriptor(); + List rulesToExecute = RuleManager.GetAssemblyRules(typeof(CommandManager)); - Type? parameterType = Nullable.GetUnderlyingType(parameterProperty.PropertyType); + foreach (Attribute attribute in distinctAttributes) + { + rulesToExecute = RuleManager.MergeRules(rulesToExecute, RuleManager.GetAssemblyRules(parameterProperty.PropertyType)); + } - if (parameterType == null) - { - descriptor.ParameterType = parameterProperty.PropertyType; - } - else + foreach (var rule in from IArgumentDefinitionRule rule in rulesToExecute + where rule.IsRuleEnabledInArgumentDefinition(parameterProperty) + select rule) + { + if (RuleManager.IsSpecializedRule(rule)) { - descriptor.ParameterType = parameterType; - } + Type argumentType = rule.GetType().GetInterface(typeof(IArgumentDefinitionRule<>).FullName!)!.GetGenericArguments()[0]; - List attributes = new List(parameterProperty.GetCustomAttributes()); + Attribute? attributeExists = (from item in distinctAttributes + where item.GetType() == argumentType + select item).FirstOrDefault(); - if (parameterProperty.GetCustomAttributes().Count() > 0) - { - foreach (MandatoryForCommandAttribute propertyAttribute in parameterProperty.GetCustomAttributes()) + if (attributeExists != default(Attribute)) { - if (propertyAttribute.Command == command.GetType()) - { - descriptor.IsMandatory = true; - attributes.Remove(propertyAttribute); - } - } - } - else - { - if (parameterProperty.PropertyType.IsValueType) - { - if (Nullable.GetUnderlyingType(parameterProperty.PropertyType) == null) - { - descriptor.IsMandatory = true; - } + descriptor = rule.DefineArgumentIfTypeExists(parameterObject, parameterProperty, descriptor); } else { - if (parameterProperty.GetValue(parameterObject) != null) - { - parameterProperty.SetValue(parameterObject, null); - descriptor.IsMandatory = true; - } - } - } - - if (parameterProperty.GetCustomAttributes().Count() > 0) - { - foreach (AliasAttribute propertyAttribute in parameterProperty.GetCustomAttributes()) - { - if (Regex.IsMatch(propertyAttribute.Name, @"^[A-Za-z0-9-]+$", RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1))) - { - if (!descriptor.Aliases.Contains(propertyAttribute.Name.ToLower().Trim())) - { - descriptor.Aliases.Add(propertyAttribute.Name.ToLower().Trim()); - attributes.Remove(propertyAttribute); - } - } - else - { - throw new ArgumentException("Invalid string found in " + parameterProperty.Name + ". Alias must contain only alphanumeric characters and hyphens (-). Null values or empty string are also not allowed"); - } + descriptor = rule.DefineArgumentIfTypeDoesNotExist(parameterObject, parameterProperty, descriptor); } - - descriptor.Name = parameterProperty.GetCustomAttributes().First().Name.ToLower().Trim(); } else { - descriptor.Name = parameterProperty.Name.ToLower().Trim(); - } - - KeyValuePair findIfAliasAlreadyExists = (from item in argument.Arguments - where item.Value.Aliases.Intersect(descriptor.Aliases).Any() - select new KeyValuePair(item.Key, item.Value)).FirstOrDefault(); - - if (!findIfAliasAlreadyExists.Equals(Activator.CreateInstance(findIfAliasAlreadyExists.GetType()))) - { - string parametersAlreadyDefined = string.Join(", ", findIfAliasAlreadyExists.Value.Aliases.Intersect(descriptor.Aliases)); - throw new DuplicateObjectException(parameterProperty.Name, findIfAliasAlreadyExists.Value.Aliases.Intersect(descriptor.Aliases).ToList(), findIfAliasAlreadyExists.Value.OriginalPropertyName, "The property " + parameterProperty.Name + " has some alias that have already defined in " + findIfAliasAlreadyExists.Value.OriginalPropertyName + ". Duplicate aliases: " + parametersAlreadyDefined); - } - - if (parameterProperty.GetCustomAttribute() != null) - { - descriptor.Description = parameterProperty.GetCustomAttribute()!.Description; - attributes.Remove(parameterProperty.GetCustomAttribute()!); + descriptor = rule.DefineArgumentIfTypeExists(parameterObject, parameterProperty, descriptor); } + } - descriptor.Value = parameterProperty.GetValue(parameterObject)!; - descriptor.Attributes = attributes; + KeyValuePair findIfAliasAlreadyExists = (from item in argument.Arguments + where item.Value.Aliases.Intersect(descriptor.Aliases).Any() + select new KeyValuePair(item.Key, item.Value)).FirstOrDefault(); - if (includePropertyNameInAliases) - { - if (descriptor.Aliases.IndexOf(parameterProperty.Name.ToLower().Trim()) < 0) - { - descriptor.Aliases.Add(parameterProperty.Name.ToLower().Trim()); - } - } + if (!findIfAliasAlreadyExists.Equals(Activator.CreateInstance(findIfAliasAlreadyExists.GetType()))) + { + string parametersAlreadyDefined = string.Join(", ", findIfAliasAlreadyExists.Value.Aliases.Intersect(descriptor.Aliases)); + throw new DuplicateObjectException(parameterProperty.Name, findIfAliasAlreadyExists.Value.Aliases.Intersect(descriptor.Aliases).ToList(), findIfAliasAlreadyExists.Value.OriginalPropertyName, "The property " + parameterProperty.Name + " has some alias that have already defined in " + findIfAliasAlreadyExists.Value.OriginalPropertyName + ". Duplicate aliases: " + parametersAlreadyDefined); + } - if (parameterProperty.GetCustomAttribute() != null - && string.IsNullOrEmpty(parameterProperty.GetCustomAttribute()!.ExampleValue)) + if (includePropertyNameInAliases) + { + if (descriptor.Aliases.IndexOf(parameterProperty.Name.ToLower().Trim()) < 0) { - throw new ArgumentException("The example value cannot be null or empty", parameterProperty.Name); + descriptor.Aliases.Add(parameterProperty.Name.ToLower().Trim()); } + } + if (!string.IsNullOrEmpty(descriptor.Name)) + { argument.Arguments.Add(descriptor.Name, descriptor); } } diff --git a/src/InterAppConnector/CommandUtil.cs b/src/InterAppConnector/CommandUtil.cs index 8eebc3b..ca07242 100644 --- a/src/InterAppConnector/CommandUtil.cs +++ b/src/InterAppConnector/CommandUtil.cs @@ -19,7 +19,7 @@ public class CommandUtil /// public static uint MaximumObjectDepth { get; set; } = 64; - internal static string DescribeCommands(CommandManager commandManager, bool writeHeader = true, bool simulateConsoleException = false) + internal static string DescribeCommands(CommandManager CommandManager, bool writeHeader = true, bool simulateConsoleException = false) { StringBuilder description = new StringBuilder(); int lineLength; @@ -50,9 +50,9 @@ internal static string DescribeCommands(CommandManager commandManager, bool writ { description.AppendLine(DrawLine(lineLength)); description.AppendLine(); - description.Append(Assembly.GetEntryAssembly().GetName().Name); + description.Append(Assembly.GetEntryAssembly()!.GetName().Name); description.Append(" "); - description.AppendLine(Assembly.GetEntryAssembly().GetName().Version.ToString()); + description.AppendLine(Assembly.GetEntryAssembly()!.GetName().Version!.ToString()); description.AppendLine(); description.AppendLine(DrawLine(lineLength)); } @@ -60,19 +60,19 @@ internal static string DescribeCommands(CommandManager commandManager, bool writ description.AppendLine("Available actions:"); description.AppendLine(); - foreach (KeyValuePair item in commandManager._arguments) + foreach (KeyValuePair item in CommandManager._arguments) { string action; string actionDescription = "No description provided"; - if (commandManager._commands[item.Key].GetType().GetCustomAttribute() != null) + if (CommandManager._commands[item.Key].GetType().GetCustomAttribute() != null) { - action = commandManager._commands[item.Key].GetType().GetCustomAttribute().Name; - actionDescription = commandManager._commands[item.Key].GetType().GetCustomAttribute().Description; + action = CommandManager._commands[item.Key].GetType().GetCustomAttribute().Name; + actionDescription = CommandManager._commands[item.Key].GetType().GetCustomAttribute().Description; } else { - action = commandManager._commands[item.Key].GetType().Name.ToLower().Trim(); + action = CommandManager._commands[item.Key].GetType().Name.ToLower().Trim(); } description.AppendLine(DescribeAction(action, actionDescription, item.Value.Arguments.Values.ToList())); @@ -113,15 +113,7 @@ internal static string DescribeAction(string action, string actionDescription, L description.Append(" (optional) : "); } - if (!string.IsNullOrEmpty(descriptor.Description)) - { - description.AppendLine(descriptor.Description); - } - else - { - description.AppendLine("No description provided"); - } - + description.AppendLine(descriptor.Description); // for enumerations, describe the numbers and values that a parameter may have if (descriptor.ParameterType.IsEnum) @@ -136,15 +128,7 @@ internal static string DescribeAction(string action, string actionDescription, L description.Append("("); description.Append(possibleValue.Value); description.Append(") : "); - if (string.IsNullOrEmpty(possibleValue.Description)) - { - description.Append("No description provided"); - } - else - { - description.Append(possibleValue.Description); - } - + description.Append(possibleValue.Description); description.AppendLine(); if (possibleValue.Aliases.Count > 1) @@ -168,7 +152,7 @@ internal static string DescribeAction(string action, string actionDescription, L } else { - /** + /* * For other types, we have three cases: * - no validators are defined * - no examples are defined diff --git a/src/InterAppConnector/EnumHelper.cs b/src/InterAppConnector/EnumHelper.cs index 0683cfd..0db9f0c 100644 --- a/src/InterAppConnector/EnumHelper.cs +++ b/src/InterAppConnector/EnumHelper.cs @@ -1,9 +1,7 @@ -using InterAppConnector.Attributes; -using InterAppConnector.DataModels; +using InterAppConnector.DataModels; using InterAppConnector.Exceptions; -using System.ComponentModel; +using InterAppConnector.Interfaces; using System.Reflection; -using System.Text.RegularExpressions; namespace InterAppConnector { @@ -13,62 +11,56 @@ public class EnumHelper internal void LoadEnumerationValues() where EnumType : Enum { - foreach (FieldInfo item in typeof(EnumType).GetFields()) + Type argumentBaseType = typeof(EnumType); + foreach (FieldInfo item in argumentBaseType.GetFields()) { // take only the value defined by the user if (!item.IsSpecialName) { - if (item.GetCustomAttribute() == null) + List distinctAttributes = RuleManager.GetDistinctAttributes(item); + ParameterDescriptor descriptor = new ParameterDescriptor(); + List rulesToExecute = RuleManager.GetAssemblyRules(typeof(CommandManager)); + + foreach (Attribute attribute in distinctAttributes) { - ParameterDescriptor descriptor = new ParameterDescriptor(); - descriptor.OriginalPropertyName = item.Name; + rulesToExecute = RuleManager.MergeRules(rulesToExecute, RuleManager.GetAssemblyRules(item.DeclaringType!)); + } - if (item.GetCustomAttributes().Count() > 0) + foreach (var rule in from IArgumentDefinitionRule rule in rulesToExecute + where rule.IsRuleEnabledInArgumentDefinition(item) + select rule) + { + try { - foreach (AliasAttribute alias in item.GetCustomAttributes()) + Type argumentType = rule.GetType().GetInterface(typeof(IArgumentDefinitionRule<>).FullName!)!.GetGenericArguments()[0]; + + Attribute? attributeExists = (from attributeItem in distinctAttributes + where attributeItem.GetType() == argumentType + select attributeItem).FirstOrDefault(); + + if (attributeExists != default(Attribute)) { - if (!string.IsNullOrWhiteSpace(alias.Name)) - { - double number; - if (!double.TryParse(alias.Name.ToLower().Trim(), out number)) - { - if (Regex.IsMatch(alias.Name.ToLower().Trim(), @"^[A-Za-z0-9-]+$", RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1))) - { - descriptor.Aliases.Add(alias.Name.ToLower().Trim()); - } - else - { - throw new ArgumentException("Invalid string found in " + alias.Name + ". Alias must contain only alphanumeric characters and hyphens (-). Null values, empty string and numbers are also not allowed"); - } - } - else - { - throw new ArgumentException("Invalid alias found in " + item.Name + ". The alias cannot be null, an empty string or a number", item.Name); - } - } - else - { - throw new ArgumentException("Invalid alias found in " + item.Name + ". The alias cannot be null, an empty string or a number", item.Name); - } + descriptor = rule.DefineArgumentIfTypeExists(argumentBaseType, item, descriptor); + } + else + { + descriptor = rule.DefineArgumentIfTypeDoesNotExist(argumentBaseType, item, descriptor); } - - descriptor.Name = descriptor.Aliases[0].ToLower().Trim(); - //descriptor.Aliases.RemoveAt(0); - } - else - { - descriptor.Name = item.Name.ToLower().Trim(); } - - if (item.GetCustomAttribute() != null) + catch { - descriptor.Description = item.GetCustomAttribute().Description; + /* + * A default rule does not have an interface with an argument type, + * so you have to consider also this case + */ + descriptor = rule.DefineArgumentIfTypeExists(argumentBaseType, item, descriptor); } + } - descriptor.Value = (int)item.GetValue(typeof(EnumType)); - + if (!string.IsNullOrEmpty(descriptor.Name)) + { _parameters.Add(item.Name.ToLower().Trim(), descriptor); - } + } } } } @@ -84,33 +76,9 @@ internal void LoadEnumerationValues(Type enumType) * Moreover, as the LoadEnumerationValues is internal to this class and it is not public, * it is necessary the BindingFlags attributes in order to invoke the method */ - typeof(EnumHelper).GetMethod("LoadEnumerationValues", BindingFlags.Instance | BindingFlags.NonPublic, Type.EmptyTypes).MakeGenericMethod(enumType).Invoke(this, null); - } - catch (Exception exc) - { - throw exc.InnerException; - } - } - else - { - throw new TypeMismatchException(typeof(Enum).FullName, enumType.GetType().FullName, null, enumType.GetType().FullName + " is not an enumeration"); - } - } - - /// - /// Get the enumeration field associated with the value - /// - /// The enum type - /// The value to search in the enumeration - /// The enumeration value - /// Exception raised when the type chosen is not an enumeration - public static object GetEnumerationFieldByValue(Type enumType, string value) - { - if (enumType.IsEnum) - { - try - { - return typeof(EnumHelper).GetMethod("GetEnumerationFieldByValue", 1, new[] { typeof(string) }).MakeGenericMethod(enumType).Invoke(null, new object[] { value }); + #pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields + typeof(EnumHelper).GetMethod("LoadEnumerationValues", BindingFlags.Instance | BindingFlags.NonPublic, Type.EmptyTypes)!.MakeGenericMethod(enumType).Invoke(this, null); + #pragma warning restore S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields } catch (Exception exc) { @@ -119,7 +87,7 @@ public static object GetEnumerationFieldByValue(Type enumType, string value) } else { - throw new TypeMismatchException(typeof(Enum).FullName, enumType.GetType().FullName, null, enumType.GetType().FullName + " is not an enumeration"); + throw new TypeMismatchException(typeof(Enum).FullName!, enumType.FullName!, null!, enumType.FullName + " is not an enumeration"); } } @@ -152,6 +120,32 @@ public static string GetFieldName(string fieldName) where EnumType : E return enumValue; } + /// + /// Get the enumeration field associated with the value + /// + /// The enum type + /// The value to search in the enumeration + /// The enumeration value + /// Exception raised when the type chosen is not an enumeration + public static object GetEnumerationFieldByValue(Type enumType, string value) + { + if (enumType.IsEnum) + { + try + { + return typeof(EnumHelper).GetMethod("GetEnumerationFieldByValue", 1, new[] { typeof(string) })!.MakeGenericMethod(enumType).Invoke(null, new object[] { value })!; + } + catch (Exception exc) + { + throw exc.InnerException!; + } + } + else + { + throw new TypeMismatchException(typeof(Enum).FullName!, enumType.FullName!, null!, enumType.FullName + " is not an enumeration"); + } + } + /// /// Get the enumeration value by searching for his value and alias /// @@ -165,7 +159,6 @@ public static EnumType GetEnumerationFieldByValue(string value) where helper.LoadEnumerationValues(); int parsedNumber; - object enumType; string stringToEvaluate = ""; foreach (ParameterDescriptor descriptor in helper._parameters.Values) @@ -174,7 +167,7 @@ public static EnumType GetEnumerationFieldByValue(string value) where { if ((int) descriptor.Value == parsedNumber) { - stringToEvaluate += parsedNumber; + stringToEvaluate = parsedNumber.ToString(); } } else @@ -198,14 +191,6 @@ public static EnumType GetEnumerationFieldByValue(string value) where if (!string.IsNullOrEmpty(stringToEvaluate)) { - /*if (Enum.TryParse(typeof(EnumType), stringToEvaluate, true, out enumType)) - { - if (Enum.IsDefined(typeof(EnumType), enumType)) - { - return (EnumType)enumType; - } - }*/ - /** * The checks and the validations done might be enough to ensure that * the value belongs to the enumeration type, so the Parse method should not diff --git a/src/InterAppConnector/Interfaces/IArgumentDefinitionRule.cs b/src/InterAppConnector/Interfaces/IArgumentDefinitionRule.cs new file mode 100644 index 0000000..66fc4fe --- /dev/null +++ b/src/InterAppConnector/Interfaces/IArgumentDefinitionRule.cs @@ -0,0 +1,14 @@ +namespace InterAppConnector.Interfaces +{ + /// + /// Interface that can be used in order to define rules for a particular attribute or object + /// + /// The type + public interface IArgumentDefinitionRule : IArgumentDefinitionRule + { + virtual bool ReturnTrue(RuleType type) + { + return true; + } + } +} diff --git a/src/InterAppConnector/Interfaces/IArgumentDefinitionRuleBase.cs b/src/InterAppConnector/Interfaces/IArgumentDefinitionRuleBase.cs new file mode 100644 index 0000000..6753088 --- /dev/null +++ b/src/InterAppConnector/Interfaces/IArgumentDefinitionRuleBase.cs @@ -0,0 +1,58 @@ +using InterAppConnector.DataModels; +using System.Reflection; + +namespace InterAppConnector.Interfaces +{ + public interface IArgumentDefinitionRule + { + /// + /// + /// + /// + /// + /// + /// + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, FieldInfo field, ParameterDescriptor descriptor); + + /// + /// + /// + /// + /// + /// + /// + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor descriptor); + + /// + /// + /// + /// + /// + /// + /// + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor descriptor); + + /// + /// + /// + /// + /// + /// + /// + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor descriptor); + + /// + /// Define if the rule should be executed or not + /// + /// + /// + public bool IsRuleEnabledInArgumentDefinition(PropertyInfo property); + + /// + /// Define if the rule should be executed or not + /// + /// + /// + public bool IsRuleEnabledInArgumentDefinition(FieldInfo field); + } +} diff --git a/src/InterAppConnector/Interfaces/IArgumentSettingRule.cs b/src/InterAppConnector/Interfaces/IArgumentSettingRule.cs new file mode 100644 index 0000000..5f9ba4f --- /dev/null +++ b/src/InterAppConnector/Interfaces/IArgumentSettingRule.cs @@ -0,0 +1,10 @@ +namespace InterAppConnector.Interfaces +{ + public interface IArgumentSettingRule : IArgumentSettingRule + { + virtual bool ReturnTrue(RuleType type) + { + return true; + } + } +} diff --git a/src/InterAppConnector/Interfaces/IArgumentSettingRuleBase.cs b/src/InterAppConnector/Interfaces/IArgumentSettingRuleBase.cs new file mode 100644 index 0000000..f4a90c5 --- /dev/null +++ b/src/InterAppConnector/Interfaces/IArgumentSettingRuleBase.cs @@ -0,0 +1,63 @@ +using InterAppConnector.DataModels; +using System.Reflection; + +namespace InterAppConnector.Interfaces +{ + public interface IArgumentSettingRule + { + /// + /// + /// + /// + /// + /// + /// + /// + public ParameterDescriptor SetArgumentValueIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor); + + + /// + /// + /// + /// + /// + /// + /// + /// + public ParameterDescriptor SetArgumentValueIfTypeExists(object parentObject, FieldInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor); + + /// + /// + /// + /// + /// + /// + /// + /// + public ParameterDescriptor SetArgumentValueIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor); + + /// + /// + /// + /// + /// + /// + /// + /// + public ParameterDescriptor SetArgumentValueIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor); + + /// + /// + /// + /// + /// + public bool IsRuleEnabledInArgumentSetting(PropertyInfo property); + + /// + /// + /// + /// + /// + public bool IsRuleEnabledInArgumentSetting(FieldInfo field); + } +} diff --git a/src/InterAppConnector/RuleManager.cs b/src/InterAppConnector/RuleManager.cs new file mode 100644 index 0000000..34c3093 --- /dev/null +++ b/src/InterAppConnector/RuleManager.cs @@ -0,0 +1,102 @@ +using InterAppConnector.Interfaces; +using System.Reflection; + +namespace InterAppConnector +{ + public static class RuleManager + { + internal static List GetDistinctAttributes(PropertyInfo property) + { + List attributes = new List(); + + foreach (Attribute attribute in property.GetCustomAttributes()) + { + Attribute? findAttribute = (from item in attributes + where item.GetType().FullName == attribute.GetType().FullName + select item).FirstOrDefault(); + + if (findAttribute == default(Attribute)) + { + attributes.Add(attribute); + } + } + + return attributes; + } + + internal static List GetDistinctAttributes(FieldInfo property) + { + List attributes = new List(); + + foreach (Attribute attribute in property.GetCustomAttributes()) + { + Attribute? findAttribute = (from item in attributes + where item.GetType().FullName == attribute.GetType().FullName + select item).FirstOrDefault(); + + if (findAttribute == default(Attribute)) + { + attributes.Add(attribute); + } + } + + return attributes; + } + + internal static List GetDistinctAttributes(List rawAttributeList) + { + List attributes = new List(); + + foreach (Attribute attribute in rawAttributeList) + { + Attribute? findAttribute = (from item in attributes + where item.GetType() == attribute.GetType() + select item).FirstOrDefault(); + + if (findAttribute == default(Attribute)) + { + attributes.Add(attribute); + } + } + + return attributes; + } + + internal static List GetAssemblyRules(Type assemblyType) + { + List rules = new List(); + + foreach (Type currentType in assemblyType.Assembly.ExportedTypes) + { + if (currentType.GetInterface(typeof(RuleType).FullName!) != null + && !currentType.ContainsGenericParameters && !currentType.IsInterface) + { + rules.Add((RuleType)Activator.CreateInstance(currentType)!); + } + } + + return rules; + } + + internal static List MergeRules(List initialRules, List rulesToMerge) + { + List resultingRules = initialRules; + + List additionalRules = (from item in rulesToMerge + where !resultingRules.Contains(item) + select item).ToList(); + + return resultingRules.Union(additionalRules).ToList(); + } + + internal static bool IsSpecializedRule(IArgumentDefinitionRule rule) + { + return (from item in rule.GetType().GetInterfaces() where item.FullName!.StartsWith(typeof(IArgumentDefinitionRule<>).FullName!) select item).Any(); + } + + internal static bool IsSpecializedRule(IArgumentSettingRule rule) + { + return (from item in rule.GetType().GetInterfaces() where item.FullName!.StartsWith(typeof(IArgumentSettingRule<>).FullName!) select item).Any(); + } + } +} diff --git a/src/InterAppConnector/Rules/AliasRule.cs b/src/InterAppConnector/Rules/AliasRule.cs new file mode 100644 index 0000000..baef4e9 --- /dev/null +++ b/src/InterAppConnector/Rules/AliasRule.cs @@ -0,0 +1,98 @@ +using InterAppConnector.Attributes; +using InterAppConnector.DataModels; +using InterAppConnector.Interfaces; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace InterAppConnector.Rules +{ + public class AliasRule : IArgumentDefinitionRule + { + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + descriptor.Name = property.Name.ToLower().Trim(); + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor descriptor) + { + descriptor.Name = property.Name.ToLower().Trim(); + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + foreach (string name in property.GetCustomAttributes().Select(x => x.Name)) + { + if (Regex.IsMatch(name, @"^[A-Za-z0-9-]+$", RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1))) + { + if (!descriptor.Aliases.Contains(name.ToLower().Trim())) + { + descriptor.Aliases.Add(name.ToLower().Trim()); + } + } + else + { + throw new ArgumentException("Invalid string found in " + property.Name + ". Alias must contain only alphanumeric characters and hyphens (-). Null values or empty string are also not allowed"); + } + } + + descriptor.Name = property.GetCustomAttributes().First().Name.ToLower().Trim(); + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, FieldInfo field, ParameterDescriptor descriptor) + { + foreach (string name in field.GetCustomAttributes().Select(x => x.Name)) + { + if (!string.IsNullOrWhiteSpace(name)) + { + double number; + if (!double.TryParse(name.ToLower().Trim(), out number) + && Regex.IsMatch(name.ToLower().Trim(), @"^[A-Za-z0-9-]+$", RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1))) + { + if (!descriptor.Aliases.Contains(name.ToLower().Trim())) + { + descriptor.Aliases.Add(name.ToLower().Trim()); + } + } + else + { + throw new ArgumentException("Invalid alias found in " + field.Name + ". The alias must contain alphanumerical characters and hypens. It cannot be null, an empty string or a number", field.Name); + } + } + else + { + throw new ArgumentException("Invalid alias found in " + field.Name + ". The alias cannot be null, an empty string or a number", field.Name); + } + } + + descriptor.Name = descriptor.Aliases[0].ToLower().Trim(); + return descriptor; + } + + public bool IsRuleEnabledInArgumentDefinition(PropertyInfo property) + { + bool isRuleEnabled = true; + + if (property.GetCustomAttribute() != null) + { + isRuleEnabled = false; + } + + return isRuleEnabled; + } + + public bool IsRuleEnabledInArgumentDefinition(FieldInfo field) + { + bool isRuleEnabled = true; + + if (field.GetCustomAttribute() != null) + { + isRuleEnabled = false; + } + + return isRuleEnabled; + } + } +} diff --git a/src/InterAppConnector/Rules/CustomInputStringRule.cs b/src/InterAppConnector/Rules/CustomInputStringRule.cs new file mode 100644 index 0000000..650bf20 --- /dev/null +++ b/src/InterAppConnector/Rules/CustomInputStringRule.cs @@ -0,0 +1,138 @@ +using InterAppConnector.Attributes; +using InterAppConnector.DataModels; +using InterAppConnector.Exceptions; +using InterAppConnector.Interfaces; +using System.Globalization; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace InterAppConnector.Rules +{ + public class CustomInputStringRule : IArgumentSettingRule + { + public bool IsRuleEnabledInArgumentSetting(PropertyInfo property) + { + /* + * If the parameter is a value, the value is directly assigned to the parameter. + * But what about a class that receives a custom InputString as his parameter? + * Generally speaking, the command class may have only one method between the constructor that + * receive a string as argument and the Parse method + * Don't forget that there is also the case when CustomInputStringAttribute is not assigned + * or there is a custom method that needs to be used + * There should be only one and one method with this attribute, and if more methods are found that have this attribute + * raise a DuplicateObjectException exception + * A string is a class, so you have to treat it as a special object. + * What about parameters that are structs, for example in the case of Guid? Actually check only if it is a struct, + * so check if the type is a value type and it is not an enumeration and a primitive + */ + Type? parameterType = Nullable.GetUnderlyingType(property.PropertyType); + if (parameterType == null) + { + parameterType = property.PropertyType; + } + return (parameterType.IsClass || StructHelper.IsStruct(parameterType)) && parameterType != typeof(string); + } + + public bool IsRuleEnabledInArgumentSetting(FieldInfo field) + { + return false; + } + + public ParameterDescriptor SetArgumentValueIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + return argumentDescriptor; + } + + public ParameterDescriptor SetArgumentValueIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + List methodsWithCustomInputStringAttribute = (from method in argumentDescriptor.ParameterType.GetMethods() + where method.GetCustomAttribute() != null + select method).ToList(); + switch (methodsWithCustomInputStringAttribute.Count) + { + case 0: + // Check if there is a constructor that accepts a string as parameter + ConstructorInfo? constructor = argumentDescriptor.ParameterType.GetConstructor(new[] { typeof(string) }); + if (constructor != null) + { + try + { + argumentDescriptor.Value = constructor.Invoke(new[] { userValueDescriptor.Value }); + property.SetValue(parentObject, argumentDescriptor.Value); + argumentDescriptor.IsSetByUser = true; + } + catch (Exception exc) + { + throw new ArgumentException("The value provided to argument " + userValueDescriptor.Name + " is not acceptable. Reason: " + exc.GetBaseException().Message, userValueDescriptor.Name, exc.InnerException); + } + } + else + { + throw new MethodNotFoundException(property.PropertyType.Name, "Cannot find a public constructor that accepts a string as parameter in class " + property.PropertyType.Name); + } + break; + case 1: + if (methodsWithCustomInputStringAttribute[0].GetParameters().Length != 1) + { + throw new TypeMismatchException(typeof(string).FullName!, "More than 1 parameter", "", "Error in argument definition. It is expected one parameter of type string, but the method has multiple parameters."); + } + + if (methodsWithCustomInputStringAttribute[0].GetParameters()[0].ParameterType == typeof(string)) + { + try + { + argumentDescriptor.Value = methodsWithCustomInputStringAttribute[0].Invoke(null, new[] { userValueDescriptor.Value })!; + property.SetValue(parentObject, argumentDescriptor.Value); + argumentDescriptor.IsSetByUser = true; + } + catch (Exception exc) + { + throw new ArgumentException("The value provided to argument " + userValueDescriptor.Name + " is not acceptable. Reason: " + exc.GetBaseException().Message, userValueDescriptor.Name, exc.InnerException); + } + } + else + { + throw new TypeMismatchException(typeof(string).FullName!, methodsWithCustomInputStringAttribute[0].GetParameters()[0].ParameterType.FullName!, "", "Error in argument definition. It is expected a string, but the actual type is " + methodsWithCustomInputStringAttribute[0].GetParameters()[0].ParameterType.FullName); + } + break; + default: + throw new DuplicateObjectException(methodsWithCustomInputStringAttribute[1].Name, new List { "[CustomInputString] attribute" }, methodsWithCustomInputStringAttribute[0].Name, "Error in class " + property.PropertyType.Name + ". It is expected that only one method may have the [CustomInputString] attribute, but there are " + methodsWithCustomInputStringAttribute.Count + " methods with this attribute"); + } + + return argumentDescriptor; + } + + public ParameterDescriptor SetArgumentValueIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + if (string.IsNullOrEmpty(property.GetCustomAttribute()!.StringFormat)) + { + /* + * CustomInputStringAttribute without arguments is redundant and should not be used. + * However it may happen that it is used by mistake or for some incomprehension, + * so consider this case + */ + argumentDescriptor = SetArgumentValueIfTypeDoesNotExist(parentObject, property, argumentDescriptor, userValueDescriptor); + } + else + { + try + { + // Use ParseExact method + argumentDescriptor.Value = argumentDescriptor.ParameterType.GetMethod("ParseExact", new[] { typeof(string), typeof(string), typeof(IFormatProvider) })!.Invoke(null, new[] { userValueDescriptor.Value, property.GetCustomAttribute()!.StringFormat, CultureInfo.InvariantCulture })!; + property.SetValue(parentObject, argumentDescriptor.Value); + argumentDescriptor.IsSetByUser = true; + } + catch (Exception exc) + { + throw new ArgumentException("The value provided to argument " + userValueDescriptor.Name + " is not acceptable. Reason: " + exc.GetBaseException().Message, userValueDescriptor.Name, exc.InnerException); + } + } + return argumentDescriptor; + } + + public ParameterDescriptor SetArgumentValueIfTypeExists(object parentObject, FieldInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + return argumentDescriptor; + } + } +} diff --git a/src/InterAppConnector/Rules/DefaultRule.cs b/src/InterAppConnector/Rules/DefaultRule.cs new file mode 100644 index 0000000..22908e9 --- /dev/null +++ b/src/InterAppConnector/Rules/DefaultRule.cs @@ -0,0 +1,169 @@ +using InterAppConnector.Attributes; +using InterAppConnector.DataModels; +using InterAppConnector.Interfaces; +using System.Reflection; + +namespace InterAppConnector.Rules +{ + public class DefaultRule : IArgumentDefinitionRule, IArgumentSettingRule + { + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor descriptor) + { + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + descriptor.OriginalPropertyName = property.Name; + + Type? parameterType = Nullable.GetUnderlyingType(property.PropertyType); + + if (parameterType == null) + { + descriptor.ParameterType = property.PropertyType; + } + else + { + descriptor.ParameterType = parameterType; + } + + descriptor.Attributes = property.GetCustomAttributes().ToList(); + descriptor.Value = property.GetValue(parentObject)!; + + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, FieldInfo field, ParameterDescriptor descriptor) + { + descriptor.OriginalPropertyName = field.Name; + descriptor.Value = (int) field.GetValue(parentObject)!; + return descriptor; + } + + public bool IsRuleEnabledInArgumentDefinition(PropertyInfo property) + { + bool isRuleEnabled = true; + + if (property.GetCustomAttribute() != null) + { + isRuleEnabled = false; + } + + return isRuleEnabled; + } + + public bool IsRuleEnabledInArgumentDefinition(FieldInfo field) + { + bool isRuleEnabled = true; + + if (field.GetCustomAttribute() != null) + { + isRuleEnabled = false; + } + + return isRuleEnabled; + } + + public bool IsRuleEnabledInArgumentSetting(PropertyInfo property) + { + Type? parameterType = Nullable.GetUnderlyingType(property.PropertyType); + if (parameterType == null) + { + parameterType = property.PropertyType; + } + bool isEnumType = parameterType.IsEnum; + bool isValueTypeAndIsNotAStruct = parameterType.IsValueType && !StructHelper.IsStruct(parameterType); + return isEnumType || isValueTypeAndIsNotAStruct; + } + + public bool IsRuleEnabledInArgumentSetting(FieldInfo field) + { + return false; + } + + public ParameterDescriptor SetArgumentValueIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + return argumentDescriptor; + } + + public ParameterDescriptor SetArgumentValueIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + return argumentDescriptor; + } + + public ParameterDescriptor SetArgumentValueIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + if (argumentDescriptor.ParameterType.IsEnum) + { + /* + * Remember that a value in an enumeration may have different aliases that works + * like aliases in properties, with the main difference that refers to a value instead of + * the property. + * Aliases can indentify a value uniquely and could mask the original value name, but be careful for batch execution, where the value + * passed to the property is also the name of the property + */ + try + { + argumentDescriptor.Value = EnumHelper.GetEnumerationFieldByValue(argumentDescriptor.ParameterType, userValueDescriptor.Value.ToString()!); + property.SetValue(parentObject, EnumHelper.GetEnumerationFieldByValue(argumentDescriptor.ParameterType, userValueDescriptor.Value.ToString()!)); + argumentDescriptor.IsSetByUser = true; + } + catch (ArgumentException exc) + { + /* + * If something went wrong with the parse of the value, throw an exception. + * You may need to change this exception with a more specialized exception, + * but the InnerException property should contain the exception raised by + * EnumHelper.GetFieldName() + */ + throw new ArgumentException("The value provided in parameter " + userValueDescriptor.Name + " is not acceptable. Please provide a new value", userValueDescriptor.Name, exc); + } + } + else + { + try + { + /* + * During the parse of the arguments, if an argument is passed without a value is considered a switch + * This is not always true, especially if the argument is placed as last token in the argument definition + * So if the type of the argument is not a boolean, you have to specify a value explicitly + */ + if (argumentDescriptor.ParameterType != typeof(bool) && !(userValueDescriptor.Value is bool)) + { + argumentDescriptor.Value = Convert.ChangeType(userValueDescriptor.Value, argumentDescriptor.ParameterType); + property.SetValue(parentObject, Convert.ChangeType(userValueDescriptor.Value, argumentDescriptor.ParameterType)); + argumentDescriptor.IsSetByUser = true; + } + else + { + if (argumentDescriptor.ParameterType == typeof(bool)) + { + argumentDescriptor.Value = Convert.ChangeType(userValueDescriptor.Value, argumentDescriptor.ParameterType); + property.SetValue(parentObject, Convert.ChangeType(userValueDescriptor.Value, argumentDescriptor.ParameterType)); + argumentDescriptor.IsSetByUser = true; + } + else + { + throw new ArgumentException("The value provided to argument " + userValueDescriptor.Name + " is not acceptable. Please provide a valid value", userValueDescriptor.Name); + } + } + } + catch (Exception exc) + { + throw new ArgumentException("The value provided to argument " + userValueDescriptor.Name + " is not acceptable. Please provide a valid value", userValueDescriptor.Name, exc); + } + } + return argumentDescriptor; + } + + public ParameterDescriptor SetArgumentValueIfTypeExists(object parentObject, FieldInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + return argumentDescriptor; + } + } +} diff --git a/src/InterAppConnector/Rules/DescriptionRule.cs b/src/InterAppConnector/Rules/DescriptionRule.cs new file mode 100644 index 0000000..8da381f --- /dev/null +++ b/src/InterAppConnector/Rules/DescriptionRule.cs @@ -0,0 +1,71 @@ +using InterAppConnector.Attributes; +using InterAppConnector.DataModels; +using InterAppConnector.Interfaces; +using System.ComponentModel; +using System.Reflection; + +namespace InterAppConnector.Rules +{ + public class DescriptionRule : IArgumentDefinitionRule + { + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + descriptor.Description = "No description provided"; + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor descriptor) + { + descriptor.Description = "No description provided"; + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + DescriptionAttribute? attribute = property.GetCustomAttribute(); + + if (attribute != null) + { + descriptor.Description = attribute.Description; + } + + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, FieldInfo field, ParameterDescriptor descriptor) + { + DescriptionAttribute? attribute = field.GetCustomAttribute(); + + if (attribute != null) + { + descriptor.Description = attribute.Description; + } + + return descriptor; + } + + public bool IsRuleEnabledInArgumentDefinition(PropertyInfo property) + { + bool isRuleEnabled = true; + + if (property.GetCustomAttribute() != null) + { + isRuleEnabled = false; + } + + return isRuleEnabled; + } + + public bool IsRuleEnabledInArgumentDefinition(FieldInfo field) + { + bool isRuleEnabled = true; + + if (field.GetCustomAttribute() != null) + { + isRuleEnabled = false; + } + + return isRuleEnabled; + } + } +} diff --git a/src/InterAppConnector/Rules/ExampleValueRule.cs b/src/InterAppConnector/Rules/ExampleValueRule.cs new file mode 100644 index 0000000..421b071 --- /dev/null +++ b/src/InterAppConnector/Rules/ExampleValueRule.cs @@ -0,0 +1,45 @@ +using InterAppConnector.Attributes; +using InterAppConnector.DataModels; +using InterAppConnector.Interfaces; +using System.Reflection; + +namespace InterAppConnector.Rules +{ + public class ExampleValueRule : IArgumentDefinitionRule + { + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor descriptor) + { + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + ExampleValueAttribute? attribute = property.GetCustomAttribute(); + if (string.IsNullOrEmpty(attribute!.ExampleValue)) + { + throw new ArgumentException("The example value cannot be null or empty", property.Name); + } + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, FieldInfo field, ParameterDescriptor descriptor) + { + return descriptor; + } + + public bool IsRuleEnabledInArgumentDefinition(PropertyInfo property) + { + return true; + } + + public bool IsRuleEnabledInArgumentDefinition(FieldInfo field) + { + return false; + } + } +} diff --git a/src/InterAppConnector/Rules/MandatoryForCommandRule.cs b/src/InterAppConnector/Rules/MandatoryForCommandRule.cs new file mode 100644 index 0000000..e7d5aa7 --- /dev/null +++ b/src/InterAppConnector/Rules/MandatoryForCommandRule.cs @@ -0,0 +1,76 @@ +using InterAppConnector.Attributes; +using InterAppConnector.DataModels; +using InterAppConnector.Interfaces; +using System.Reflection; + +namespace InterAppConnector.Rules +{ + public class MandatoryForCommandRule : IArgumentDefinitionRule + { + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + if (property.PropertyType.IsValueType) + { + if (Nullable.GetUnderlyingType(property.PropertyType) == null) + { + descriptor.IsMandatory = true; + } + } + else + { + if (property.GetValue(parentObject) != null) + { + property.SetValue(parentObject, null); + descriptor.IsMandatory = true; + } + } + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor descriptor) + { + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + foreach (MandatoryForCommandAttribute propertyAttribute in property.GetCustomAttributes()) + { + if (propertyAttribute.Command == CommandManager.CurrentCommand!.GetType()) + { + descriptor.IsMandatory = true; + } + } + return descriptor; + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, FieldInfo field, ParameterDescriptor descriptor) + { + return descriptor; + } + + public bool IsRuleEnabledInArgumentDefinition(PropertyInfo property) + { + bool isRuleEnabled = true; + + if (property.GetCustomAttribute() != null) + { + isRuleEnabled = false; + } + + return isRuleEnabled; + } + + public bool IsRuleEnabledInArgumentDefinition(FieldInfo field) + { + bool isRuleEnabled = true; + + if (field.GetCustomAttribute() != null) + { + isRuleEnabled = false; + } + + return isRuleEnabled; + } + } +} diff --git a/src/InterAppConnector/Rules/ValueValidatorRule.cs b/src/InterAppConnector/Rules/ValueValidatorRule.cs new file mode 100644 index 0000000..8312977 --- /dev/null +++ b/src/InterAppConnector/Rules/ValueValidatorRule.cs @@ -0,0 +1,70 @@ +using InterAppConnector.Attributes; +using InterAppConnector.DataModels; +using InterAppConnector.Exceptions; +using InterAppConnector.Interfaces; +using System.Reflection; + +namespace InterAppConnector.Rules +{ + public class ValueValidatorRule : IArgumentSettingRule + { + public bool IsRuleEnabledInArgumentSetting(PropertyInfo property) + { + return true; + } + + public bool IsRuleEnabledInArgumentSetting(FieldInfo field) + { + return true; + } + + public ParameterDescriptor SetArgumentValueIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + return argumentDescriptor; + } + + public ParameterDescriptor SetArgumentValueIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + return argumentDescriptor; + } + + public ParameterDescriptor SetArgumentValueIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + ValueValidatorAttribute validator = property.GetCustomAttribute()!; + + if (validator.ValueValidatorType.GetInterface(typeof(IValueValidator).Name) != null) + { + IValueValidator valueValidator = (IValueValidator)Activator.CreateInstance(validator.ValueValidatorType)!; + bool customValidationErrorMessageMissing = false; + + try + { + if (!valueValidator.ValidateValue(argumentDescriptor.Value)) + { + customValidationErrorMessageMissing = true; + } + } + catch (Exception exc) + { + throw new ArgumentException("The value provided to argument " + userValueDescriptor.Name + " is not acceptable. Reason: " + exc.GetBaseException().Message, userValueDescriptor.Name, exc.InnerException); + } + + if (customValidationErrorMessageMissing) + { + throw new ArgumentException("The value provided to argument " + userValueDescriptor.Name + " is not valid according to the validation procedure"); + } + } + else + { + throw new TypeMismatchException(typeof(IValueValidator).Name, "", "", validator.ValueValidatorType.Name + " doesn't have an interface of type " + typeof(IValueValidator).Name); + } + + return argumentDescriptor; + } + + public ParameterDescriptor SetArgumentValueIfTypeExists(object parentObject, FieldInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + return argumentDescriptor; + } + } +} diff --git a/tests/InterAppConnector.Test.Library/CommandManagerTest.cs b/tests/InterAppConnector.Test.Library/CommandManagerTest.cs index 84dc38d..fda09f3 100644 --- a/tests/InterAppConnector.Test.Library/CommandManagerTest.cs +++ b/tests/InterAppConnector.Test.Library/CommandManagerTest.cs @@ -114,17 +114,17 @@ public void AddCommand_AddExampleCommands_ShouldConfigureObjectProperly() .AddCommand() .AddCommand(); - Assert.That(command._commands.Count, Is.EqualTo(4)); + Assert.That(command._commands, Has.Count.EqualTo(4)); Assert.That(command._commands[typeof(AppendTextCommand).FullName].GetType(), Is.EqualTo(typeof(AppendTextCommand))); Assert.That(command._commands[typeof(CreateFileCommand).FullName].GetType(), Is.EqualTo(typeof(CreateFileCommand))); Assert.That(command._commands[typeof(WriteTextCommand).FullName].GetType(), Is.EqualTo(typeof(WriteTextCommand))); Assert.That(command._commands[typeof(ReadFileCommand).FullName].GetType(), Is.EqualTo(typeof(ReadFileCommand))); - Assert.That(command._arguments.Count, Is.EqualTo(4)); - Assert.That(command._arguments[typeof(AppendTextCommand).FullName].Arguments.Count, Is.EqualTo(3)); - Assert.That(command._arguments[typeof(CreateFileCommand).FullName].Arguments.Count, Is.EqualTo(3)); - Assert.That(command._arguments[typeof(WriteTextCommand).FullName].Arguments.Count, Is.EqualTo(3)); - Assert.That(command._arguments[typeof(ReadFileCommand).FullName].Arguments.Count, Is.EqualTo(2)); - Assert.That(command._parameterObject.Count, Is.EqualTo(4)); + Assert.That(command._arguments, Has.Count.EqualTo(4)); + Assert.That(command._arguments[typeof(AppendTextCommand).FullName].Arguments, Has.Count.EqualTo(3)); + Assert.That(command._arguments[typeof(CreateFileCommand).FullName].Arguments, Has.Count.EqualTo(3)); + Assert.That(command._arguments[typeof(WriteTextCommand).FullName].Arguments, Has.Count.EqualTo(3)); + Assert.That(command._arguments[typeof(ReadFileCommand).FullName].Arguments, Has.Count.EqualTo(2)); + Assert.That(command._parameterObject, Has.Count.EqualTo(4)); Assert.That(command._parameterObject[typeof(AppendTextCommand).FullName].GetType(), Is.EqualTo(typeof(FileManagerParameter))); Assert.That(command._parameterObject[typeof(CreateFileCommand).FullName].GetType(), Is.EqualTo(typeof(FileManagerParameter))); Assert.That(command._parameterObject[typeof(WriteTextCommand).FullName].GetType(), Is.EqualTo(typeof(FileManagerParameter))); @@ -138,7 +138,7 @@ public void AddCommand_CheckMandatoryParameters_ShouldConfigureObjectProperly() command.AddCommand(); - Assert.That(command._arguments[typeof(MultipleArgumentTypeCommand).FullName].Arguments.Count, Is.EqualTo(9)); + Assert.That(command._arguments[typeof(MultipleArgumentTypeCommand).FullName].Arguments, Has.Count.EqualTo(9)); Assert.That(command._arguments[typeof(MultipleArgumentTypeCommand).FullName].Arguments["mandatorynumber"].IsMandatory, Is.EqualTo(true)); Assert.That(command._arguments[typeof(MultipleArgumentTypeCommand).FullName].Arguments["mandatoryfileinfo"].IsMandatory, Is.EqualTo(true)); Assert.That(command._arguments[typeof(MultipleArgumentTypeCommand).FullName].Arguments["mandatoryguid"].IsMandatory, Is.EqualTo(true)); @@ -152,7 +152,7 @@ public void AddCommand_CheckOptionalParameters_ShouldConfigureObjectProperly() command.AddCommand(); - Assert.That(command._arguments[typeof(MultipleArgumentTypeCommand).FullName].Arguments.Count, Is.EqualTo(9)); + Assert.That(command._arguments[typeof(MultipleArgumentTypeCommand).FullName].Arguments, Has.Count.EqualTo(9)); Assert.That(command._arguments[typeof(MultipleArgumentTypeCommand).FullName].Arguments["number"].IsMandatory, Is.EqualTo(false)); Assert.That(command._arguments[typeof(MultipleArgumentTypeCommand).FullName].Arguments["guid"].IsMandatory, Is.EqualTo(false)); Assert.That(command._arguments[typeof(MultipleArgumentTypeCommand).FullName].Arguments["switch"].IsMandatory, Is.EqualTo(false)); @@ -300,7 +300,7 @@ public void SetArgument_WithValidatorCustomInputStringAndValueInRange_ReturnArgu } [TestCase("validatedcustomclass")] - //[TestCase("othervalidatedcustomclass")] + [TestCase("othervalidatedcustomclass")] public void SetArgument_WithAnotherValidatorCustomInputStringAndValueInRange_ReturnArgumentSet(string argument) { Argument arguments = Argument.Parse(new[] { "setargument", "-" + argument, "apple,pear" }, "-"); @@ -309,7 +309,7 @@ public void SetArgument_WithAnotherValidatorCustomInputStringAndValueInRange_Ret manager.SetArguments(new List(new[] { "setargument" }), arguments.Arguments.Values.ToList()); - Assert.That(((CustomStringFormatClass)manager._arguments[typeof(SetArgumentCommand).FullName].Arguments[argument].Value).List.Count, Is.EqualTo(2)); + Assert.That(((CustomStringFormatClass)manager._arguments[typeof(SetArgumentCommand).FullName].Arguments[argument].Value).List, Has.Count.EqualTo(2)); } [Test] @@ -359,7 +359,7 @@ public void SetArgument_WithValidatorCustomInputStringAndValueNotInRange_ReturnA } [TestCase("validatedcustomclass")] - //[TestCase("othervalidatedcustomclass")] + [TestCase("othervalidatedcustomclass")] public void SetArgument_WithAnotherValidatorCustomInputStringAndValueNotInRange_ReturnArgumentException(string argument) { Argument arguments = Argument.Parse(new[] { "setargument", "-" + argument, "apple,grapefruitpear" }, "-"); @@ -376,6 +376,9 @@ public void SetArgument_WithAnotherValidatorCustomInputStringAndValueNotInRange_ [TestCase("abcd123a")] [TestCase("ab")] + // + [TestCase("")] + // public void SetArgument_ParameterWithWrongCustomString_ReturnArgumentException(string value) { Argument arguments = Argument.Parse(new[] { "setargument", "-plate", value }, "-"); diff --git a/tests/InterAppConnector.Test.Library/DataModels/SetArgumentMethodDataModel.cs b/tests/InterAppConnector.Test.Library/DataModels/SetArgumentMethodDataModel.cs index 0853640..fd28aa5 100644 --- a/tests/InterAppConnector.Test.Library/DataModels/SetArgumentMethodDataModel.cs +++ b/tests/InterAppConnector.Test.Library/DataModels/SetArgumentMethodDataModel.cs @@ -81,12 +81,9 @@ public class SetArgumentMethodDataModel [ValueValidator(typeof(AgeValidatorWithCustomErrorMessageValidator))] public uint ValidatedValue { get; set; } - - /* - * To check. There may be a bug here - * [ValueValidator(typeof(CustomStringClassvalidator))] + + [ValueValidator(typeof(CustomStringClassValidator))] [CustomInputString] public CustomStringFormatClass OtherValidatedCustomClass { get; set; } - */ } } diff --git a/tests/InterAppConnector.Test.Library/Enumerations/VehicleType.cs b/tests/InterAppConnector.Test.Library/Enumerations/VehicleType.cs index 986cd75..1c5e8d0 100644 --- a/tests/InterAppConnector.Test.Library/Enumerations/VehicleType.cs +++ b/tests/InterAppConnector.Test.Library/Enumerations/VehicleType.cs @@ -19,7 +19,7 @@ public enum VehicleType Airplane = 20, - [ExcludeItemFromCommandAttribute] + [ExcludeItemFromCommand] Undefined = 25 } } diff --git a/tests/InterAppConnector.Test.Library/Rules/CustomInputStringRuleTest.cs b/tests/InterAppConnector.Test.Library/Rules/CustomInputStringRuleTest.cs new file mode 100644 index 0000000..76b6b3c --- /dev/null +++ b/tests/InterAppConnector.Test.Library/Rules/CustomInputStringRuleTest.cs @@ -0,0 +1,46 @@ +using InterAppConnector.DataModels; +using InterAppConnector.Rules; +using NUnit.Framework; + +namespace InterAppConnector.Test.Library.Rules +{ + /// + /// These tests are used only to increase the code coverage score. + /// The methods thested in this class will not be used in real applications + /// + public class CustomInputStringRuleTest + { + public static int fictiousField; + public static int FictiousProperty { get; set; } + + [Theory] + public void IsRuleEnabledInArgumentSetting_WithAFictiousField_ReturnTrue() + { + CustomInputStringRule rule = new CustomInputStringRule(); + + bool returnFalse = rule.IsRuleEnabledInArgumentSetting(typeof(CustomInputStringRuleTest).GetFields()[0]); + + Assert.That(returnFalse, Is.False); + } + + [Theory] + public void SetArgumentValueIfTypeDoesNotExist_WithAFictiousField_ReturnNotImplementedException() + { + CustomInputStringRule rule = new CustomInputStringRule(); + + ParameterDescriptor descriptor = rule.SetArgumentValueIfTypeDoesNotExist(null, typeof(CustomInputStringRuleTest).GetFields()[0], new ParameterDescriptor(), new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + + [Theory] + public void SetArgumentValueIfTypeExists_WithAFictiousField_ReturnNotImplementedException() + { + CustomInputStringRule rule = new CustomInputStringRule(); + + ParameterDescriptor descriptor = rule.SetArgumentValueIfTypeExists(null, typeof(CustomInputStringRuleTest).GetFields()[0], new ParameterDescriptor(), new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + } +} diff --git a/tests/InterAppConnector.Test.Library/Rules/DefaultRuleTest.cs b/tests/InterAppConnector.Test.Library/Rules/DefaultRuleTest.cs new file mode 100644 index 0000000..663c3d4 --- /dev/null +++ b/tests/InterAppConnector.Test.Library/Rules/DefaultRuleTest.cs @@ -0,0 +1,76 @@ +using InterAppConnector.DataModels; +using InterAppConnector.Rules; +using NUnit.Framework; + +namespace InterAppConnector.Test.Library.Rules +{ + /// + /// These tests are used only to increase the code coverage score. + /// The methods thested in this class will not be used in real applications + /// + public class DefaultRuleTest + { + public static int fictiousField; + public static int FictiousProperty { get; set; } + + [Theory] + public void SetArgumentValueIfTypeDoesNotExist_WithAFictiousField_ReturnArgumentDescriptor() + { + DefaultRule rule = new DefaultRule(); + + ParameterDescriptor descriptor = rule.SetArgumentValueIfTypeDoesNotExist(null, typeof(DefaultRuleTest).GetFields()[0], new ParameterDescriptor(), new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + + [Theory] + public void SetArgumentValueIfTypeDoesNotExist_WithAFictiousProperty_ReturnArgumentDescriptor() + { + DefaultRule rule = new DefaultRule(); + + ParameterDescriptor descriptor = rule.SetArgumentValueIfTypeDoesNotExist(null, typeof(DefaultRuleTest).GetProperties()[0], new ParameterDescriptor(), new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + + [Theory] + public void DefineArgumentIfTypeDoesNotExist_WithAFictiousField_ReturnArgumentDescriptor() + { + DefaultRule rule = new DefaultRule(); + + ParameterDescriptor descriptor = rule.DefineArgumentIfTypeDoesNotExist(null, typeof(DefaultRuleTest).GetFields()[0], new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + + [Theory] + public void DefineArgumentIfTypeDoesNotExist_WithAFictiousProperty_ReturnArgumentDescriptor() + { + DefaultRule rule = new DefaultRule(); + + ParameterDescriptor descriptor = rule.DefineArgumentIfTypeDoesNotExist(null, typeof(DefaultRuleTest).GetProperties()[0], new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + + [Theory] + public void IsRuleEnabledInArgumentSetting_WithAFictiousField_ReturnFalse() + { + DefaultRule rule = new DefaultRule(); + + bool returnFalse = rule.IsRuleEnabledInArgumentSetting(typeof(DefaultRuleTest).GetFields()[0]); + + Assert.That(returnFalse, Is.False); + } + + [Theory] + public void SetArgumentValueIfTypeExists_WithAFictiousField_ReturnArgumentDescriptor() + { + DefaultRule rule = new DefaultRule(); + + ParameterDescriptor descriptor = rule.SetArgumentValueIfTypeExists(null, typeof(DefaultRuleTest).GetFields()[0], new ParameterDescriptor(), new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + } +} diff --git a/tests/InterAppConnector.Test.Library/Rules/ExampleValueRuleTest.cs b/tests/InterAppConnector.Test.Library/Rules/ExampleValueRuleTest.cs new file mode 100644 index 0000000..a0c8606 --- /dev/null +++ b/tests/InterAppConnector.Test.Library/Rules/ExampleValueRuleTest.cs @@ -0,0 +1,36 @@ +using InterAppConnector.DataModels; +using InterAppConnector.Rules; +using NUnit.Framework; + +namespace InterAppConnector.Test.Library.Rules +{ + /// + /// These tests are used only to increase the code coverage score. + /// The methods thested in this class will not be used in real applications + /// + public class ExampleValueRuleTest + { + public static int fictiousField; + public static int FictiousProperty { get; set; } + + [Theory] + public void DefineArgumentIfTypeExists_WithAFictiousField_ReturnNotImplementedException() + { + ExampleValueRule rule = new ExampleValueRule(); + + ParameterDescriptor descriptor = rule.DefineArgumentIfTypeExists(null, typeof(ExampleValueRuleTest).GetFields()[0], new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + + [Theory] + public void DefineArgumentIfTypeDoesNotExist_WithAFictiousField_ReturnNotImplementedException() + { + ExampleValueRule rule = new ExampleValueRule(); + + ParameterDescriptor descriptor = rule.DefineArgumentIfTypeDoesNotExist(null, typeof(ExampleValueRuleTest).GetFields()[0], new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + } +} diff --git a/tests/InterAppConnector.Test.Library/Rules/GenericRuleTest.cs b/tests/InterAppConnector.Test.Library/Rules/GenericRuleTest.cs new file mode 100644 index 0000000..b90f0ed --- /dev/null +++ b/tests/InterAppConnector.Test.Library/Rules/GenericRuleTest.cs @@ -0,0 +1,90 @@ +using InterAppConnector.DataModels; +using InterAppConnector.Interfaces; +using NUnit.Framework; +using System.Reflection; + +namespace InterAppConnector.Test.Library.Rules +{ + internal class GenericRuleTest : IArgumentDefinitionRule, IArgumentSettingRule + { + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + throw new NotImplementedException(); + } + + public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor descriptor) + { + throw new NotImplementedException(); + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, FieldInfo field, ParameterDescriptor descriptor) + { + throw new NotImplementedException(); + } + + public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor descriptor) + { + throw new NotImplementedException(); + } + + public bool IsRuleEnabledInArgumentDefinition(PropertyInfo property) + { + return false; + } + + public bool IsRuleEnabledInArgumentDefinition(FieldInfo field) + { + return false; + } + + public bool IsRuleEnabledInArgumentSetting(PropertyInfo property) + { + return false; + } + + public bool IsRuleEnabledInArgumentSetting(FieldInfo field) + { + return false; + } + + public ParameterDescriptor SetArgumentValueIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + throw new NotImplementedException(); + } + + public ParameterDescriptor SetArgumentValueIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + throw new NotImplementedException(); + } + + public ParameterDescriptor SetArgumentValueIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + throw new NotImplementedException(); + } + + public ParameterDescriptor SetArgumentValueIfTypeExists(object parentObject, FieldInfo property, ParameterDescriptor argumentDescriptor, ParameterDescriptor userValueDescriptor) + { + throw new NotImplementedException(); + } + + [Theory] + public void TestDefineRule_ShouldReturnTrue() + { + GenericRuleTest test = new GenericRuleTest(); + + bool returnTrue = ((IArgumentDefinitionRule)test).ReturnTrue(23); + + Assert.That(returnTrue, Is.True); + } + + [Theory] + public void TestSetRule_ShouldReturnTrue() + { + GenericRuleTest test = new GenericRuleTest(); + + bool returnTrue = ((IArgumentSettingRule)test).ReturnTrue(23); + + Assert.That(returnTrue, Is.True); + } + } +} diff --git a/tests/InterAppConnector.Test.Library/Rules/MandatoryForCommandRuleTest.cs b/tests/InterAppConnector.Test.Library/Rules/MandatoryForCommandRuleTest.cs new file mode 100644 index 0000000..57891d1 --- /dev/null +++ b/tests/InterAppConnector.Test.Library/Rules/MandatoryForCommandRuleTest.cs @@ -0,0 +1,23 @@ +using InterAppConnector.DataModels; +using InterAppConnector.Rules; +using NUnit.Framework; + +namespace InterAppConnector.Test.Library.Rules +{ + /// + /// These tests are used only to increase the code coverage score. + /// The methods thested in this class will not be used in real applications + /// + public class MandatoryForCommandRuleTest + { + [Theory] + public void DefineArgumentIfTypeExists_WithAFictiousField_ReturnArgumentDescriptor() + { + MandatoryForCommandRule rule = new MandatoryForCommandRule(); + + ParameterDescriptor descriptor = rule.DefineArgumentIfTypeExists(null, typeof(ExampleValueRuleTest).GetFields()[0], new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + } +} diff --git a/tests/InterAppConnector.Test.Library/Rules/ValueValidatorRuleTest.cs b/tests/InterAppConnector.Test.Library/Rules/ValueValidatorRuleTest.cs new file mode 100644 index 0000000..e7750b4 --- /dev/null +++ b/tests/InterAppConnector.Test.Library/Rules/ValueValidatorRuleTest.cs @@ -0,0 +1,42 @@ +using InterAppConnector.DataModels; +using InterAppConnector.Rules; +using NUnit.Framework; + +namespace InterAppConnector.Test.Library.Rules +{ + public class ValueValidatorRuleTest + { + public static int fictiousField; + public static int FictiousProperty { get; set; } + + [Theory] + public void IsRuleEnabledInArgumentSetting_WithAFictiousField_ReturnTrue() + { + ValueValidatorRule rule = new ValueValidatorRule(); + + bool returnFalse = rule.IsRuleEnabledInArgumentSetting(typeof(ValueValidatorRuleTest).GetFields()[0]); + + Assert.That(returnFalse, Is.True); + } + + [Theory] + public void SetArgumentValueIfTypeDoesNotExist_WithAFictiousField_ReturnArgumentDescriptor() + { + ValueValidatorRule rule = new ValueValidatorRule(); + + ParameterDescriptor descriptor = rule.SetArgumentValueIfTypeDoesNotExist(null, typeof(ValueValidatorRuleTest).GetFields()[0], new ParameterDescriptor(), new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + + [Theory] + public void SetArgumentValueIfTypeExists_WithAFictiousField_ReturnArgumentDescriptor() + { + ValueValidatorRule rule = new ValueValidatorRule(); + + ParameterDescriptor descriptor = rule.SetArgumentValueIfTypeExists(null, typeof(ValueValidatorRuleTest).GetFields()[0], new ParameterDescriptor(), new ParameterDescriptor()); + + Assert.That(descriptor, Is.EqualTo(new ParameterDescriptor())); + } + } +}