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

Fixed variable coercion. #991

Merged
merged 15 commits into from
Aug 10, 2019
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,77 @@ public void QueryWithNonNullVariableAndDefaultWhereValueWasProvided()
coercedVariableValues.GetVariable<string>("test"));
}

[Fact]
public void Coerce_Variable_Value_Int_To_String()
{
// arrange
Schema schema = CreateSchema();
OperationDefinitionNode operation = CreateQuery(
"query test($test: String! = \"foo\") { a }");
var variableValues = new Dictionary<string, object>
{
{
"test",
123
}
};

// act
var resolver = new VariableValueBuilder(schema, operation);
Action action = () => resolver.CreateValues(variableValues);

// assert
Assert.Throws<QueryException>(action).Errors.MatchSnapshot();
}

[Fact]
public void Coerce_Variable_Value_Float_To_Int()
{
// arrange
Schema schema = CreateSchema();
OperationDefinitionNode operation = CreateQuery(
"query test($test: Int!) { a }");
var variableValues = new Dictionary<string, object>
{
{
"test",
123.123
}
};

// act
var resolver = new VariableValueBuilder(schema, operation);
Action action = () => resolver.CreateValues(variableValues);

// assert
Assert.Throws<QueryException>(action).Errors.MatchSnapshot();
}

[Fact]
public void Coerce_Variable_Value_Int_To_Float()
{
// arrange
Schema schema = CreateSchema();
OperationDefinitionNode operation = CreateQuery(
"query test($test: Float!) { a }");
var variableValues = new Dictionary<string, object>
{
{
"test",
123
}
};

// act
var resolver = new VariableValueBuilder(schema, operation);
VariableValueCollection coercedVariableValues =
resolver.CreateValues(variableValues);

// assert
Assert.Equal((float)123,
coercedVariableValues.GetVariable<float>("test"));
}

