diff --git a/DataGateway.Service.Tests/CosmosTests/MutationTests.cs b/DataGateway.Service.Tests/CosmosTests/MutationTests.cs
index 9621df58e9..f3abe6e78a 100644
--- a/DataGateway.Service.Tests/CosmosTests/MutationTests.cs
+++ b/DataGateway.Service.Tests/CosmosTests/MutationTests.cs
@@ -81,7 +81,7 @@ public async Task CanCreateItemWithoutVariables()
name
}}
}}";
- JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", mutation, new());
+ JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", mutation, variables: new());
// Validate results
Assert.AreEqual(id, response.GetProperty("id").GetString());
@@ -100,7 +100,7 @@ public async Task CanDeleteItemWithoutVariables()
name
}}
}}";
- _ = await ExecuteGraphQLRequestAsync("addPlanet", addMutation, new());
+ _ = await ExecuteGraphQLRequestAsync("addPlanet", addMutation, variables: new());
// Run mutation delete item;
string deleteMutation = $@"
@@ -110,12 +110,43 @@ public async Task CanDeleteItemWithoutVariables()
name
}}
}}";
- JsonElement response = await ExecuteGraphQLRequestAsync("deletePlanet", deleteMutation, new());
+ JsonElement response = await ExecuteGraphQLRequestAsync("deletePlanet", deleteMutation, variables: new());
// Validate results
Assert.IsNull(response.GetProperty("id").GetString());
}
+ [TestMethod]
+ public async Task MutationMissingInputReturnError()
+ {
+ // Run mutation Add planet without any input
+ string mutation = $@"
+mutation {{
+ addPlanet {{
+ id
+ name
+ }}
+}}";
+ JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", mutation, variables: new());
+ Assert.AreEqual("inputDict is missing", response[0].GetProperty("message").ToString());
+ }
+
+ [TestMethod]
+ public async Task MutationMissingRequiredIdReturnError()
+ {
+ // Run mutation Add planet without id
+ const string name = "test_name";
+ string mutation = $@"
+mutation {{
+ addPlanet ( name: ""{name}"") {{
+ id
+ name
+ }}
+}}";
+ JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", mutation, variables: new());
+ Assert.AreEqual("id field is mandatory", response[0].GetProperty("message").ToString());
+ }
+
///
/// Runs once after all tests in this class are executed
///
diff --git a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs
index 8d111d7c1f..9445926038 100644
--- a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs
+++ b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -69,10 +70,14 @@ public async Task GetPaginatedWithVariables()
// Run query
JsonElement response = await ExecuteGraphQLRequestAsync("planetList", PlanetListQuery);
int actualElements = response.GetArrayLength();
+ List responseTotal = new();
+ ConvertJsonElementToStringList(response, responseTotal);
+
// Run paginated query
int totalElementsFromPaginatedQuery = 0;
string continuationToken = null;
const int pagesize = 5;
+ List pagedResponse = new();
do
{
@@ -80,10 +85,12 @@ public async Task GetPaginatedWithVariables()
JsonElement continuation = page.GetProperty("endCursor");
continuationToken = continuation.ToString();
totalElementsFromPaginatedQuery += page.GetProperty("items").GetArrayLength();
+ ConvertJsonElementToStringList(page.GetProperty("items"), pagedResponse);
} while (!string.IsNullOrEmpty(continuationToken));
// Validate results
Assert.AreEqual(actualElements, totalElementsFromPaginatedQuery);
+ Assert.IsTrue(responseTotal.SequenceEqual(pagedResponse));
}
[TestMethod]
@@ -110,10 +117,14 @@ public async Task GetPaginatedWithoutVariables()
// Run query
JsonElement response = await ExecuteGraphQLRequestAsync("planetList", PlanetListQuery);
int actualElements = response.GetArrayLength();
+ List responseTotal = new();
+ ConvertJsonElementToStringList(response, responseTotal);
+
// Run paginated query
int totalElementsFromPaginatedQuery = 0;
string continuationToken = null;
const int pagesize = 5;
+ List pagedResponse = new();
do
{
@@ -129,14 +140,123 @@ public async Task GetPaginatedWithoutVariables()
}}
}}";
- JsonElement page = await ExecuteGraphQLRequestAsync("planets", planetConnectionQueryStringFormat, new());
+ JsonElement page = await ExecuteGraphQLRequestAsync("planets", planetConnectionQueryStringFormat, variables: new());
JsonElement continuation = page.GetProperty("endCursor");
continuationToken = continuation.ToString();
totalElementsFromPaginatedQuery += page.GetProperty("items").GetArrayLength();
+ ConvertJsonElementToStringList(page.GetProperty("items"), pagedResponse);
} while (!string.IsNullOrEmpty(continuationToken));
// Validate results
Assert.AreEqual(actualElements, totalElementsFromPaginatedQuery);
+ Assert.IsTrue(responseTotal.SequenceEqual(pagedResponse));
+ }
+
+ ///
+ /// Query List Type with input parameters
+ ///
+ ///
+ [TestMethod]
+ public async Task GetListTypeWithParameters()
+ {
+ string id = _idList[0];
+ string query = @$"
+query {{
+ getPlanetListById (id: ""{id}"") {{
+ id
+ name
+ }}
+}}";
+
+ JsonElement response = await ExecuteGraphQLRequestAsync("getPlanetListById", query);
+
+ // Validate results
+ Assert.AreEqual(1, response.GetArrayLength());
+ Assert.AreEqual(id, response[0].GetProperty("id").ToString());
+ }
+
+ ///
+ /// Query single item by non-primary key field, found no match
+ ///
+ ///
+ [TestMethod]
+ public async Task GetByNonePrimaryFieldResultNotFound()
+ {
+ string name = "non-existed name";
+ string query = @$"
+query {{
+ getPlanetByName (name: ""{name}"") {{
+ id
+ name
+ }}
+}}";
+
+ JsonElement response = await ExecuteGraphQLRequestAsync("getPlanetByName", query);
+
+ // Validate results
+ Assert.IsNull(response.Deserialize());
+ }
+
+ ///
+ /// Query single item by non-primary key field, found record back
+ ///
+ ///
+ [TestMethod]
+ public async Task GetByNonPrimaryFieldReturnsResult()
+ {
+ string name = "test name";
+ string query = @$"
+query {{
+ getPlanetByName (name: ""{name}"") {{
+ id
+ name
+ }}
+}}";
+
+ JsonElement response = await ExecuteGraphQLRequestAsync("getPlanetByName", query);
+
+ // Validate results
+ Assert.AreEqual(name, response.GetProperty("name").ToString());
+ }
+
+ ///
+ /// Query result with nested object
+ ///
+ ///
+ [TestMethod]
+ public async Task GetByPrimaryKeyWithInnerObject()
+ {
+ // Run query
+ string id = _idList[0];
+ string query = @$"
+query {{
+ planetById (id: ""{id}"") {{
+ id
+ name
+ character {{
+ id
+ name
+ }}
+ }}
+}}";
+ JsonElement response = await ExecuteGraphQLRequestAsync("planetById", query);
+
+ // Validate results
+ Assert.AreEqual(id, response.GetProperty("id").GetString());
+ }
+
+ private static void ConvertJsonElementToStringList(JsonElement ele, List strList)
+ {
+ if (ele.ValueKind == JsonValueKind.Array)
+ {
+ JsonElement.ArrayEnumerator enumerator = ele.EnumerateArray();
+
+ while (enumerator.MoveNext())
+ {
+ JsonElement prop = enumerator.Current;
+ strList.Add(prop.ToString());
+ }
+ }
}
///
diff --git a/DataGateway.Service.Tests/CosmosTests/TestBase.cs b/DataGateway.Service.Tests/CosmosTests/TestBase.cs
index 1d4239266f..81ff8046b0 100644
--- a/DataGateway.Service.Tests/CosmosTests/TestBase.cs
+++ b/DataGateway.Service.Tests/CosmosTests/TestBase.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
+using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
@@ -42,6 +43,8 @@ type Query {
getPlanet(id: ID, name: String): Planet
planetList: [Planet]
planets(first: Int, after: String): PlanetConnection
+ getPlanetListById(id: ID): [Planet]
+ getPlanetByName(name: String): Planet
}
type Mutation {
@@ -65,7 +68,8 @@ type Character {
type Planet {
id : ID,
- name : String
+ name : String,
+ character: Character
}";
_metadataStoreProvider.GraphQLSchema = jsonString;
@@ -109,9 +113,11 @@ private static DefaultHttpContext GetHttpContextWithBody(string data)
HttpRequestMessage request = new();
MemoryStream stream = new(Encoding.UTF8.GetBytes(data));
request.Method = HttpMethod.Post;
+ ClaimsPrincipal user = new(new ClaimsIdentity(authenticationType: "Bearer"));
DefaultHttpContext httpContext = new()
{
- Request = { Body = stream, ContentLength = stream.Length }
+ Request = { Body = stream, ContentLength = stream.Length },
+ User = user
};
return httpContext;
}
@@ -182,7 +188,8 @@ internal static async Task ExecuteGraphQLRequestAsync(string queryN
if (graphQLResult.TryGetProperty("errors", out JsonElement errors))
{
- Assert.Fail(errors.GetRawText());
+ // to validate expected errors and error message
+ return errors;
}
return graphQLResult.GetProperty("data").GetProperty(queryName);
diff --git a/DataGateway.Service.Tests/CosmosTests/TestHelper.cs b/DataGateway.Service.Tests/CosmosTests/TestHelper.cs
index a2bf916320..6ec6563fde 100644
--- a/DataGateway.Service.Tests/CosmosTests/TestHelper.cs
+++ b/DataGateway.Service.Tests/CosmosTests/TestHelper.cs
@@ -43,6 +43,7 @@ public static object GetItem(string id)
return new
{
id = id,
+ name = "test name",
myProp = "a value",
myIntProp = 4,
myBooleanProp = true,
@@ -56,6 +57,14 @@ public static object GetItem(string id)
lastName = "the last name",
zipCode = 784298
}
+ },
+ character = new
+ {
+ id = id,
+ name = "planet character",
+ type = "Mars",
+ homePlanet = 1,
+ primaryFunction = "test function"
}
};
}
diff --git a/DataGateway.Service/Resolvers/CosmosMutationEngine.cs b/DataGateway.Service/Resolvers/CosmosMutationEngine.cs
index bdba73fbf6..66ee4e18ba 100644
--- a/DataGateway.Service/Resolvers/CosmosMutationEngine.cs
+++ b/DataGateway.Service/Resolvers/CosmosMutationEngine.cs
@@ -32,7 +32,7 @@ private async Task ExecuteAsync(IDictionary inputDict,
JObject jObject;
- if (inputDict != null)
+ if (inputDict != null && inputDict.Count > 0)
{
// TODO: optimize this multiple round of serialization/deserialization
string json = JsonConvert.SerializeObject(inputDict);
@@ -75,7 +75,7 @@ private async Task ExecuteAsync(IDictionary inputDict,
break;
default:
- throw new NotSupportedException($"unsupprted operation type: {resolver.OperationType.ToString()}");
+ throw new NotSupportedException($"unsupported operation type: {resolver.OperationType.ToString()}");
}
return response.Resource;
diff --git a/DataGateway.Service/Resolvers/CosmosQueryEngine.cs b/DataGateway.Service/Resolvers/CosmosQueryEngine.cs
index dd32aec7e4..9a881167ea 100644
--- a/DataGateway.Service/Resolvers/CosmosQueryEngine.cs
+++ b/DataGateway.Service/Resolvers/CosmosQueryEngine.cs
@@ -1,6 +1,7 @@
# nullable disable
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
@@ -67,49 +68,48 @@ public async Task> ExecuteAsync(IMiddlewareContex
requestContinuation = Base64Decode(structure.Continuation);
}
- FeedResponse firstPage = await container.GetItemQueryIterator(querySpec, requestContinuation, queryRequestOptions).ReadNextAsync();
-
- if (structure.IsPaginated)
+ using (FeedIterator query = container.GetItemQueryIterator(querySpec, requestContinuation, queryRequestOptions))
{
- JArray jarray = new();
- IEnumerator enumerator = firstPage.GetEnumerator();
- while (enumerator.MoveNext())
+ do
{
- JObject item = enumerator.Current;
- jarray.Add(item);
+ FeedResponse page = await query.ReadNextAsync();
+
+ // For connection type, return first page result directly
+ if (structure.IsPaginated)
+ {
+ JArray jarray = new();
+ IEnumerator enumerator = page.GetEnumerator();
+ while (enumerator.MoveNext())
+ {
+ JObject item = enumerator.Current;
+ jarray.Add(item);
+ }
+
+ string responseContinuation = page.ContinuationToken;
+ if (string.IsNullOrEmpty(responseContinuation))
+ {
+ responseContinuation = null;
+ }
+
+ JObject res = new(
+ new JProperty("endCursor", Base64Encode(responseContinuation)),
+ new JProperty("hasNextPage", responseContinuation != null),
+ new JProperty("items", jarray));
+
+ // This extra deserialize/serialization will be removed after moving to Newtonsoft from System.Text.Json
+ return new Tuple(JsonDocument.Parse(res.ToString()), null);
+ }
+
+ if (page.Count > 0)
+ {
+ return new Tuple(JsonDocument.Parse(page.First().ToString()), null);
+ }
}
-
- string responseContinuation = firstPage.ContinuationToken;
- if (string.IsNullOrEmpty(responseContinuation))
- {
- responseContinuation = null;
- }
-
- JObject res = new(
- new JProperty("endCursor", Base64Encode(responseContinuation)),
- new JProperty("hasNextPage", responseContinuation != null),
- new JProperty("items", jarray));
-
- // This extra deserialize/serialization will be removed after moving to Newtonsoft from System.Text.Json
- return new Tuple(JsonDocument.Parse(res.ToString()), null);
+ while (query.HasMoreResults);
}
- static JObject FindFirstItem(IEnumerator iterator)
- {
- JObject firstItem;
- if (iterator.MoveNext() && (firstItem = iterator.Current) == null)
- {
- return FindFirstItem(iterator);
- }
-
- return iterator.Current;
- }
-
- JObject firstItem = FindFirstItem(firstPage.GetEnumerator());
-
- JsonDocument jsonDocument = JsonDocument.Parse(firstItem.ToString());
-
- return new Tuple(jsonDocument, null);
+ // Return empty list when query gets no result back
+ return new Tuple(null, null);
}
public async Task, IMetadata>> ExecuteListAsync(IMiddlewareContext context, IDictionary parameters)
@@ -124,12 +124,9 @@ public async Task, IMetadata>> ExecuteListAsync(
Container container = _clientProvider.Client.GetDatabase(structure.Database).GetContainer(structure.Container);
QueryDefinition querySpec = new(_queryBuilder.Build(structure));
- if (parameters != null)
+ foreach (KeyValuePair parameterEntry in structure.Parameters)
{
- foreach (KeyValuePair parameterEntry in parameters)
- {
- querySpec.WithParameter("@" + parameterEntry.Key, parameterEntry.Value);
- }
+ querySpec.WithParameter("@" + parameterEntry.Key, parameterEntry.Value);
}
FeedIterator resultSetIterator = container.GetItemQueryIterator(querySpec);
diff --git a/DataGateway.Service/Resolvers/CosmosQueryStructure.cs b/DataGateway.Service/Resolvers/CosmosQueryStructure.cs
index 6b9aaedd9f..d033b6b048 100644
--- a/DataGateway.Service/Resolvers/CosmosQueryStructure.cs
+++ b/DataGateway.Service/Resolvers/CosmosQueryStructure.cs
@@ -46,12 +46,12 @@ private void Init(IDictionary queryParams)
if (fieldNode != null)
{
- Columns.AddRange(fieldNode.SelectionSet!.Selections.Select(x => new LabelledColumn(_containerAlias, "", x.ToString())));
+ Columns.AddRange(fieldNode.SelectionSet!.Selections.Select(x => new LabelledColumn(_containerAlias, "", x.GetNodes().First().ToString())));
}
}
else
{
- Columns.AddRange(selection.SyntaxNode.SelectionSet!.Selections.Select(x => new LabelledColumn(_containerAlias, "", x.ToString())));
+ Columns.AddRange(selection.SyntaxNode.SelectionSet!.Selections.Select(x => new LabelledColumn(_containerAlias, "", x.GetNodes().First().ToString())));
}
Container = graphqlType.ContainerName;
diff --git a/DataGateway.Service/Services/GraphQLService.cs b/DataGateway.Service/Services/GraphQLService.cs
index 6a4e324335..3d2731b8f4 100644
--- a/DataGateway.Service/Services/GraphQLService.cs
+++ b/DataGateway.Service/Services/GraphQLService.cs
@@ -117,6 +117,7 @@ private void MakeSchemaExecutable()
{
Console.Error.WriteLine(error.Exception.Message);
Console.Error.WriteLine(error.Exception.StackTrace);
+ return error.WithMessage(error.Exception.Message);
}
return error;