Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,51 @@ void Action() => helper.CoerceVariableValues(
Assert.Throws<ArgumentNullException>(Action);
}

[Fact]
public void Coerce_String_WithEscapedQuotes_IsUnescaped()
{
// arrange
var schema = SchemaBuilder.New().AddStarWarsTypes().Create();

var variableDefinitions = new List<VariableDefinitionNode>
{
new VariableDefinitionNode(
null,
new VariableNode("abc"),
description: null,
new NamedTypeNode("String"),
null,
Array.Empty<DirectiveNode>())
};

using var variableValues = JsonDocument.Parse(
"""
{
"abc": "tag:\"type_portable-lamp\""
}
""");

var coercedValues = new Dictionary<string, VariableValue>();
var featureProvider = new MockFeatureProvider();
var helper = new VariableCoercionHelper(new());

// act
helper.CoerceVariableValues(
schema, variableDefinitions, variableValues.RootElement, coercedValues, featureProvider);

// assert
Assert.Collection(coercedValues,
t =>
{
Assert.Equal("abc", t.Key);
Assert.Equal("String", Assert.IsType<StringType>(t.Value.Type).Name);
Assert.Equal("tag:\"type_portable-lamp\"", t.Value.RuntimeValue);
Assert.Equal(
"tag:\"type_portable-lamp\"",
Assert.IsType<StringValueNode>(t.Value.ValueLiteral).Value);
});
}

[Fact]
public void Coerce_Nullable_String_Variable_With_Default_Where_Value_Is_Not_Provided()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,17 +435,10 @@ private readonly IValueNode ParseLiteral(JsonElement element, int depth)
return BooleanValueNode.False;

case JsonValueKind.String:
{
var rawValue = element.GetRawText();
var utf8Value = System.Text.Encoding.UTF8.GetBytes(rawValue);
var span = utf8Value.AsSpan();
span = span[1..^1]; // Remove quotes
var segment = WriteValue(span);
return new StringValueNode(null, segment, false);
}
var stringValue = element.GetString()!;
return new StringValueNode(null, stringValue, false);

case JsonValueKind.Number:
{
var rawValue = element.GetRawText();
var utf8Value = System.Text.Encoding.UTF8.GetBytes(rawValue);
var span = utf8Value.AsSpan();
Expand All @@ -462,7 +455,6 @@ private readonly IValueNode ParseLiteral(JsonElement element, int depth)
}

return new IntValueNode(segment);
}

case JsonValueKind.Array:
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,46 @@
using HotChocolate.Transport;
using HotChocolate.Transport.Http;
using Microsoft.Extensions.DependencyInjection;

namespace HotChocolate.Fusion;

public class VariableCoercionTests : FusionTestBase
{
[Fact]
public async Task String_With_Quotes()
{
// arrange
using var serverA = CreateSourceSchema(
"A",
r => r.AddQueryType<SourceSchema1.Query>());

using var gateway = await CreateCompositeSchemaAsync(
[
("A", serverA)
]);

// act
using var client = GraphQLHttpClient.Create(gateway.CreateClient());

var request = new OperationRequest(
"""
query testQuery($input: String!) {
field(input: $input)
}
""",
variables: new Dictionary<string, object?>
{
["input"] = "tag:\"type_portable-lamp\""
});

using var result = await client.PostAsync(
request,
new Uri("http://localhost:5000/graphql"));

// assert
await MatchSnapshotAsync(gateway, request, result);
}

[Fact]
public async Task InputObject_Invalid_Field()
{
Expand Down Expand Up @@ -418,4 +454,12 @@ query testQuery($pet: Pet!) {
// assert
await MatchSnapshotAsync(gateway, request, result);
}

public static class SourceSchema1
{
public class Query
{
public string GetField(string input) => input;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
title: String_With_Quotes
request:
document: |
query testQuery(
$input: String!
) {
field(input: $input)
}
variables: |
{
"input": "tag:\u0022type_portable-lamp\u0022"
}
response:
body: |
{
"data": {
"field": "tag:\u0022type_portable-lamp\u0022"
}
}
sourceSchemas:
- name: A
schema: |
schema {
query: Query
}

type Query {
field(input: String!): String!
}
interactions:
- request:
document: |
query testQuery_9dd2f586_1(
$input: String!
) {
field(input: $input)
}
variables: |
{
"input": "tag:\u0022type_portable-lamp\u0022"
}
response:
results:
- |
{
"data": {
"field": "tag:\u0022type_portable-lamp\u0022"
}
}
operationPlan:
operation:
- document: |
query testQuery(
$input: String!
) {
field(input: $input)
}
name: testQuery
hash: 9dd2f5869cf5d627883a05994b98b50a
searchSpace: 1
expandedNodes: 1
nodes:
- id: 1
type: Operation
schema: A
operation: |
query testQuery_9dd2f586_1(
$input: String!
) {
field(input: $input)
}
forwardedVariables:
- input
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Text.Json;
using HotChocolate.Features;
using HotChocolate.Language;

namespace HotChocolate.Fusion.Execution;

public class VariableCoercionHelperTests : FusionTestBase
{
[Fact]
public void Coerce_String_WithEscapedQuotes_IsUnescaped()
{
// arrange
var schema = CreateCompositeSchema();

var variableDefinitions = new List<VariableDefinitionNode>
{
new VariableDefinitionNode(
null,
new VariableNode("abc"),
description: null,
new NamedTypeNode("String"),
null,
Array.Empty<DirectiveNode>())
};

using var variableValues = JsonDocument.Parse(
"""
{
"abc": "tag:\"type_portable-lamp\""
}
""");

// act
var success = VariableCoercionHelper.TryCoerceVariableValues(
new MockFeatureProvider(),
schema,
variableDefinitions,
variableValues.RootElement,
out var coercedVariableValues,
out var error);

// assert
Assert.True(success, error?.Message);
Assert.Null(error);
Assert.NotNull(coercedVariableValues);

var stringValue = Assert.IsType<StringValueNode>(coercedVariableValues["abc"].Value);
Assert.Equal("tag:\"type_portable-lamp\"", stringValue.Value);
}

private sealed class MockFeatureProvider : IFeatureProvider
{
public IFeatureCollection Features { get; } = new FeatureCollection();
}
}
10 changes: 2 additions & 8 deletions src/HotChocolate/Language/src/Language.Web/JsonValueParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,10 @@ internal IValueNode Parse(JsonElement element, int depth)
return BooleanValueNode.False;

case JsonValueKind.String:
{
var value = JsonMarshal.GetRawUtf8Value(element);
value = value.Slice(1, value.Length - 2); // Remove quotes.
var segment = WriteValue(value);
return new StringValueNode(null, segment, false);
}
var stringValue = element.GetString()!;
return new StringValueNode(null, stringValue, false);

case JsonValueKind.Number:
{
var value = JsonMarshal.GetRawUtf8Value(element);
var segment = WriteValue(value);

Expand All @@ -113,7 +108,6 @@ internal IValueNode Parse(JsonElement element, int depth)
}

return new IntValueNode(segment);
}

case JsonValueKind.Array:
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Text.Json;

namespace HotChocolate.Language;

public class JsonValueParserTests
{
[Fact]
public void Parse_JsonElement_StringWithEscapedQuotes_IsUnescaped()
{
// arrange
using var document = JsonDocument.Parse(
"""
"tag:\"type_portable-lamp\""
""");
var parser = new JsonValueParser();

// act
var value = parser.Parse(document.RootElement);

// assert
var stringValue = Assert.IsType<StringValueNode>(value);
Assert.Equal("tag:\"type_portable-lamp\"", stringValue.Value);
}
}
Loading