blob: 228e6c6bd788e75c94557f2e6b770e68a438e30b [file] [log] [blame]
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ 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())));
}
}
}