diff --git a/src/activities/Elsa.Activities.Http/Models/StringValuesModel.cs b/src/activities/Elsa.Activities.Http/Models/StringValuesModel.cs
index 71fe2125b..c9fb296e4 100644
--- a/src/activities/Elsa.Activities.Http/Models/StringValuesModel.cs
+++ b/src/activities/Elsa.Activities.Http/Models/StringValuesModel.cs
@@ -1,33 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
using Microsoft.Extensions.Primitives;
namespace Elsa.Activities.Http.Models
{
- public class StringValuesModel
+ ///
+ /// Represents a collection of zero or more string values. It is used for values such as
+ /// HTTP header/query string values. These are often thought-of as key/value pairs but might
+ /// have more than one value.
+ ///
+ public class StringValuesModel : IConvertible
{
- public string? Value { get; set; }
- public string[]? Values { get; set; }
+ ///
+ /// Gets either the first value of or a reference
+ /// if the values are null or empty.
+ ///
+ /// The first value
+ public string? Value => Values?.FirstOrDefault();
- public StringValuesModel()
- {
- }
+ ///
+ /// Gets a collection of the string values for the current instance.
+ ///
+ /// The values
+ public string[] Values { get; set; }
- public StringValuesModel(StringValues value)
- {
- Value = value.Count == 1 ? value.ToString() : default;
- Values = value.Count != 1 ? value.ToArray() : default;
- }
+ ///
+ /// Initializes an instance of with an empty collection of values.
+ ///
+ public StringValuesModel() => Values = new string[0];
- public override string? ToString()
- {
- if (Values == null)
- return Value;
+ ///
+ /// Initializes an instance of from a .
+ ///
+ /// A model of zero or more string values.
+ public StringValuesModel(StringValues value) => Values = value.ToArray();
- return Values.Length switch
- {
- 0 => default,
- 1 => Values[0],
- _ => string.Join(",", Values)
- };
- }
+ ///
+ /// Gets a string representation of the current instance. Either null (for no, or an empty-collection of ),
+ /// a single string (for a collection of one ) or a comma-separated string of many values.
+ ///
+ /// The string representation of the .
+ public override string? ToString() => (Values?.Any() == true) ? string.Join(",", Values) : null;
+
+ #region IConvertible implementation
+
+ TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
+
+ bool IConvertible.ToBoolean(IFormatProvider provider) => Convert.ToBoolean(ToString(), provider);
+
+ byte IConvertible.ToByte(IFormatProvider provider) => Convert.ToByte(ToString(), provider);
+
+ char IConvertible.ToChar(IFormatProvider provider) => Convert.ToChar(ToString(), provider);
+
+ DateTime IConvertible.ToDateTime(IFormatProvider provider) => Convert.ToDateTime(ToString(), provider);
+
+ decimal IConvertible.ToDecimal(IFormatProvider provider) => Convert.ToDecimal(ToString(), provider);
+
+ double IConvertible.ToDouble(IFormatProvider provider) => Convert.ToDouble(ToString(), provider);
+
+ short IConvertible.ToInt16(IFormatProvider provider) => Convert.ToInt16(ToString(), provider);
+
+ int IConvertible.ToInt32(IFormatProvider provider) => Convert.ToInt32(ToString(), provider);
+
+ long IConvertible.ToInt64(IFormatProvider provider) => Convert.ToInt64(ToString(), provider);
+
+ sbyte IConvertible.ToSByte(IFormatProvider provider) => Convert.ToSByte(ToString(), provider);
+
+ float IConvertible.ToSingle(IFormatProvider provider) => Convert.ToSingle(ToString(), provider);
+
+ string IConvertible.ToString(IFormatProvider provider) => ToString()!;
+
+ object IConvertible.ToType(Type conversionType, IFormatProvider provider) => Convert.ChangeType(ToString(), conversionType, provider);
+
+ ushort IConvertible.ToUInt16(IFormatProvider provider) => Convert.ToUInt16(ToString(), provider);
+
+ uint IConvertible.ToUInt32(IFormatProvider provider) => Convert.ToUInt32(ToString(), provider);
+
+ ulong IConvertible.ToUInt64(IFormatProvider provider) => Convert.ToUInt64(ToString(), provider);
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/test/unit/Elsa.UnitTests/Activities/Http/StringValuesModelTests.cs b/test/unit/Elsa.UnitTests/Activities/Http/StringValuesModelTests.cs
new file mode 100644
index 000000000..c61ef147b
--- /dev/null
+++ b/test/unit/Elsa.UnitTests/Activities/Http/StringValuesModelTests.cs
@@ -0,0 +1,96 @@
+using System;
+using Elsa.Activities.Http.Models;
+using Microsoft.Extensions.Primitives;
+using Xunit;
+
+namespace Elsa.Activities.Http
+{
+ public class StringValuesModelTests
+ {
+ [Theory(DisplayName = "It should be possible to use Convert.ChangeType to convert an instance to string. This is primarily to support usage via Jint."), AutoMoqData]
+ public void ItShouldBePossibleToConvertAStringValuesModelToString(string[] values)
+ {
+ var sut = new StringValuesModel(new StringValues(values));
+ var result = Convert.ChangeType(sut, typeof(string));
+ Assert.Equal(sut.ToString(), result);
+ }
+
+ [Theory(DisplayName = "It should be possible to use Convert.ToInt32 to convert an instance to a number if the value is a valid string representation of a number. This is primarily to support usage via Jint."), AutoMoqData]
+ public void ItShouldBePossibleToConvertAStringValuesModelToInt32IfValid()
+ {
+ var sut = new StringValuesModel(new StringValues("3"));
+ var result = Convert.ChangeType(sut, typeof(int));
+ Assert.Equal(3, result);
+ }
+
+ [Theory(DisplayName = "The Value property should return the first value if there is more than one."), AutoMoqData]
+ public void ValueShouldReturnFirstItemIfThereAreMoreThanOne(string firstValue, string secondValue)
+ {
+ var sut = new StringValuesModel(new StringValues(new [] { firstValue, secondValue }));
+ Assert.Equal(firstValue, sut.Value);
+ }
+
+ [Theory(DisplayName = "The Value property should always return the first value if there is only one."), AutoMoqData]
+ public void ValueShouldReturnFirstItemIfThereIsOne(string firstValue)
+ {
+ var sut = new StringValuesModel(new StringValues(new [] { firstValue }));
+ Assert.Equal(firstValue, sut.Value);
+ }
+
+ [Fact(DisplayName = "The Value property should return null if there are no values.")]
+ public void ValueShouldReturnNullIfThereAreNone()
+ {
+ var sut = new StringValuesModel(new StringValues(new string[0]));
+ Assert.Null(sut.Value);
+ }
+
+ [Theory(DisplayName = "The Values property should return a length-one collection if there is a single value."), AutoMoqData]
+ public void ValuesShouldReturnLengthOneCollectionIfThereIsOneValue(string firstValue)
+ {
+ var sut = new StringValuesModel(new StringValues(new [] { firstValue }));
+ Assert.Equal(new [] {firstValue}, sut.Values);
+ }
+
+ [Theory(DisplayName = "The Values property should return a length-two collection if there are two values."), AutoMoqData]
+ public void ValuesShouldReturnLengthTwoCollectionIfThereAreTwoValues(string firstValue, string secondValue)
+ {
+ var sut = new StringValuesModel(new StringValues(new [] { firstValue, secondValue }));
+ Assert.Equal(new [] {firstValue, secondValue}, sut.Values);
+ }
+
+ [Fact(DisplayName = "The Values property should return an empty collection if there are no values.")]
+ public void ValuesShouldReturnEmptyCollectionIfThereAreNone()
+ {
+ var sut = new StringValuesModel(new StringValues(new string[0]));
+ Assert.Empty(sut.Values);
+ }
+
+ [Fact(DisplayName = "The ToString method should return null if there are no values")]
+ public void ToStringShouldReturnNullIfNotValues()
+ {
+ var sut = new StringValuesModel(new StringValues(new string[0]));
+ Assert.Null(sut.ToString());
+ }
+
+ [Theory(DisplayName = "The ToString method should return the first value if there is only one"), AutoMoqData]
+ public void ToStringShouldReturnFirstValueIfThereIsOnlyOne(string firstValue)
+ {
+ var sut = new StringValuesModel(new StringValues(new [] { firstValue }));
+ Assert.Equal(firstValue, sut.ToString());
+ }
+
+ [Theory(DisplayName = "The ToString method should return comma-separated values if there are more than one"), AutoMoqData]
+ public void ToStringShouldReturnCommaSeparatedValuesIfThereIsMoreThanOne(string firstValue, string secondValue)
+ {
+ var sut = new StringValuesModel(new StringValues(new [] { firstValue, secondValue }));
+ Assert.Equal($"{firstValue},{secondValue}", sut.ToString());
+ }
+
+ [Fact(DisplayName = "The parameterless constructor should initialize Values to an empty collection")]
+ public void ParameterlessCtorShouldInitializeWithEmptyCollection()
+ {
+ var sut = new StringValuesModel();
+ Assert.Empty(sut.Values);
+ }
+ }
+}
\ No newline at end of file