Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feature #9 added example section in command help for interactive applications #21

Merged
merged 1 commit into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/InterAppConnector/Attributes/ExampleValueAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace InterAppConnector.Attributes
{
[AttributeUsage(AttributeTargets.Property)]
public class ExampleValueAttribute : Attribute
{
private readonly string _exampleValue;

public string ExampleValue {
get
{
return _exampleValue;
}
}

public ExampleValueAttribute(string exampleValue)
{
_exampleValue = exampleValue;
}
}
}
19 changes: 10 additions & 9 deletions src/InterAppConnector/CommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,22 +219,17 @@ where method.GetCustomAttribute<CustomInputStringAttribute>() != null

if (validator.ValueValidatorType.GetInterface(typeof(IValueValidator).Name) != null)
{
object constructor = validator.ValueValidatorType.GetConstructor(Array.Empty<Type>())!.Invoke(Array.Empty<object>());

MethodInfo validateInputMethod = (from methood in constructor.GetType().GetMethods()
where methood.Name == "ValidateValue"
select methood).First();

IValueValidator valueValidator = (IValueValidator) Activator.CreateInstance(validator.ValueValidatorType)!;
bool customValidationErrorMessageMissing = false;

try
{
if (!((bool)validateInputMethod.Invoke(constructor, new[] { _arguments[selectedCommand].Arguments[findArgument.Name].Value })!))
if (!valueValidator.ValidateValue(_arguments[selectedCommand].Arguments[findArgument.Name].Value))
{
customValidationErrorMessageMissing = true;
}
}
catch (Exception exc)
catch(Exception exc)
{
throw new ArgumentException("The value provided to argument " + item.Name + " is not acceptable. Reason: " + exc.GetBaseException().Message, item.Name, exc.InnerException);
}
Expand Down Expand Up @@ -400,7 +395,7 @@ where item.Value.Aliases.Intersect(descriptor.Aliases).Any()
attributes.Remove(parameterProperty.GetCustomAttribute<DescriptionAttribute>()!);
}

descriptor.Value = parameterProperty.GetValue(parameterObject);
descriptor.Value = parameterProperty.GetValue(parameterObject)!;
descriptor.Attributes = attributes;

if (includePropertyNameInAliases)
Expand All @@ -411,6 +406,12 @@ where item.Value.Aliases.Intersect(descriptor.Aliases).Any()
}
}

if (parameterProperty.GetCustomAttribute<ExampleValueAttribute>() != null
&& string.IsNullOrEmpty(parameterProperty.GetCustomAttribute<ExampleValueAttribute>()!.ExampleValue))
{
throw new ArgumentException("The example value cannot be null or empty", parameterProperty.Name);
}

argument.Arguments.Add(descriptor.Name, descriptor);
}
}
Expand Down
65 changes: 65 additions & 0 deletions src/InterAppConnector/CommandUtil.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using InterAppConnector.Attributes;
using InterAppConnector.DataModels;
using InterAppConnector.Enumerations;
using InterAppConnector.Interfaces;
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Text;

Expand Down Expand Up @@ -93,15 +97,18 @@ private static string DrawLine(int lenght)

