From 3534a7904287224b16dea361480d3e9b32f4a884 Mon Sep 17 00:00:00 2001 From: tarazou9 <40870773+tarazou9@users.noreply.github.com> Date: Sun, 8 May 2022 22:15:20 -0400 Subject: [PATCH 01/10] Add unit tests and fix a few issues --- .../MetadataStoreProviderForTest.cs | 8 ++ .../CosmosTests/MutationTests.cs | 55 +++++++++++ .../CosmosTests/QueryTests.cs | 95 +++++++++++++++++++ .../CosmosTests/TestBase.cs | 22 ++++- .../CosmosTests/TestHelper.cs | 1 + .../Resolvers/CosmosMutationEngine.cs | 4 +- .../Resolvers/CosmosQueryEngine.cs | 14 +-- .../Services/GraphQLService.cs | 1 + 8 files changed, 188 insertions(+), 12 deletions(-) diff --git a/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs b/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs index a3115d85ff..685a004a90 100644 --- a/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs +++ b/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs @@ -37,6 +37,14 @@ public void StoreMutationResolver(MutationResolver mutationResolver) MutationResolvers.Add(mutationResolver.Id, mutationResolver); } + public void RemoveMutationResolver(string mutationResolverId) + { + if(MutationResolvers.ContainsKey(mutationResolverId)) + { + MutationResolvers.Remove(mutationResolverId); + } + } + public void StoreGraphQLType(string name, GraphQLType graphQLType) { GraphQLTypes.Add(name, graphQLType); diff --git a/DataGateway.Service.Tests/CosmosTests/MutationTests.cs b/DataGateway.Service.Tests/CosmosTests/MutationTests.cs index 9621df58e9..5b1b3ab5f9 100644 --- a/DataGateway.Service.Tests/CosmosTests/MutationTests.cs +++ b/DataGateway.Service.Tests/CosmosTests/MutationTests.cs @@ -116,6 +116,61 @@ public async Task CanDeleteItemWithoutVariables() 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, 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, new()); + Assert.AreEqual("id field is mandatory", response[0].GetProperty("message").ToString()); + } + + [TestMethod] + public async Task InvalidResolverOperationTypeReturnError() + { + //Register invalid operation type resolver + RemoveMutationResolver("addPlanet"); + RegisterMutationResolver("addPlanet", DATABASE_NAME, _containerName, "None"); + + string id = Guid.NewGuid().ToString(); + const string name = "test_name"; + string addMutation = $@" +mutation {{ + addPlanet (id: ""{id}"", name: ""{name}"") {{ + id + name + }} +}}"; + JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", addMutation, new()); + Assert.AreEqual("unsupported operation type: None", response[0].GetProperty("message").ToString()); + + //Register valid mutation resolver back after testing invalid senario + RemoveMutationResolver("addPlanet"); + RegisterMutationResolver("addPlanet", DATABASE_NAME, _containerName); + } + /// /// 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..a28b410158 100644 --- a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs +++ b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json.Linq; namespace Azure.DataGateway.Service.Tests.CosmosTests { @@ -69,10 +71,14 @@ public async Task GetPaginatedWithVariables() // Run query JsonElement response = await ExecuteGraphQLRequestAsync("planetList", PlanetListQuery); int actualElements = response.GetArrayLength(); + JArray responseTotal = new(); + ConvertJsonElementToJArray(response, responseTotal); + // Run paginated query int totalElementsFromPaginatedQuery = 0; string continuationToken = null; const int pagesize = 5; + JArray pagedResponse = new(); do { @@ -80,10 +86,12 @@ public async Task GetPaginatedWithVariables() JsonElement continuation = page.GetProperty("endCursor"); continuationToken = continuation.ToString(); totalElementsFromPaginatedQuery += page.GetProperty("items").GetArrayLength(); + ConvertJsonElementToJArray(page.GetProperty("items"), pagedResponse); } while (!string.IsNullOrEmpty(continuationToken)); // Validate results Assert.AreEqual(actualElements, totalElementsFromPaginatedQuery); + Assert.IsTrue(JArray.DeepEquals(responseTotal, pagedResponse)); } [TestMethod] @@ -110,10 +118,14 @@ public async Task GetPaginatedWithoutVariables() // Run query JsonElement response = await ExecuteGraphQLRequestAsync("planetList", PlanetListQuery); int actualElements = response.GetArrayLength(); + JArray responseTotal = new(); + ConvertJsonElementToJArray(response, responseTotal); + // Run paginated query int totalElementsFromPaginatedQuery = 0; string continuationToken = null; const int pagesize = 5; + JArray pagedResponse = new(); do { @@ -133,12 +145,95 @@ public async Task GetPaginatedWithoutVariables() JsonElement continuation = page.GetProperty("endCursor"); continuationToken = continuation.ToString(); totalElementsFromPaginatedQuery += page.GetProperty("items").GetArrayLength(); + ConvertJsonElementToJArray(page.GetProperty("items"), pagedResponse); } while (!string.IsNullOrEmpty(continuationToken)); // Validate results Assert.AreEqual(actualElements, totalElementsFromPaginatedQuery); } + /// + /// 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 = "test name"; + 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 no match + /// + /// + [TestMethod] + public async Task GetByNonePrimaryFieldReturnsResult() + { + 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()); + } + + private static void ConvertJsonElementToJArray(JsonElement ele, JArray jObj) + { + if (ele.ValueKind == JsonValueKind.Array) + { + JsonElement.ArrayEnumerator enumerator = ele.EnumerateArray(); + + while (enumerator.MoveNext()) + { + JsonElement prop = enumerator.Current; + jObj.Add(prop.ToString()); + } + } + } + /// /// Runs once after all tests in this class are executed /// diff --git a/DataGateway.Service.Tests/CosmosTests/TestBase.cs b/DataGateway.Service.Tests/CosmosTests/TestBase.cs index 834dbc0ea1..a21d00fd7b 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 { @@ -66,7 +69,8 @@ type Character { type Planet { id : ID, name : String -}"; +} +"; _metadataStoreProvider.GraphQLSchema = jsonString; _queryEngine = new CosmosQueryEngine(_clientProvider, _metadataStoreProvider); _mutationEngine = new CosmosMutationEngine(_clientProvider, _metadataStoreProvider); @@ -101,9 +105,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(new Claim[] { new Claim(ClaimTypes.Name, "test@microsoft.com") }, "TestAuthentication")); DefaultHttpContext httpContext = new() { - Request = { Body = stream, ContentLength = stream.Length } + Request = { Body = stream, ContentLength = stream.Length }, + User = user }; return httpContext; } @@ -131,6 +137,15 @@ internal static void RegisterMutationResolver(string id, _metadataStoreProvider.StoreMutationResolver(mutationResolver); } + /// + /// + /// + /// name of the mutation + internal static void RemoveMutationResolver(string id) + { + _metadataStoreProvider.RemoveMutationResolver(id); + } + /// /// Creates and registers a GraphQLType /// @@ -174,7 +189,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 cc932c1e83..c4313a2fc3 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, 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..3f9619cec0 100644 --- a/DataGateway.Service/Resolvers/CosmosQueryEngine.cs +++ b/DataGateway.Service/Resolvers/CosmosQueryEngine.cs @@ -94,14 +94,14 @@ public async Task> ExecuteAsync(IMiddlewareContex return new Tuple(JsonDocument.Parse(res.ToString()), null); } - static JObject FindFirstItem(IEnumerator iterator) + if (firstPage.Count == 0) { - JObject firstItem; - if (iterator.MoveNext() && (firstItem = iterator.Current) == null) - { - return FindFirstItem(iterator); - } + return new Tuple(null, null); + } + static JObject FindFirstItem(IEnumerator iterator) + { + iterator.MoveNext(); return iterator.Current; } @@ -126,7 +126,7 @@ public async Task, IMetadata>> ExecuteListAsync( if (parameters != null) { - foreach (KeyValuePair parameterEntry in parameters) + foreach (KeyValuePair parameterEntry in structure.Parameters) { querySpec.WithParameter("@" + parameterEntry.Key, parameterEntry.Value); } diff --git a/DataGateway.Service/Services/GraphQLService.cs b/DataGateway.Service/Services/GraphQLService.cs index 0d766e88f4..9cc0014072 100644 --- a/DataGateway.Service/Services/GraphQLService.cs +++ b/DataGateway.Service/Services/GraphQLService.cs @@ -60,6 +60,7 @@ public void ParseAsync(string data) { Console.Error.WriteLine(error.Exception.Message); Console.Error.WriteLine(error.Exception.StackTrace); + return error.WithMessage(error.Exception!.Message); } return error; From cd9138e09cfff1e2225b5419a14e2d7081bec41e Mon Sep 17 00:00:00 2001 From: tarazou9 <40870773+tarazou9@users.noreply.github.com> Date: Sun, 8 May 2022 22:19:25 -0400 Subject: [PATCH 02/10] Add more assert --- DataGateway.Service.Tests/CosmosTests/QueryTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs index a28b410158..ec9932ee8f 100644 --- a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs +++ b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs @@ -150,6 +150,7 @@ public async Task GetPaginatedWithoutVariables() // Validate results Assert.AreEqual(actualElements, totalElementsFromPaginatedQuery); + Assert.IsTrue(JArray.DeepEquals(responseTotal, pagedResponse)); } /// From fe3eb55f9f1f89a51dae2570c1858498efa041a7 Mon Sep 17 00:00:00 2001 From: tarazou9 <40870773+tarazou9@users.noreply.github.com> Date: Mon, 9 May 2022 11:11:14 -0400 Subject: [PATCH 03/10] Fix format --- .../CosmosTests/MetadataStoreProviderForTest.cs | 2 +- DataGateway.Service.Tests/CosmosTests/QueryTests.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs b/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs index 685a004a90..d9b36623c3 100644 --- a/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs +++ b/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs @@ -39,7 +39,7 @@ public void StoreMutationResolver(MutationResolver mutationResolver) public void RemoveMutationResolver(string mutationResolverId) { - if(MutationResolvers.ContainsKey(mutationResolverId)) + if (MutationResolvers.ContainsKey(mutationResolverId)) { MutationResolvers.Remove(mutationResolverId); } diff --git a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs index ec9932ee8f..bff83f63ea 100644 --- a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs +++ b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text.Json; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; From 41e392531407af40db6da54684ce281e7d15282b Mon Sep 17 00:00:00 2001 From: tarazou9 <40870773+tarazou9@users.noreply.github.com> Date: Tue, 10 May 2022 09:45:24 -0400 Subject: [PATCH 04/10] Add tests for inner objects and fix an issue related to query with inner object, also address PR comments --- .../CosmosTests/MutationTests.cs | 2 +- .../CosmosTests/QueryTests.cs | 53 ++++++++++++++----- .../CosmosTests/TestBase.cs | 5 +- .../CosmosTests/TestHelper.cs | 8 +++ .../Resolvers/CosmosQueryEngine.cs | 23 ++------ .../Resolvers/CosmosQueryStructure.cs | 4 +- .../Services/GraphQLService.cs | 2 +- 7 files changed, 58 insertions(+), 39 deletions(-) diff --git a/DataGateway.Service.Tests/CosmosTests/MutationTests.cs b/DataGateway.Service.Tests/CosmosTests/MutationTests.cs index 5b1b3ab5f9..bb001888ca 100644 --- a/DataGateway.Service.Tests/CosmosTests/MutationTests.cs +++ b/DataGateway.Service.Tests/CosmosTests/MutationTests.cs @@ -122,7 +122,7 @@ public async Task MutationMissingInputReturnError() // Run mutation Add planet without any input string mutation = $@" mutation {{ - addPlanet () {{ + addPlanet {{ id name }} diff --git a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs index bff83f63ea..f827c74109 100644 --- a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs +++ b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Newtonsoft.Json.Linq; namespace Azure.DataGateway.Service.Tests.CosmosTests { @@ -70,14 +70,14 @@ public async Task GetPaginatedWithVariables() // Run query JsonElement response = await ExecuteGraphQLRequestAsync("planetList", PlanetListQuery); int actualElements = response.GetArrayLength(); - JArray responseTotal = new(); - ConvertJsonElementToJArray(response, responseTotal); + List responseTotal = new(); + ConvertJsonElementToStringList(response, responseTotal); // Run paginated query int totalElementsFromPaginatedQuery = 0; string continuationToken = null; const int pagesize = 5; - JArray pagedResponse = new(); + List pagedResponse = new(); do { @@ -85,12 +85,12 @@ public async Task GetPaginatedWithVariables() JsonElement continuation = page.GetProperty("endCursor"); continuationToken = continuation.ToString(); totalElementsFromPaginatedQuery += page.GetProperty("items").GetArrayLength(); - ConvertJsonElementToJArray(page.GetProperty("items"), pagedResponse); + ConvertJsonElementToStringList(page.GetProperty("items"), pagedResponse); } while (!string.IsNullOrEmpty(continuationToken)); // Validate results Assert.AreEqual(actualElements, totalElementsFromPaginatedQuery); - Assert.IsTrue(JArray.DeepEquals(responseTotal, pagedResponse)); + Assert.IsTrue(responseTotal.SequenceEqual(pagedResponse)); } [TestMethod] @@ -117,14 +117,14 @@ public async Task GetPaginatedWithoutVariables() // Run query JsonElement response = await ExecuteGraphQLRequestAsync("planetList", PlanetListQuery); int actualElements = response.GetArrayLength(); - JArray responseTotal = new(); - ConvertJsonElementToJArray(response, responseTotal); + List responseTotal = new(); + ConvertJsonElementToStringList(response, responseTotal); // Run paginated query int totalElementsFromPaginatedQuery = 0; string continuationToken = null; const int pagesize = 5; - JArray pagedResponse = new(); + List pagedResponse = new(); do { @@ -144,12 +144,12 @@ public async Task GetPaginatedWithoutVariables() JsonElement continuation = page.GetProperty("endCursor"); continuationToken = continuation.ToString(); totalElementsFromPaginatedQuery += page.GetProperty("items").GetArrayLength(); - ConvertJsonElementToJArray(page.GetProperty("items"), pagedResponse); + ConvertJsonElementToStringList(page.GetProperty("items"), pagedResponse); } while (!string.IsNullOrEmpty(continuationToken)); // Validate results Assert.AreEqual(actualElements, totalElementsFromPaginatedQuery); - Assert.IsTrue(JArray.DeepEquals(responseTotal, pagedResponse)); + Assert.IsTrue(responseTotal.SequenceEqual(pagedResponse)); } /// @@ -182,7 +182,6 @@ public async Task GetListTypeWithParameters() [TestMethod] public async Task GetByNonePrimaryFieldResultNotFound() { - //string name = "test name"; string name = "non-existed name"; string query = @$" query {{ @@ -220,7 +219,33 @@ public async Task GetByNonePrimaryFieldReturnsResult() Assert.AreEqual(name, response.GetProperty("name").ToString()); } - private static void ConvertJsonElementToJArray(JsonElement ele, JArray jObj) + /// + /// 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) { @@ -229,7 +254,7 @@ private static void ConvertJsonElementToJArray(JsonElement ele, JArray jObj) while (enumerator.MoveNext()) { JsonElement prop = enumerator.Current; - jObj.Add(prop.ToString()); + strList.Add(prop.ToString()); } } } diff --git a/DataGateway.Service.Tests/CosmosTests/TestBase.cs b/DataGateway.Service.Tests/CosmosTests/TestBase.cs index a21d00fd7b..f8e387d73c 100644 --- a/DataGateway.Service.Tests/CosmosTests/TestBase.cs +++ b/DataGateway.Service.Tests/CosmosTests/TestBase.cs @@ -68,7 +68,8 @@ type Character { type Planet { id : ID, - name : String + name : String, + character: Character } "; _metadataStoreProvider.GraphQLSchema = jsonString; @@ -105,7 +106,7 @@ 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(new Claim[] { new Claim(ClaimTypes.Name, "test@microsoft.com") }, "TestAuthentication")); + ClaimsPrincipal user = new(new ClaimsIdentity(authenticationType: "Bearer")); DefaultHttpContext httpContext = new() { Request = { Body = stream, ContentLength = stream.Length }, diff --git a/DataGateway.Service.Tests/CosmosTests/TestHelper.cs b/DataGateway.Service.Tests/CosmosTests/TestHelper.cs index c4313a2fc3..31900f1b2b 100644 --- a/DataGateway.Service.Tests/CosmosTests/TestHelper.cs +++ b/DataGateway.Service.Tests/CosmosTests/TestHelper.cs @@ -57,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/CosmosQueryEngine.cs b/DataGateway.Service/Resolvers/CosmosQueryEngine.cs index 3f9619cec0..b97fdd95e9 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; @@ -94,20 +95,7 @@ public async Task> ExecuteAsync(IMiddlewareContex return new Tuple(JsonDocument.Parse(res.ToString()), null); } - if (firstPage.Count == 0) - { - return new Tuple(null, null); - } - - static JObject FindFirstItem(IEnumerator iterator) - { - iterator.MoveNext(); - return iterator.Current; - } - - JObject firstItem = FindFirstItem(firstPage.GetEnumerator()); - - JsonDocument jsonDocument = JsonDocument.Parse(firstItem.ToString()); + JsonDocument jsonDocument = (firstPage.Count == 0) ? null : JsonDocument.Parse(firstPage.FirstOrDefault().ToString()); return new Tuple(jsonDocument, null); } @@ -124,12 +112,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 structure.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 9cc0014072..6433f0f3f8 100644 --- a/DataGateway.Service/Services/GraphQLService.cs +++ b/DataGateway.Service/Services/GraphQLService.cs @@ -60,7 +60,7 @@ public void ParseAsync(string data) { Console.Error.WriteLine(error.Exception.Message); Console.Error.WriteLine(error.Exception.StackTrace); - return error.WithMessage(error.Exception!.Message); + return error.WithMessage(error.Exception.Message); } return error; From b70f0384e643cddc4cb79b1cd94140b50825e539 Mon Sep 17 00:00:00 2001 From: tarazou9 <40870773+tarazou9@users.noreply.github.com> Date: Tue, 10 May 2022 14:29:45 -0400 Subject: [PATCH 05/10] add named argument for tests --- .../CosmosTests/MutationTests.cs | 12 ++++++------ DataGateway.Service.Tests/CosmosTests/QueryTests.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/DataGateway.Service.Tests/CosmosTests/MutationTests.cs b/DataGateway.Service.Tests/CosmosTests/MutationTests.cs index bb001888ca..6b82c0a396 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,7 +110,7 @@ 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()); @@ -127,7 +127,7 @@ public async Task MutationMissingInputReturnError() name }} }}"; - JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", mutation, new()); + JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", mutation, variables: new()); Assert.AreEqual("inputDict is missing", response[0].GetProperty("message").ToString()); } @@ -143,7 +143,7 @@ public async Task MutationMissingRequiredIdReturnError() name }} }}"; - JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", mutation, new()); + JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", mutation, variables: new()); Assert.AreEqual("id field is mandatory", response[0].GetProperty("message").ToString()); } @@ -163,7 +163,7 @@ public async Task InvalidResolverOperationTypeReturnError() name }} }}"; - JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", addMutation, new()); + JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", addMutation, variables: new()); Assert.AreEqual("unsupported operation type: None", response[0].GetProperty("message").ToString()); //Register valid mutation resolver back after testing invalid senario diff --git a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs index f827c74109..53fd0f79fc 100644 --- a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs +++ b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs @@ -140,7 +140,7 @@ 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(); From 867d6ef3af117fbc851fc85815194a30429eb2c0 Mon Sep 17 00:00:00 2001 From: tarazou9 <40870773+tarazou9@users.noreply.github.com> Date: Wed, 11 May 2022 10:08:04 -0400 Subject: [PATCH 06/10] Check empty page for querying single item --- .../Resolvers/CosmosQueryEngine.cs | 63 +++++++++++-------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/DataGateway.Service/Resolvers/CosmosQueryEngine.cs b/DataGateway.Service/Resolvers/CosmosQueryEngine.cs index b97fdd95e9..223509df5b 100644 --- a/DataGateway.Service/Resolvers/CosmosQueryEngine.cs +++ b/DataGateway.Service/Resolvers/CosmosQueryEngine.cs @@ -68,36 +68,49 @@ 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()) - { - JObject item = enumerator.Current; - jarray.Add(item); - } - - string responseContinuation = firstPage.ContinuationToken; - if (string.IsNullOrEmpty(responseContinuation)) + do { - responseContinuation = null; + 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); + } + + // For non-connection, check if it's an empty page from partition, return first item when non-empty, otherwise getting next page + if (page.Count > 0) + { + return new Tuple(JsonDocument.Parse(page.FirstOrDefault().ToString()), 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); } - JsonDocument jsonDocument = (firstPage.Count == 0) ? null : JsonDocument.Parse(firstPage.FirstOrDefault().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) From 94456c6369b456b676b35cc87f41ca6c01e02c13 Mon Sep 17 00:00:00 2001 From: tarazou9 <40870773+tarazou9@users.noreply.github.com> Date: Wed, 11 May 2022 10:19:14 -0400 Subject: [PATCH 07/10] Fix formatting error --- DataGateway.Service/Resolvers/CosmosQueryEngine.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/DataGateway.Service/Resolvers/CosmosQueryEngine.cs b/DataGateway.Service/Resolvers/CosmosQueryEngine.cs index 223509df5b..ae9552e282 100644 --- a/DataGateway.Service/Resolvers/CosmosQueryEngine.cs +++ b/DataGateway.Service/Resolvers/CosmosQueryEngine.cs @@ -100,7 +100,6 @@ public async Task> ExecuteAsync(IMiddlewareContex return new Tuple(JsonDocument.Parse(res.ToString()), null); } - // For non-connection, check if it's an empty page from partition, return first item when non-empty, otherwise getting next page if (page.Count > 0) { return new Tuple(JsonDocument.Parse(page.FirstOrDefault().ToString()), null); From a4e31d8e1f7edf459d94efec2fdf60c30c10e7b5 Mon Sep 17 00:00:00 2001 From: tarazou9 <40870773+tarazou9@users.noreply.github.com> Date: Wed, 11 May 2022 13:12:58 -0400 Subject: [PATCH 08/10] Address PR comments --- .../MetadataStoreProviderForTest.cs | 8 ------- .../CosmosTests/MutationTests.cs | 24 ------------------- .../CosmosTests/TestBase.cs | 9 ------- .../Resolvers/CosmosQueryEngine.cs | 4 ++-- 4 files changed, 2 insertions(+), 43 deletions(-) diff --git a/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs b/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs index d9b36623c3..a3115d85ff 100644 --- a/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs +++ b/DataGateway.Service.Tests/CosmosTests/MetadataStoreProviderForTest.cs @@ -37,14 +37,6 @@ public void StoreMutationResolver(MutationResolver mutationResolver) MutationResolvers.Add(mutationResolver.Id, mutationResolver); } - public void RemoveMutationResolver(string mutationResolverId) - { - if (MutationResolvers.ContainsKey(mutationResolverId)) - { - MutationResolvers.Remove(mutationResolverId); - } - } - public void StoreGraphQLType(string name, GraphQLType graphQLType) { GraphQLTypes.Add(name, graphQLType); diff --git a/DataGateway.Service.Tests/CosmosTests/MutationTests.cs b/DataGateway.Service.Tests/CosmosTests/MutationTests.cs index 6b82c0a396..f3abe6e78a 100644 --- a/DataGateway.Service.Tests/CosmosTests/MutationTests.cs +++ b/DataGateway.Service.Tests/CosmosTests/MutationTests.cs @@ -147,30 +147,6 @@ public async Task MutationMissingRequiredIdReturnError() Assert.AreEqual("id field is mandatory", response[0].GetProperty("message").ToString()); } - [TestMethod] - public async Task InvalidResolverOperationTypeReturnError() - { - //Register invalid operation type resolver - RemoveMutationResolver("addPlanet"); - RegisterMutationResolver("addPlanet", DATABASE_NAME, _containerName, "None"); - - string id = Guid.NewGuid().ToString(); - const string name = "test_name"; - string addMutation = $@" -mutation {{ - addPlanet (id: ""{id}"", name: ""{name}"") {{ - id - name - }} -}}"; - JsonElement response = await ExecuteGraphQLRequestAsync("addPlanet", addMutation, variables: new()); - Assert.AreEqual("unsupported operation type: None", response[0].GetProperty("message").ToString()); - - //Register valid mutation resolver back after testing invalid senario - RemoveMutationResolver("addPlanet"); - RegisterMutationResolver("addPlanet", DATABASE_NAME, _containerName); - } - /// /// Runs once after all tests in this class are executed /// diff --git a/DataGateway.Service.Tests/CosmosTests/TestBase.cs b/DataGateway.Service.Tests/CosmosTests/TestBase.cs index 1671042608..d4068e0250 100644 --- a/DataGateway.Service.Tests/CosmosTests/TestBase.cs +++ b/DataGateway.Service.Tests/CosmosTests/TestBase.cs @@ -148,15 +148,6 @@ internal static void RegisterMutationResolver(string id, _metadataStoreProvider.StoreMutationResolver(mutationResolver); } - /// - /// - /// - /// name of the mutation - internal static void RemoveMutationResolver(string id) - { - _metadataStoreProvider.RemoveMutationResolver(id); - } - /// /// Creates and registers a GraphQLType /// diff --git a/DataGateway.Service/Resolvers/CosmosQueryEngine.cs b/DataGateway.Service/Resolvers/CosmosQueryEngine.cs index ae9552e282..9a881167ea 100644 --- a/DataGateway.Service/Resolvers/CosmosQueryEngine.cs +++ b/DataGateway.Service/Resolvers/CosmosQueryEngine.cs @@ -98,11 +98,11 @@ public async Task> ExecuteAsync(IMiddlewareContex // 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.FirstOrDefault().ToString()), null); + return new Tuple(JsonDocument.Parse(page.First().ToString()), null); } } while (query.HasMoreResults); From d7d96a78bd04197cebf7b7d6661431134ead72e8 Mon Sep 17 00:00:00 2001 From: tarazou9 <40870773+tarazou9@users.noreply.github.com> Date: Wed, 11 May 2022 14:26:04 -0400 Subject: [PATCH 09/10] Fix typo --- DataGateway.Service.Tests/CosmosTests/QueryTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs index 53fd0f79fc..661e001235 100644 --- a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs +++ b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs @@ -202,7 +202,7 @@ public async Task GetByNonePrimaryFieldResultNotFound() /// /// [TestMethod] - public async Task GetByNonePrimaryFieldReturnsResult() + public async Task GetByNonPrimaryFieldReturnsResult() { string name = "test name"; string query = @$" From d0346ba94d280fe8932ae8b31cc8d482d065f477 Mon Sep 17 00:00:00 2001 From: tarazou9 <40870773+tarazou9@users.noreply.github.com> Date: Wed, 11 May 2022 14:28:01 -0400 Subject: [PATCH 10/10] Fix comment --- DataGateway.Service.Tests/CosmosTests/QueryTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs index 661e001235..9445926038 100644 --- a/DataGateway.Service.Tests/CosmosTests/QueryTests.cs +++ b/DataGateway.Service.Tests/CosmosTests/QueryTests.cs @@ -198,7 +198,7 @@ public async Task GetByNonePrimaryFieldResultNotFound() } /// - /// Query single item by non-primary key field, found no match + /// Query single item by non-primary key field, found record back /// /// [TestMethod]