| /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| ~ Licensed to the Apache Software Foundation (ASF) under one |
| ~ or more contributor license agreements. See the NOTICE file |
| ~ distributed with this work for additional information |
| ~ regarding copyright ownership. The ASF licenses this file |
| ~ to you under the Apache License, Version 2.0 (the |
| ~ "License"); you may not use this file except in compliance |
| ~ with the License. You may obtain a copy of the License at |
| ~ |
| ~ http://www.apache.org/licenses/LICENSE-2.0 |
| ~ |
| ~ Unless required by applicable law or agreed to in writing, |
| ~ software distributed under the License is distributed on an |
| ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| ~ KIND, either express or implied. See the License for the |
| ~ specific language governing permissions and limitations |
| ~ under the License. |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ |
| package org.apache.sling.graphql.core.engine; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Objects; |
| |
| import org.apache.sling.graphql.api.SchemaProvider; |
| import org.apache.sling.graphql.api.SelectedField; |
| import org.apache.sling.graphql.api.SelectionSet; |
| import org.apache.sling.graphql.api.SlingDataFetcher; |
| import org.apache.sling.graphql.api.SlingGraphQLException; |
| import org.apache.sling.graphql.api.engine.QueryExecutor; |
| import org.apache.sling.graphql.api.engine.ValidationResult; |
| import org.apache.sling.graphql.core.mocks.DigestDataFetcher; |
| import org.apache.sling.graphql.core.mocks.DummyTypeResolver; |
| import org.apache.sling.graphql.core.mocks.EchoDataFetcher; |
| import org.apache.sling.graphql.core.mocks.FailingDataFetcher; |
| import org.apache.sling.graphql.core.mocks.MockSchemaProvider; |
| import org.apache.sling.graphql.core.mocks.TestUtil; |
| import org.apache.sling.graphql.core.mocks.TypeSlingResourceDTO; |
| import org.apache.sling.graphql.core.mocks.TypeTestDTO; |
| import org.apache.sling.graphql.core.mocks.UnionTypeResolver; |
| import org.junit.Test; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.ServiceRegistration; |
| |
| import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; |
| import static org.hamcrest.Matchers.containsString; |
| import static org.hamcrest.Matchers.equalTo; |
| import static org.hamcrest.Matchers.is; |
| import static org.hamcrest.Matchers.nullValue; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| public class DefaultQueryExecutorTest extends ResourceQueryTestBase { |
| |
| protected void setupAdditionalServices() { |
| final Dictionary<String, Object> staticData = new Hashtable<>(); |
| staticData.put("test", true); |
| |
| final Dictionary<String, Object> unionData = new Hashtable<>(); |
| final List<Object> items = new ArrayList<>(); |
| items.add(new TypeTestDTO(true, false, "path/to/resource", "1, 2, 3")); |
| items.add(new TypeSlingResourceDTO(resource.getPath(), resource.getResourceType())); |
| unionData.put("items", items); |
| |
| TestUtil.registerSlingTypeResolver(context.bundleContext(), "union/resolver", new UnionTypeResolver()); |
| TestUtil.registerSlingDataFetcher(context.bundleContext(), "union/fetcher", new EchoDataFetcher(unionData)); |
| TestUtil.registerSlingDataFetcher(context.bundleContext(), "echoNS/echo", new EchoDataFetcher(null)); |
| TestUtil.registerSlingDataFetcher(context.bundleContext(), "failure/fail", new FailingDataFetcher()); |
| TestUtil.registerSlingDataFetcher(context.bundleContext(), "test/static", new EchoDataFetcher(staticData)); |
| TestUtil.registerSlingDataFetcher(context.bundleContext(), "test/fortyTwo", new EchoDataFetcher(42)); |
| TestUtil.registerSlingDataFetcher(context.bundleContext(), "sling/digest", new DigestDataFetcher()); |
| TestUtil.registerSlingDataFetcher(context.bundleContext(), "combined/fetcher", new EchoDataFetcher(unionData)); |
| } |
| |
| @Test |
| public void basicTest() throws Exception { |
| final String json = queryJSON("{ currentResource { path resourceType } }"); |
| assertThat(json, hasJsonPath("$.data.currentResource")); |
| assertThat(json, hasJsonPath("$.data.currentResource.path", equalTo(resource.getPath()))); |
| assertThat(json, hasJsonPath("$.data.currentResource.resourceType", equalTo(resource.getResourceType()))); |
| } |
| |
| @Test |
| public void staticContentTest() throws Exception { |
| final String json = queryJSON("{ staticContent { test } }"); |
| assertThat(json, hasJsonPath("$.data.staticContent")); |
| assertThat(json, hasJsonPath("$.data.staticContent.test", equalTo(true))); |
| } |
| |
| @Test |
| public void digestFieldsTest() throws Exception { |
| final String json = queryJSON("{ currentResource { path pathMD5 pathSHA256 resourceTypeMD5 } }"); |
| |
| final String pathMD5 = DigestDataFetcher.computeDigest("md5", resource.getPath()); |
| final String pathSHA256 = DigestDataFetcher.computeDigest("sha-256", resource.getPath()); |
| final String resourceTypeMD5 = DigestDataFetcher.computeDigest("md5", resource.getResourceType()); |
| |
| assertThat(json, hasJsonPath("$.data.currentResource")); |
| assertThat(json, hasJsonPath("$.data.currentResource.path", equalTo(resource.getPath()))); |
| assertThat(json, hasJsonPath("$.data.currentResource.pathMD5", equalTo("md5#path#" + pathMD5))); |
| assertThat(json, hasJsonPath("$.data.currentResource.pathSHA256", equalTo("sha-256#path#" + pathSHA256))); |
| assertThat(json, hasJsonPath("$.data.currentResource.resourceTypeMD5", equalTo("md5#resourceType#" + resourceTypeMD5))); |
| } |
| |
| @Test |
| public void nullValueTest() throws Exception { |
| final String json = queryJSON("{ currentResource { nullValue } }"); |
| assertThat(json, hasJsonPath("$.data.currentResource")); |
| assertThat(json, hasJsonPath("$.data.currentResource.nullValue", is(nullValue()))); |
| } |
| |
| @Test |
| public void queryValidationErrorResponseTest() throws Exception { |
| final String json = queryJSON("{ currentResource_NON_EXISTENT { nullValue } }"); |
| assertThat(json, hasJsonPath("$.errors[0].message")); |
| assertThat(json, hasJsonPath("$.errors[0].locations")); |
| assertThat(json, hasJsonPath("$.errors[0].extensions")); |
| assertThat(json, hasJsonPath("$.errors[0].extensions.classification", is("ValidationError"))); |
| } |
| |
| @Test |
| public void dataFetcherFailureTest() { |
| try { |
| final String stmt = "{ currentResource { failure } }"; |
| QueryExecutor queryExecutor = context.getService(QueryExecutor.class); |
| assertNotNull(queryExecutor); |
| queryExecutor.execute(stmt, Collections.emptyMap(), resource, new String[] {}); |
| } catch(RuntimeException rex) { |
| assertThat(rex.getMessage(), containsString("Error: type=DataFetchingException; message=Exception while fetching data (/currentResource/failure) : FailingDataFetcher")); |
| } |
| } |
| |
| @Test |
| public void invalidQueryTest() { |
| final String stmt = "{ currentRsrc { failure } }"; |
| QueryExecutor queryExecutor = context.getService(QueryExecutor.class); |
| assertNotNull(queryExecutor); |
| ValidationResult result = queryExecutor.validate(stmt, Collections.emptyMap(), resource, new String[] {}); |
| assertFalse(result.isValid()); |
| String errors = String.join("\n", result.getErrors()); |
| assertTrue(errors.contains("Error: type=ValidationError; message=Validation error of type FieldUndefined: Field 'currentRsrc' in type 'Query' is undefined @ 'currentRsrc'; location=1,3;")); |
| } |
| |
| @Test |
| public void schemaSelectorsTest(){ |
| final String [] selectors = { "selected", "foryou" }; |
| final String json = queryJSON("{ currentResource { path fortyTwo } }", selectors); |
| |
| assertThat(json, hasJsonPath("$.data.currentResource")); |
| assertThat(json, hasJsonPath("$.data.currentResource.path", equalTo(42))); |
| assertThat(json, hasJsonPath("$.data.currentResource.fortyTwo", equalTo(42))); |
| } |
| |
| @Test |
| public void invalidFetcherNamesTest() { |
| context.registerService(SchemaProvider.class, new MockSchemaProvider("failing-fetcher-schema"), Constants.SERVICE_RANKING, |
| Integer.MAX_VALUE); |
| final ServiceRegistration<?> reg = TestUtil.registerSlingDataFetcher(context.bundleContext(), "missingSlash", new EchoDataFetcher(42)); |
| try { |
| final String json = queryJSON("{ currentResource { missingSlash } }", new String[] {}); |
| assertThat(json, hasJsonPath("$.errors[0].message", containsString("Invalid fetcher name missingSlash"))); |
| assertThat(json, hasJsonPath("$.errors[0].extensions.exception", is(SlingGraphQLException.class.getName()))); |
| } finally { |
| reg.unregister(); |
| } |
| } |
| |
| @Test |
| public void invalidTypeResolverNamesTest() { |
| context.registerService(SchemaProvider.class, new MockSchemaProvider("failing-type-resolver-schema"), Constants.SERVICE_RANKING, |
| Integer.MAX_VALUE); |
| final ServiceRegistration<?> reg = TestUtil.registerSlingTypeResolver(context.bundleContext(), "missingSlash", |
| new DummyTypeResolver()); |
| try { |
| final String json = queryJSON("{ currentResource { missingSlash } }", new String[] {}); |
| assertThat(json, hasJsonPath("$.errors[0].message", containsString("Invalid type resolver name missingSlash"))); |
| assertThat(json, hasJsonPath("$.errors[0].extensions.exception", is(SlingGraphQLException.class.getName()))); |
| } finally { |
| reg.unregister(); |
| } |
| } |
| |
| @Test |
| public void unionFetcherTest() throws Exception { |
| final String json = queryJSON("{ unionFetcher { items { ... on Test { testingArgument } ... on SlingResource { path }} } }"); |
| assertThat(json, hasJsonPath("$.data.unionFetcher")); |
| assertThat(json, hasJsonPath("$.data.unionFetcher.items[0].testingArgument", equalTo("1, 2, 3"))); |
| assertThat(json, hasJsonPath("$.data.unionFetcher.items[1].path", equalTo(resource.getPath()))); |
| } |
| |
| @Test |
| public void selectionSetTest() throws Exception { |
| queryJSON("{ combinedFetcher { boolValue resourcePath aTest { boolValue test resourcePath } allTests { boolValue test resourcePath } items { ... on Test { testingArgument } ... on SlingResource { path }} } }"); |
| |
| // retrieve the service used |
| ServiceReference<?>[] serviceReferences = context.bundleContext().getServiceReferences(SlingDataFetcher.class.getName(), "(name=combined/fetcher)"); |
| EchoDataFetcher echoDataFetcher = (EchoDataFetcher) context.bundleContext().getService(serviceReferences[0]); |
| |
| // Access the computed SelectionSet |
| SelectionSet selectionSet = echoDataFetcher.getSelectionSet(); |
| |
| assertEquals(5, selectionSet.getFields().size()); |
| |
| String[] expectedFieldNames = new String[] { |
| "boolValue", |
| "resourcePath", |
| "aTest", |
| "items" |
| }; |
| final List<SelectedField> selectionSetFields = selectionSet.getFields(); |
| for (String expectedFieldname : expectedFieldNames) { |
| assertTrue(selectionSetFields.stream().anyMatch(f -> expectedFieldname.equals(f.getName()))); |
| } |
| |
| // Assert it contains the expected results |
| String[] expectedQualifiedName = new String[] { |
| "boolValue", |
| "resourcePath", |
| "aTest", |
| "aTest/test", |
| "aTest/boolValue", |
| "aTest/resourcePath", |
| "allTests", |
| "allTests/test", |
| "allTests/boolValue", |
| "allTests/resourcePath", |
| "items", |
| "items/Test", |
| "items/Test/testingArgument", |
| "items/SlingResource", |
| "items/SlingResource/path" |
| }; |
| for (String expectedQN : expectedQualifiedName) { |
| assertTrue(selectionSet.contains(expectedQN)); |
| } |
| |
| String[] expectedNonInlineQNs = new String[] { |
| "boolValue", |
| "resourcePath", |
| "aTest", |
| "aTest/test", |
| "aTest/boolValue", |
| "aTest/resourcePath", |
| "allTests", |
| "allTests/test", |
| "allTests/boolValue", |
| "allTests/resourcePath", |
| "items", |
| "items/Test/testingArgument", |
| "items/SlingResource/path" |
| }; |
| for (String expectedNonInlineQN : expectedNonInlineQNs) { |
| assertFalse(Objects.requireNonNull(selectionSet.get(expectedNonInlineQN)).isInline()); |
| } |
| |
| String[] expectedInlineQNs = new String[] { |
| "items/Test", |
| "items/SlingResource" |
| }; |
| for (String expectedInlineQN : expectedInlineQNs) { |
| assertTrue(Objects.requireNonNull(selectionSet.get(expectedInlineQN)).isInline()); |
| } |
| |
| String[] expectedSubFieldNames = new String[] { |
| "test", |
| "boolValue", |
| "resourcePath" |
| }; |
| |
| SelectedField allTests = selectionSet.get("allTests"); |
| assert allTests != null; |
| List<SelectedField> subSelectedFields = allTests.getSubSelectedFields(); |
| for (String expectedSubFieldname : expectedSubFieldNames) { |
| assertTrue(subSelectedFields.stream().anyMatch(f -> expectedSubFieldname.equals(f.getName()))); |
| } |
| |
| |
| } |
| } |