internal static string DescribeAction(string action, string actionDescription, List<ParameterDescriptor> arguments)
{
StringBuilder exampleParameters = new StringBuilder();
StringBuilder description = new StringBuilder();
description.AppendLine("- " + action);
description.AppendLine(" " + actionDescription);
exampleParameters.Append(" " + action);
foreach (ParameterDescriptor descriptor in arguments)
{
description.Append("\t-" + descriptor.Name);
if (descriptor.IsMandatory)
{
description.Append(" (required) : ");
exampleParameters.Append(" -" + descriptor.Name + " ");
}
else
{
Expand All @@ -124,6 +131,7 @@ internal static string DescribeAction(string action, string actionDescription, L
description.AppendLine("\tAccepted values for this parameter: ");
EnumHelper helper = new EnumHelper();
helper.LoadEnumerationValues(descriptor.ParameterType);
string exampleEnumValue = "";
foreach (ParameterDescriptor possibleValue in helper._parameters.Values)
{
description.Append("\t\t" + possibleValue.Name + " ");
Expand All @@ -148,6 +156,55 @@ internal static string DescribeAction(string action, string actionDescription, L
description.AppendLine("\t\t" + possibleValue.Aliases[i] + " : Same as " + possibleValue.Name);
}
}

if (string.IsNullOrEmpty(exampleEnumValue))
{
exampleEnumValue = possibleValue.Name;
}
}

if (descriptor.IsMandatory)
{
exampleParameters.Append("\"" + exampleEnumValue + "\"");
}
}
else
{
/**
* For other types, we have three cases:
* - no validators are defined
* - no examples are defined
* - it is defined a validator and an example
* A validator has a precedence to everything. If a validator is not defined, the library checks
* if there is an example defined in [ExampleValue] attribute. If there is not this attribute
* write a generic string. Remember that booleans haven't any values
*/
if (descriptor.IsMandatory && descriptor.ParameterType != typeof(bool))
{
if (descriptor.Attributes.Exists(item => item.GetType() == typeof(ValueValidatorAttribute)))
{
ValueValidatorAttribute attribute = (ValueValidatorAttribute) descriptor.Attributes.Find(item => item.GetType() == typeof(ValueValidatorAttribute))!;
IValueValidator? validator = Activator.CreateInstance(attribute.ValueValidatorType) as IValueValidator;
if (validator != null)
{
exampleParameters.Append('\"');
exampleParameters.Append(validator.GetSampleValidValue());
exampleParameters.Append('\"');
}
else
{
exampleParameters.Append("\"##INVALIDVALIDATOR##\"");
}
}
else if (descriptor.Attributes.Exists(item => item.GetType() == typeof(ExampleValueAttribute)))
{
ExampleValueAttribute attribute = (ExampleValueAttribute)descriptor.Attributes.Find(item => item.GetType() == typeof(ExampleValueAttribute))!;
exampleParameters.Append("\"" + attribute.ExampleValue + "\"");
}
else
{
exampleParameters.Append("\"<value>\"");
}
}
}

Expand All @@ -165,6 +222,14 @@ internal static string DescribeAction(string action, string actionDescription, L
}

}

if (InterAppCommunication.CommandExecutionType == CommandExecutionType.Interactive)
{
description.AppendLine();
description.AppendLine(" Command example:");
description.AppendLine(" " + Path.GetFileName(Environment.ProcessPath) + exampleParameters.ToString());
}

return description.ToString();
}

Expand Down
17 changes: 17 additions & 0 deletions tests/InterAppConnector.Test.Library/CommandManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ public void AddCommand_AddCommandWithNullAlias_ReturnArgumentExceptionNullAliasN
Assert.That(commands, Throws.InstanceOf(typeof(ArgumentException)));
}

[Test]
public void AddCommand_AddCommandWithWrongExample_ReturnArgumentException()
{
CommandManager manager = new CommandManager();

Action commands = () =>
{
manager.AddCommand<WrongArgumentExampleCommand, WrongExampleDataModel>();
};

Assert.That(commands, Throws.InstanceOf(typeof(ArgumentException)));
}

