Skip to content
Open
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
6 changes: 3 additions & 3 deletions src/Core/Resolvers/CosmosQueryStructure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,14 @@ private static IEnumerable<LabelledColumn> GenerateQueryColumns(SelectionSetNode
[MemberNotNull(nameof(OrderByColumns))]
private void Init(IDictionary<string, object?> queryParams)
{
ISelection selection = _context.Selection;
Selection selection = _context.Selection;
ObjectType underlyingType = selection.Field.Type.NamedType<ObjectType>();

IsPaginated = QueryBuilder.IsPaginationType(underlyingType);
OrderByColumns = new();
if (IsPaginated)
{
FieldNode? fieldNode = ExtractQueryField(selection.SyntaxNode);
FieldNode? fieldNode = ExtractQueryField(selection.SyntaxNodes[0].Node);

if (fieldNode is not null)
{
Expand All @@ -139,7 +139,7 @@ private void Init(IDictionary<string, object?> queryParams)
}
else
{
Columns.AddRange(GenerateQueryColumns(selection.SyntaxNode.SelectionSet!, _context.Operation.Document, SourceAlias));
Columns.AddRange(GenerateQueryColumns(selection.SyntaxNodes[0].Node.SelectionSet!, _context.Operation.Document, SourceAlias));
string typeName = GraphQLUtils.TryExtractGraphQLFieldModelName(underlyingType.Directives, out string? modelName) ?
modelName :
underlyingType.Name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public SqlQueryStructure(
sqlMetadataProvider,
authorizationResolver,
ctx.Selection.Field,
ctx.Selection.SyntaxNode,
ctx.Selection.SyntaxNodes[0].Node,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is SyntaxNodes guaranteed to have at least 1 node?

// The outermost query is where we start, so this can define
// create the IncrementingInteger that will be shared between
// all subqueries in this query.
Expand Down Expand Up @@ -173,7 +173,7 @@ public SqlQueryStructure(
IsMultipleCreateOperation = isMultipleCreateOperation;

ObjectField schemaField = _ctx.Selection.Field;
FieldNode? queryField = _ctx.Selection.SyntaxNode;
FieldNode? queryField = _ctx.Selection.SyntaxNodes[0].Node;

IOutputType outputType = schemaField.Type;
_underlyingFieldType = outputType.NamedType<ObjectType>();
Expand All @@ -182,7 +182,7 @@ public SqlQueryStructure(

if (PaginationMetadata.IsPaginated)
{
if (queryField != null && queryField.SelectionSet != null)
if (queryField.SelectionSet != null)
{
// process pagination fields without overriding them
ProcessPaginationFields(queryField.SelectionSet.Selections);
Expand Down
2 changes: 1 addition & 1 deletion src/Core/Services/DetermineStatusCodeMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public async ValueTask InvokeAsync(RequestContext context)
}

contextData[ExecutionContextData.HttpStatusCode] = HttpStatusCode.BadRequest;
context.Result = singleResult.WithContextData(contextData.ToImmutable());
singleResult.ContextData = contextData.ToImmutable();
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/Core/Services/ExecutionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ fieldValue.ValueKind is not (JsonValueKind.Undefined or JsonValueKind.Null))
DateTimeType => DateTimeOffset.TryParse(fieldValue.GetString()!, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTimeOffset date) ? date : null, // for DW when datetime is null it will be in "" (double quotes) due to stringagg parsing and hence we need to ensure parsing is correct.
DateType => DateTimeOffset.TryParse(fieldValue.GetString()!, out DateTimeOffset date) ? date : null,
HotChocolate.Types.NodaTime.LocalTimeType => fieldValue.GetString()!.Equals("null", StringComparison.OrdinalIgnoreCase) ? null : LocalTimePattern.ExtendedIso.Parse(fieldValue.GetString()!).Value,
ByteArrayType => fieldValue.GetBytesFromBase64(),
Base64StringType => fieldValue.GetBytesFromBase64(),
BooleanType => fieldValue.GetBoolean(), // spec
UrlType => new Uri(fieldValue.GetString()!),
UuidType => fieldValue.GetGuid(),
Expand Down Expand Up @@ -508,7 +508,7 @@ public static InputObjectType InputObjectTypeFromIInputField(IInputValueDefiniti
{
return GetParametersFromSchemaAndQueryFields(
context.Selection.Field,
context.Selection.SyntaxNode,
context.Selection.SyntaxNodes[0].Node,
context.Variables);
}

Expand Down Expand Up @@ -627,7 +627,7 @@ private static IMetadata GetMetadataObjectField(IResolverContext context)
// e.g. metadata for index 4 will not exist. only 3.
// Depth: / 0 / 1 / 2 / 3 / 4
// Path: /books/items/items[0]/publishers/books
//
//
// To handle arbitrary nesting depths with sibling relationships, we need to include
// the relationship field path in the key. For example:
// - /entity/items[0]/rel1/nested uses key ::3::rel1
Expand Down
19 changes: 10 additions & 9 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.2" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="HotChocolate" Version="16.0.0-p.7.68" />
<PackageVersion Include="HotChocolate.AspNetCore" Version="16.0.0-p.7.68" />
<PackageVersion Include="HotChocolate.AspNetCore.Authorization" Version="16.0.0-p.7.68" />
<PackageVersion Include="HotChocolate.ModelContextProtocol" Version="16.0.0-p.7.68" />
<PackageVersion Include="HotChocolate.Types.NodaTime" Version="16.0.0-p.7.68" />
<PackageVersion Include="HotChocolate.Utilities.Introspection" Version="16.0.0-p.7.68" />
<PackageVersion Include="HotChocolate.Transport.Http" Version="16.0.0-p.7.68" />
<PackageVersion Include="HotChocolate.Diagnostics" Version="16.0.0-p.7.68" />
<PackageVersion Include="CookieCrumble" Version="16.0.0-p.7.68" />
<PackageVersion Include="HotChocolate" Version="16.0.0-p.11.2" />
<PackageVersion Include="HotChocolate.AspNetCore" Version="16.0.0-p.11.2" />
<PackageVersion Include="HotChocolate.AspNetCore.Authorization" Version="16.0.0-p.11.2" />
<PackageVersion Include="HotChocolate.Adapters.Mcp" Version="16.0.0-p.11.2" />
<PackageVersion Include="HotChocolate.Adapters.OpenApi" Version="16.0.0-p.11.2" />
<PackageVersion Include="HotChocolate.Types.NodaTime" Version="16.0.0-p.11.2" />
<PackageVersion Include="HotChocolate.Utilities.Introspection" Version="16.0.0-p.11.2" />
<PackageVersion Include="HotChocolate.Transport.Http" Version="16.0.0-p.11.2" />
<PackageVersion Include="HotChocolate.Diagnostics" Version="16.0.0-p.11.2" />
<PackageVersion Include="CookieCrumble" Version="16.0.0-p.11.2" />
<PackageVersion Include="Humanizer" Version="2.14.1" />
<PackageVersion Include="Humanizer.Core" Version="2.14.1" />
<PackageVersion Include="DotNetEnv" Version="3.0.0" />
Expand Down
16 changes: 12 additions & 4 deletions src/Service.GraphQLBuilder/CustomScalars/SingleType.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json;
using HotChocolate.Language;
using HotChocolate.Text.Json;
using HotChocolate.Types;

namespace Azure.DataApiBuilder.Service.GraphQLBuilder.CustomScalars
Expand Down Expand Up @@ -48,10 +50,16 @@ public SingleType(
Description = description;
}

protected override float ParseLiteral(IFloatValueLiteral valueSyntax) =>
valueSyntax.ToSingle();
protected override float OnCoerceInputLiteral(IFloatValueLiteral valueLiteral)
=> valueLiteral.ToSingle();

protected override FloatValueNode ParseValue(float runtimeValue) =>
new(runtimeValue);
protected override float OnCoerceInputValue(JsonElement inputValue)
=> inputValue.GetSingle();

public override void OnCoerceOutputValue(float runtimeValue, ResultElement resultValue)
=> resultValue.SetNumberValue(runtimeValue);

public override IValueNode OnValueToLiteral(float runtimeValue)
=> new FloatValueNode(runtimeValue);
}
}
14 changes: 7 additions & 7 deletions src/Service.GraphQLBuilder/GraphQLStoredProcedureBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ public static FieldDefinitionNode GenerateStoredProcedureSchema(

/// <summary>
/// Takes the result from DB as JsonDocument and formats it in a way that can be filtered by column
/// name. It parses the Json document into a list of Dictionary with key as result_column_name
/// with it's corresponding value.
/// name. It parses the JSON document into a list of Dictionary with key as result_column_name
/// with its corresponding value.
/// returns an empty list in case of no result
/// or stored-procedure is trying to read from DB without READ permission.
/// </summary>
Expand Down Expand Up @@ -156,12 +156,12 @@ private static Tuple<string, IValueNode> ConvertValueToGraphQLType(string defaul
{
Tuple<string, IValueNode> valueNode = paramValueType switch
{
UUID_TYPE => new(UUID_TYPE, new UuidType().ParseValue(Guid.Parse(defaultValueFromConfig))),
UUID_TYPE => new(UUID_TYPE, new UuidType().ValueToLiteral(Guid.Parse(defaultValueFromConfig))),
BYTE_TYPE => new(BYTE_TYPE, new IntValueNode(byte.Parse(defaultValueFromConfig))),
SHORT_TYPE => new(SHORT_TYPE, new IntValueNode(short.Parse(defaultValueFromConfig))),
INT_TYPE => new(INT_TYPE, new IntValueNode(int.Parse(defaultValueFromConfig))),
LONG_TYPE => new(LONG_TYPE, new IntValueNode(long.Parse(defaultValueFromConfig))),
SINGLE_TYPE => new(SINGLE_TYPE, new SingleType().ParseValue(float.Parse(defaultValueFromConfig))),
SINGLE_TYPE => new(SINGLE_TYPE, new SingleType().ValueToLiteral(float.Parse(defaultValueFromConfig))),
FLOAT_TYPE => new(FLOAT_TYPE, new FloatValueNode(double.Parse(defaultValueFromConfig))),
DECIMAL_TYPE => new(DECIMAL_TYPE, new FloatValueNode(decimal.Parse(defaultValueFromConfig))),
STRING_TYPE => new(STRING_TYPE, new StringValueNode(defaultValueFromConfig)),
Expand All @@ -174,10 +174,10 @@ var s when s.Equals("true", StringComparison.OrdinalIgnoreCase) => true,
var s when s.Equals("false", StringComparison.OrdinalIgnoreCase) => false,
_ => throw new FormatException($"String '{defaultValueFromConfig}' was not recognized as a valid Boolean.")
})),
DATETIME_TYPE => new(DATETIME_TYPE, new DateTimeType().ParseResult(
DATETIME_TYPE => new(DATETIME_TYPE, new DateTimeType().ValueToLiteral(
DateTime.Parse(defaultValueFromConfig, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal))),
BYTEARRAY_TYPE => new(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(Convert.FromBase64String(defaultValueFromConfig))),
LOCALTIME_TYPE => new(LOCALTIME_TYPE, new HotChocolate.Types.NodaTime.LocalTimeType().ParseResult(LocalTimePattern.ExtendedIso.Parse(defaultValueFromConfig).Value)),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do both ParseResult and ParseValue map to ValueToLiteral now?

BYTEARRAY_TYPE => new(BYTEARRAY_TYPE, new Base64StringType().ValueToLiteral(Convert.FromBase64String(defaultValueFromConfig))),
LOCALTIME_TYPE => new(LOCALTIME_TYPE, new HotChocolate.Types.NodaTime.LocalTimeType().ValueToLiteral(LocalTimePattern.ExtendedIso.Parse(defaultValueFromConfig).Value)),
_ => throw new NotSupportedException(message: $"The {defaultValueFromConfig} parameter's value type [{paramValueType}] is not supported.")
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ protected override void Configure(IInputObjectTypeDescriptor descriptor)
descriptor.Field(FLOAT_TYPE).Type<FloatType>();
descriptor.Field(DECIMAL_TYPE).Type<DecimalType>();
descriptor.Field(DATETIME_TYPE).Type<DateTimeType>();
descriptor.Field(BYTEARRAY_TYPE).Type<ByteArrayType>();
descriptor.Field(BYTEARRAY_TYPE).Type<Base64StringType>();
descriptor.Field(LOCALTIME_TYPE).Type<HotChocolate.Types.NodaTime.LocalTimeType>();
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/Service.GraphQLBuilder/Sql/SchemaConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -582,16 +582,16 @@ public static IValueNode CreateValueNodeFromDbObjectMetadata(object metadataValu
short value => new ObjectValueNode(new ObjectFieldNode(SHORT_TYPE, new IntValueNode(value))),
int value => new ObjectValueNode(new ObjectFieldNode(INT_TYPE, value)),
long value => new ObjectValueNode(new ObjectFieldNode(LONG_TYPE, new IntValueNode(value))),
Guid value => new ObjectValueNode(new ObjectFieldNode(UUID_TYPE, new UuidType().ParseValue(value))),
Guid value => new ObjectValueNode(new ObjectFieldNode(UUID_TYPE, new UuidType().ValueToLiteral(value))),
string value => new ObjectValueNode(new ObjectFieldNode(STRING_TYPE, value)),
bool value => new ObjectValueNode(new ObjectFieldNode(BOOLEAN_TYPE, value)),
float value => new ObjectValueNode(new ObjectFieldNode(SINGLE_TYPE, new SingleType().ParseValue(value))),
float value => new ObjectValueNode(new ObjectFieldNode(SINGLE_TYPE, new SingleType().ValueToLiteral(value))),
double value => new ObjectValueNode(new ObjectFieldNode(FLOAT_TYPE, value)),
decimal value => new ObjectValueNode(new ObjectFieldNode(DECIMAL_TYPE, new FloatValueNode(value))),
DateTimeOffset value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ParseValue(value))),
DateTime value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ParseResult(value))),
byte[] value => new ObjectValueNode(new ObjectFieldNode(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(value))),
TimeOnly value => new ObjectValueNode(new ObjectFieldNode(LOCALTIME_TYPE, new HotChocolate.Types.NodaTime.LocalTimeType().ParseResult(value))),
DateTimeOffset value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ValueToLiteral(value))),
DateTime value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ValueToLiteral(value))),
byte[] value => new ObjectValueNode(new ObjectFieldNode(BYTEARRAY_TYPE, new Base64StringType().ValueToLiteral(value))),
TimeOnly value => new ObjectValueNode(new ObjectFieldNode(LOCALTIME_TYPE, new HotChocolate.Types.NodaTime.LocalTimeType().ValueToLiteral(value))),
_ => throw new DataApiBuilderException(
message: $"The type {metadataValue.GetType()} is not supported as a GraphQL default value",
statusCode: HttpStatusCode.InternalServerError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using HotChocolate;
using HotChocolate.Execution;
using HotChocolate.Resolvers;
using HotChocolate.Text.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
Expand Down Expand Up @@ -118,18 +119,18 @@ public async Task TestMultiSourceQuery()
Assert.AreEqual(1, cosmosQueryEngine.Invocations.Count, "Cosmos query engine should be invoked for multi-source query as an entity belongs to cosmos db.");

OperationResult singleResult = result.ExpectOperationResult();
Assert.IsNull(singleResult.Errors, "There should be no errors in processing of multisource query.");
Assert.IsTrue(singleResult.Errors.IsEmpty, "There should be no errors in processing of multisource query.");
Assert.IsNotNull(singleResult.Data, "Data should be returned for multisource query.");
IReadOnlyDictionary<string, object> data = singleResult.Data;
Assert.IsTrue(data.TryGetValue(QUERY_NAME_1, out object queryNode1), $"Query node for {QUERY_NAME_1} should have data populated.");
Assert.IsTrue(data.TryGetValue(QUERY_NAME_2, out object queryNode2), $"Query node for {QUERY_NAME_2} should have data populated.");
ResultDocument document = (ResultDocument)singleResult.Data.Value.Value;
Assert.IsTrue(document.Data.TryGetProperty(QUERY_NAME_1, out ResultElement queryNode1), $"Query node for {QUERY_NAME_1} should have data populated.");
Assert.IsTrue(document.Data.TryGetProperty(QUERY_NAME_2, out ResultElement queryNode2), $"Query node for {QUERY_NAME_2} should have data populated.");

KeyValuePair<string, object> firstEntryMap1 = ((IReadOnlyDictionary<string, object>)queryNode1).FirstOrDefault();
KeyValuePair<string, object> firstEntryMap2 = ((IReadOnlyDictionary<string, object>)queryNode2).FirstOrDefault();
ResultProperty firstEntryMap1 = queryNode1.EnumerateObject().FirstOrDefault();
ResultProperty firstEntryMap2 = queryNode2.EnumerateObject().FirstOrDefault();

// validate that the data returned for the queries we did matches the moq data we set up for the respective query engines.
Assert.AreEqual("db1", firstEntryMap1.Value, $"Data returned for {QUERY_NAME_1} is incorrect for multi-source query");
Assert.AreEqual("db2", firstEntryMap2.Value, $"Data returned for {QUERY_NAME_2} is incorrect for multi-source query");
Assert.AreEqual("db1", firstEntryMap1.Value.GetString(), $"Data returned for {QUERY_NAME_1} is incorrect for multi-source query");
Assert.AreEqual("db2", firstEntryMap2.Value.GetString(), $"Data returned for {QUERY_NAME_2} is incorrect for multi-source query");
}

/// <summary>
Expand Down
Loading