[Fact]
public void StringVariableIsObject()
{
Expand Down Expand Up @@ -350,7 +421,7 @@ public void CreateValues_SerializedDecimal_Decimal()
Schema schema = CreateSchema();
OperationDefinitionNode operation = CreateQuery(
"query test($test: Decimal) { a }");
var input = "1.000000E-004";
var input = 1.000000E-004;

var variableValues = new Dictionary<string, object>();
variableValues.Add("test", input);
Expand Down Expand Up @@ -551,6 +622,7 @@ private Schema CreateSchema()
c.RegisterType<FooType>();
c.RegisterType<BazType>();
c.RegisterType<BarEnumType>();
c.RegisterType<FloatType>();
c.RegisterExtendedScalarTypes();
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[
{
"Message": "Variable `test` got invalid value.",
"Code": null,
"Path": null,
"Locations": [
{
"Line": 1,
"Column": 12
}
],
"Exception": null,
"Extensions": {}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[
{
"Message": "Variable `test` got invalid value.",
"Code": null,
"Path": null,
"Locations": [
{
"Line": 1,
"Column": 12
}
],
"Exception": null,
"Extensions": {}
}
]
41 changes: 2 additions & 39 deletions src/Core/Core/Execution/Utilities/VariableValueBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ internal sealed class VariableValueBuilder
private readonly ISchema _schema;
private readonly OperationDefinitionNode _operation;
private readonly ITypeConversion _converter;
private readonly DictionaryToInputObjectConverter _inputTypeConverter;

public VariableValueBuilder(
ISchema schema,
Expand All @@ -28,8 +27,6 @@ public VariableValueBuilder(
?? throw new ArgumentNullException(nameof(operation));

_converter = _schema.Services.GetTypeConversion();
_inputTypeConverter = new DictionaryToInputObjectConverter(
_converter);
}

/// <summary>
Expand Down Expand Up @@ -139,7 +136,7 @@ private Variable CoerceVariableValue(
return variable;
}

private object Normalize(
private static object Normalize(
VariableDefinitionNode variableDefinition,
Variable variable,
object rawValue)
Expand All @@ -158,45 +155,11 @@ private object Normalize(
value = variable.Type.ParseLiteral(literal);
}

value = DeserializeValue(variable.Type, value);
value = EnsureClrTypeIsCorrect(variable.Type, value);

return value;
}

private object DeserializeValue(IInputType type, object value)
{
if (type.IsLeafType()
&& type.NamedType() is ISerializableType serializable
&& serializable.TryDeserialize(value, out object deserialized))
if (variable.Type.TryDeserialize(value, out object deserialized))
{
return deserialized;
}

if (type.IsListType() && value is IList<object>)
{
return _inputTypeConverter.Convert(value, type);
}

if (type.IsInputObjectType()
&& value is IDictionary<string, object>)
{
return _inputTypeConverter.Convert(value, type);
}

return value;
}

private object EnsureClrTypeIsCorrect(IHasClrType type, object value)
{
if (type.ClrType != typeof(object)
&& value.GetType() != type.ClrType
&& _converter.TryConvert(value.GetType(),
type.ClrType, value,
out object converted))
{
return converted;
}
return value;
}

Expand Down
154 changes: 80 additions & 74 deletions src/Core/Subscriptions.Redis.Tests/RedisIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,85 +42,91 @@ public RedisIntegrationTests()
}

[Fact]
public async Task Subscribe()
public Task Subscribe()
{
// arrange
var services = new ServiceCollection();
services.AddRedisSubscriptionProvider(_configuration);

IServiceProvider serviceProvider = services.BuildServiceProvider();

var cts = new CancellationTokenSource(30000);
string name = "field_" + Guid.NewGuid().ToString("N");
IQueryExecutor executor = SchemaBuilder.New()
.AddServices(serviceProvider)
.AddQueryType(d => d
.Name("foo")
.Field("a")
.Resolver("b"))
.AddSubscriptionType(d => d.Name("bar")
.Field(name)
.Resolver("baz"))
.Create()
.MakeExecutable();

var eventDescription = new EventDescription(name);
var outgoing = new EventMessage(eventDescription, "bar");

IExecutionResult result = executor.Execute(
"subscription { " + name + " }");

// act
await _sender.SendAsync(outgoing);

// assert
var stream = (IResponseStream)result;
IReadOnlyQueryResult message = await stream.ReadAsync(cts.Token);
Assert.Equal("baz", message.Data.First().Value);
stream.Dispose();
return TestHelper.TryTest(async () =>
{
// arrange
var services = new ServiceCollection();
services.AddRedisSubscriptionProvider(_configuration);

IServiceProvider serviceProvider = services.BuildServiceProvider();

var cts = new CancellationTokenSource(30000);
string name = "field_" + Guid.NewGuid().ToString("N");
IQueryExecutor executor = SchemaBuilder.New()
.AddServices(serviceProvider)
.AddQueryType(d => d
.Name("foo")
.Field("a")
.Resolver("b"))
.AddSubscriptionType(d => d.Name("bar")
.Field(name)
.Resolver("baz"))
.Create()
.MakeExecutable();

var eventDescription = new EventDescription(name);
var outgoing = new EventMessage(eventDescription, "bar");

IExecutionResult result = executor.Execute(
"subscription { " + name + " }");

// act
await _sender.SendAsync(outgoing);

// assert
var stream = (IResponseStream)result;
IReadOnlyQueryResult message = await stream.ReadAsync(cts.Token);
Assert.Equal("baz", message.Data.First().Value);
stream.Dispose();
});
}

[Fact]
public async Task Subscribe_With_ObjectValue()
public Task Subscribe_With_ObjectValue()
{
// arrange
var services = new ServiceCollection();
services.AddRedisSubscriptionProvider(_configuration);

IServiceProvider serviceProvider = services.BuildServiceProvider();

var cts = new CancellationTokenSource(30000);
string name = "field_" + Guid.NewGuid().ToString("N");
IQueryExecutor executor = SchemaBuilder.New()
.AddServices(serviceProvider)
.AddQueryType(d => d
.Name("foo")
.Field("a")
.Resolver("b"))
.AddSubscriptionType(d => d.Name("bar")
.Field(name)
.Argument("a", a => a.Type<FooType>())
.Resolver("baz"))
.Create()
.MakeExecutable();

var eventDescription = new EventDescription(name,
new ArgumentNode("a",
new ObjectValueNode(
new ObjectFieldNode("def", "xyz"))));
var outgoing = new EventMessage(eventDescription, "bar");

IExecutionResult result = executor.Execute(
"subscription { " + name + "(a: { def: \"xyz\" }) }");

// act
await _sender.SendAsync(outgoing);

// assert
var stream = (IResponseStream)result;
IReadOnlyQueryResult message = await stream.ReadAsync(cts.Token);
Assert.Equal("baz", message.Data.First().Value);
stream.Dispose();
return TestHelper.TryTest(async () =>
{
// arrange
var services = new ServiceCollection();
services.AddRedisSubscriptionProvider(_configuration);

IServiceProvider serviceProvider = services.BuildServiceProvider();

var cts = new CancellationTokenSource(30000);
string name = "field_" + Guid.NewGuid().ToString("N");
IQueryExecutor executor = SchemaBuilder.New()
.AddServices(serviceProvider)
.AddQueryType(d => d
.Name("foo")
.Field("a")
.Resolver("b"))
.AddSubscriptionType(d => d.Name("bar")
.Field(name)
.Argument("a", a => a.Type<FooType>())
.Resolver("baz"))
.Create()
.MakeExecutable();

var eventDescription = new EventDescription(name,
new ArgumentNode("a",
new ObjectValueNode(
new ObjectFieldNode("def", "xyz"))));
var outgoing = new EventMessage(eventDescription, "bar");

IExecutionResult result = executor.Execute(
"subscription { " + name + "(a: { def: \"xyz\" }) }");

// act
await _sender.SendAsync(outgoing);

// assert
var stream = (IResponseStream)result;
IReadOnlyQueryResult message = await stream.ReadAsync(cts.Token);
Assert.Equal("baz", message.Data.First().Value);
stream.Dispose();
});
}

public class FooType : InputObjectType
Expand Down
Loading