[Test]
public void AddCommand_AddCommandWithEmptyStringAlias_ReturnArgumentExceptionEmptyStringAliasNotAllowed()
{
Expand Down Expand Up @@ -363,6 +376,10 @@ public void SetArgument_WithAnotherValidatorCustomInputStringAndValueNotInRange_

[TestCase("abcd123a")]
[TestCase("ab")]
//
[TestCase("")]
[TestCase("abcdd123")]
//
public void SetArgument_ParameterWithWrongCustomString_ReturnArgumentException(string value)
{
Argument arguments = Argument.Parse(new[] { "setargument", "-plate", value }, "-");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using InterAppConnector.Attributes;
using InterAppConnector.Interfaces;
using InterAppConnector.Test.Library.DataModels;

namespace InterAppConnector.Test.Library.Commands
{
[Command("wrongexampledatamodel")]
public class WrongArgumentExampleCommand : ICommand<WrongExampleDataModel>
{
public string Main(WrongExampleDataModel arguments)
{
return CommandOutput.Error("If you see this message, there is a problem with the validation of the Example attribute");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using InterAppConnector.Attributes;
using InterAppConnector.Interfaces;
using InterAppConnector.Test.Library.DataModels;

namespace InterAppConnector.Test.Library.Commands
{
[Command("wrongvalidator")]
public class WrongValidatorCommand : ICommand<WrongValidatorDataModel>
{
public string Main(WrongValidatorDataModel arguments)
{
return CommandOutput.Error("You should not see this error message");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
namespace InterAppConnector.Test.Library.DataModels
using InterAppConnector.Attributes;
using InterAppConnector.Test.Library.Validators;

namespace InterAppConnector.Test.Library.DataModels
{
public class ValueTypeDataModel
{
[ExampleValue("50")]
public int MandatoryValue { get; set; }
public int? OptionalValue { get; set; }

public uint? OptionalValue { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using InterAppConnector.Attributes;

namespace InterAppConnector.Test.Library.DataModels
{
public class WrongExampleDataModel
{
[ExampleValue("")]
public int Number { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using InterAppConnector.Attributes;

namespace InterAppConnector.Test.Library.DataModels
{
public class WrongValidatorDataModel
{
[ValueValidator(typeof(WrongValidatorDataModel))]
public string Validator { get; set; } = string.Empty;
}
}
1 change: 0 additions & 1 deletion tests/InterAppConnector.Test.Library/EnumHelperTest.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using InterAppConnector.Enumerations;
using InterAppConnector.Exceptions;
using InterAppConnector.Test.Library.Enumerations;
using Newtonsoft.Json.Linq;
using NUnit.Framework;

namespace InterAppConnector.Test.Library
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using InterAppConnector.Exceptions;
using InterAppConnector.Test.SampleCommandsLibrary.DataModels;
using InterAppConnector.Test.SampleCommandsLibrary;
using System.Diagnostics;

namespace InterAppConnector.Test.Library
{
Expand Down Expand Up @@ -337,7 +338,24 @@ public void ExecuteAsInteractiveCLI_WithMissingOptionalValueTypeArguments_Return
}

[Test]
public void ExecuteAsInteractiveCLI_WithMissingOptionalValueTypeArguments_ReturnError()
public void ExecuteAsInteractiveCLI_ValidatedMissingArgument_ReturnError()
{
CommandManager command = new CommandManager();
command.AddCommand<ValidatedValueTypeCommand, ValidatedValueTypeDataModel>();
string[] arguments = "validatedvaluetype".Split(" ");

Action connectorAction = () =>
{
InterAppCommunication connector = new InterAppCommunication(command);
connector.ExecuteAsInteractiveCLI(arguments);
};

Assert.That(connectorAction, Throws.Nothing);
Assert.That(Environment.ExitCode, Is.EqualTo(3));
}

[Test]
public void ExecuteAsInteractiveCLI_WithWrongOptionalValueTypeArguments_ReturnError()
{
CommandManager command = new CommandManager();
command.AddCommand<ValueTypeCommand, ValueTypeDataModel>();
Expand Down Expand Up @@ -387,6 +405,23 @@ public void ExecuteAsInteractiveCLI_WithAllValueTypeArgumentsSet_ReturnValue()
Assert.That(Environment.ExitCode, Is.EqualTo(0));
}

[Test]
public void ExecuteAsInteractiveCLI_WithWrongValidator_ReturnNoErrors()
{
CommandManager command = new CommandManager();
command.AddCommand<WrongValidatorCommand, WrongValidatorDataModel>();
string[] arguments = "wrongvalidator".Split(" ");

Action connectorAction = () =>
{
InterAppCommunication connector = new InterAppCommunication(command);
connector.ExecuteAsInteractiveCLI(arguments);
};

Assert.That(connectorAction, Throws.Nothing);
Assert.That(Environment.ExitCode, Is.EqualTo(3));
}

[Test]
public void ExecuteAsInteractiveCLI_WithInexistentAction_ReturnFailureStatusCode()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace InterAppConnector.Test.SampleCommandsLibrary.DataModels
public class BaseParameter
{
[Description("Define the file path")]
public string FilePath { get; set; }
public string FilePath { get; set; } = string.Empty;

[Description("Define the output format")]
public CommandOutputFormat? OutputFormat { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ public class FileManagerParameter : BaseParameter
[MandatoryForCommand(typeof(WriteTextCommand))]
[MandatoryForCommand(typeof(AppendTextCommand))]
[Description("The text to insert into the file")]
public string? Text { get; set; }
public string Text { get; set; }
}
}