You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2020/10/08 14:48:11 UTC
[sling-org-apache-sling-graphql-core] 01/01: SLING-9800 - Extract a
service to be able to execute GraphQL queries directly
This is an automated email from the ASF dual-hosted git repository.
radu pushed a commit to branch issue/SLING-9800
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-core.git
commit a897812104b93ba852116cd541ff201c20329f43
Author: Radu Cotescu <co...@adobe.com>
AuthorDate: Thu Oct 8 16:45:50 2020 +0200
SLING-9800 - Extract a service to be able to execute GraphQL queries directly
* defined a new service - QueryExecutor - to execute GraphQL queries
* switched the GraphQLServlet and GraphQLScriptEngine to use the new service
* cleaned code interacting with the OSGi service registry
* made handling of OSGi services consistent, by using the service with
the highest ranking which matches the filter
---
pom.xml | 24 ++-
.../apache/sling/graphql/api/SlingDataFetcher.java | 4 +
.../sling/graphql/api/SlingGraphQLException.java | 43 +++++
.../sling/graphql/api/engine/QueryExecutor.java | 61 +++++++
.../sling/graphql/api/engine/package-info.java | 22 +++
.../org/apache/sling/graphql/api/package-info.java | 4 +-
.../core/cache/SimpleGraphQLCacheProvider.java | 2 +-
...esourceQuery.java => DefaultQueryExecutor.java} | 180 +++++++++++----------
.../core/engine/ScriptedDataFetcherProvider.java | 41 +----
.../core/engine/SlingDataFetcherSelector.java | 141 ++++++++++------
.../sling/graphql/core/json/JsonSerializer.java | 68 --------
.../core/osgi/ServiceReferenceObjectTuple.java | 65 ++++++++
.../graphql/core/scalars/SlingScalarsProvider.java | 74 +++++----
.../graphql/core/schema/DefaultSchemaProvider.java | 2 +-
.../graphql/core/schema/RankedSchemaProviders.java | 2 +-
.../core/scripting/GraphQLScriptEngine.java | 18 +--
.../core/scripting/GraphQLScriptEngineFactory.java | 25 +--
.../sling/graphql/core/servlet/GraphQLServlet.java | 42 ++---
.../sling/graphql/core/servlet/QueryParser.java | 49 +++---
...ueryTest.java => DefaultQueryExecutorTest.java} | 79 ++++-----
.../graphql/core/engine/ResourceQueryTestBase.java | 32 ++--
.../engine/SlingDataFetcherNameValidationTest.java | 10 +-
.../core/engine/SlingDataFetcherSelectorTest.java | 31 ++--
.../graphql/core/it/GraphQLCoreTestSupport.java | 41 +++--
.../sling/graphql/core/it/GraphQLServletIT.java | 4 +-
.../sling/graphql/core/it/ServerSideQueryIT.java | 4 +-
.../graphql/core/json/JsonSerializerTest.java | 86 ----------
.../apache/sling/graphql/core/mocks/TestUtil.java | 10 +-
.../core/osgi/ServiceReferenceObjectTupleTest.java | 80 +++++++++
.../core/schema/RankedSchemaProvidersTest.java | 17 +-
.../core/schema/SchemaDescriptionsTest.java | 38 +++--
.../graphql/core/servlet/GraphQLServletTest.java | 35 +---
32 files changed, 729 insertions(+), 605 deletions(-)
diff --git a/pom.xml b/pom.xml
index bcc8f94..c9472ae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -159,6 +159,24 @@
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.johnzon</artifactId>
+ <version>1.2.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.johnzon</groupId>
+ <artifactId>johnzon-core</artifactId>
+ <version>1.2.8</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.johnzon</groupId>
+ <artifactId>johnzon-mapper</artifactId>
+ <version>1.2.8</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.resource.presence</artifactId>
<version>0.0.2</version>
<scope>test</scope>
@@ -188,12 +206,6 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>com.cedarsoftware</groupId>
- <artifactId>json-io</artifactId>
- <version>4.12.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
diff --git a/src/main/java/org/apache/sling/graphql/api/SlingDataFetcher.java b/src/main/java/org/apache/sling/graphql/api/SlingDataFetcher.java
index 142f2ae..371ff36 100644
--- a/src/main/java/org/apache/sling/graphql/api/SlingDataFetcher.java
+++ b/src/main/java/org/apache/sling/graphql/api/SlingDataFetcher.java
@@ -30,6 +30,10 @@ import org.osgi.annotation.versioning.ConsumerType;
*/
@ConsumerType
public interface SlingDataFetcher<T> {
+ /**
+ * Defines the service registration property with which all {@code SlingDataFetcher} services have to be registered. The value should
+ * be namespaced, with namespaces being delimited by the "/" character.
+ */
String NAME_SERVICE_PROPERTY = "name";
@Nullable
diff --git a/src/main/java/org/apache/sling/graphql/api/SlingGraphQLException.java b/src/main/java/org/apache/sling/graphql/api/SlingGraphQLException.java
new file mode 100644
index 0000000..d447ba2
--- /dev/null
+++ b/src/main/java/org/apache/sling/graphql/api/SlingGraphQLException.java
@@ -0,0 +1,43 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.api;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * The {@code SlingGraphQLException} defines the class of errors that can be thrown by the {@code org.apache.sling.graphql.core} bundle.
+ */
+@ProviderType
+public class SlingGraphQLException extends RuntimeException {
+
+ /**
+ * Creates a {@code SlingGraphQLException} without a known cause.
+ */
+ public SlingGraphQLException(String message) {
+ this(message, null);
+ }
+
+ /**
+ * Creates a {@code SlingGraphQLException} with a known cause.
+ */
+ public SlingGraphQLException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/graphql/api/engine/QueryExecutor.java b/src/main/java/org/apache/sling/graphql/api/engine/QueryExecutor.java
new file mode 100644
index 0000000..29e49ea
--- /dev/null
+++ b/src/main/java/org/apache/sling/graphql/api/engine/QueryExecutor.java
@@ -0,0 +1,61 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.api.engine;
+
+import java.util.Map;
+
+import javax.json.JsonObject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.graphql.api.SlingGraphQLException;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * A {@code QueryExecutor} service allows consumers to validate and execute GraphQL queries directly.
+ */
+@ProviderType
+public interface QueryExecutor {
+
+ /**
+ * Validates the passed {@code query} and {@code variables}, by checking if the query obeys the known schemas.
+ *
+ * @param query the query
+ * @param variables the query's variables; can be an empty {@link Map} if the query doesn't accept variables
+ * @param queryResource the current resource, used as the root for the query
+ * @param selectors potential selectors used to select the schema applicable to the passed {@code query}
+ * @return {code true} if the {@code query} is valid, {@code false} otherwise
+ */
+ boolean isValid(@NotNull String query, @NotNull Map<String, Object> variables, @NotNull Resource queryResource,
+ @NotNull String[] selectors);
+
+ /**
+ * Executes the passed {@code query}.
+ *
+ * @param query the query
+ * @param variables the query's variables; can be an empty {@link Map} if the query doesn't accept variables
+ * @param queryResource the current resource, used as the root for the query
+ * @param selectors potential selectors used to select the schema applicable to the passed {@code query}
+ * @return a {@link JsonObject} representing the query's result
+ * @throws SlingGraphQLException if the execution of the query leads to any issues
+ */
+ @NotNull
+ JsonObject execute(@NotNull String query, @NotNull Map<String, Object> variables, @NotNull Resource queryResource,
+ @NotNull String[] selectors);
+}
diff --git a/src/main/java/org/apache/sling/graphql/api/engine/package-info.java b/src/main/java/org/apache/sling/graphql/api/engine/package-info.java
new file mode 100644
index 0000000..7e7ccb7
--- /dev/null
+++ b/src/main/java/org/apache/sling/graphql/api/engine/package-info.java
@@ -0,0 +1,22 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+@Version("0.0.1")
+package org.apache.sling.graphql.api.engine;
+
+import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/graphql/api/package-info.java b/src/main/java/org/apache/sling/graphql/api/package-info.java
index 496bec5..e5d4b27 100644
--- a/src/main/java/org/apache/sling/graphql/api/package-info.java
+++ b/src/main/java/org/apache/sling/graphql/api/package-info.java
@@ -21,6 +21,6 @@
* This package contains APIs which are independent of
* a specific implementation of the underlying graphQL engine.
*/
-@Version("3.1.0")
+@Version("3.2.0")
package org.apache.sling.graphql.api;
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProvider.java b/src/main/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProvider.java
index 97a6092..a2411a3 100644
--- a/src/main/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProvider.java
+++ b/src/main/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProvider.java
@@ -37,7 +37,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.sling.commons.metrics.Counter;
import org.apache.sling.commons.metrics.MetricsService;
import org.apache.sling.graphql.api.cache.GraphQLCacheProvider;
-import org.apache.sling.graphql.core.engine.SlingGraphQLException;
+import org.apache.sling.graphql.api.SlingGraphQLException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.framework.BundleContext;
diff --git a/src/main/java/org/apache/sling/graphql/core/engine/GraphQLResourceQuery.java b/src/main/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutor.java
similarity index 52%
rename from src/main/java/org/apache/sling/graphql/core/engine/GraphQLResourceQuery.java
rename to src/main/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutor.java
index 2175f9d..2b87dab 100644
--- a/src/main/java/org/apache/sling/graphql/core/engine/GraphQLResourceQuery.java
+++ b/src/main/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutor.java
@@ -1,43 +1,48 @@
-/*
- * 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.
- */
-
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.io.IOException;
import java.util.List;
import java.util.Map;
-import java.util.regex.Pattern;
+import javax.json.Json;
+import javax.json.JsonObject;
import javax.script.ScriptException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.graphql.api.SchemaProvider;
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.core.scalars.SlingScalarsProvider;
+import org.apache.sling.graphql.core.schema.RankedSchemaProviders;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQL;
+import graphql.GraphQLError;
import graphql.ParseAndValidate;
import graphql.language.Argument;
import graphql.language.Directive;
@@ -52,76 +57,81 @@ import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
-/**
- * Run a GraphQL query in the context of a Sling Resource
- */
-public class GraphQLResourceQuery {
+@Component(
+ service = QueryExecutor.class
+)
+public class DefaultQueryExecutor implements QueryExecutor {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DefaultQueryExecutor.class);
public static final String FETCHER_DIRECTIVE = "fetcher";
public static final String FETCHER_NAME = "name";
public static final String FETCHER_OPTIONS = "options";
public static final String FETCHER_SOURCE = "source";
- private static final Pattern FETCHER_NAME_PATTERN = Pattern.compile("\\w+(/\\w+)+");
- private static final Logger LOGGER = LoggerFactory.getLogger(GraphQLResourceQuery.class);
+ @Reference
+ private RankedSchemaProviders schemaProvider;
- private GraphQLResourceQuery() {}
+ @Reference
+ private SlingDataFetcherSelector dataFetcherSelector;
- public static ExecutionResult executeQuery(@NotNull SchemaProvider schemaProvider,
- @NotNull SlingDataFetcherSelector fetchersSelector,
- @NotNull SlingScalarsProvider scalarsProvider,
- @NotNull Resource r,
- @NotNull String[] requestSelectors,
- @NotNull String query, @NotNull Map<String, Object> variables) throws ScriptException {
- String schemaDef = null;
+ @Reference
+ private SlingScalarsProvider scalarsProvider;
+
+ @Override
+ public boolean isValid(@NotNull String query, @NotNull Map<String, Object> variables, @NotNull Resource queryResource,
+ @NotNull String[] selectors) {
try {
- schemaDef = prepareSchemaDefinition(schemaProvider, r, requestSelectors);
- LOGGER.debug("Resource {} maps to GQL schema {}", r.getPath(), schemaDef);
- final GraphQLSchema schema = buildSchema(schemaDef, fetchersSelector, scalarsProvider, r);
- final GraphQL graphQL = GraphQL.newGraphQL(schema).build();
- LOGGER.debug("Executing query\n[{}]\nat [{}] with variables [{}]", query, r.getPath(), variables);
- ExecutionInput ei = ExecutionInput.newExecutionInput()
+ String schemaDef = prepareSchemaDefinition(schemaProvider, queryResource, selectors);
+ LOGGER.debug("Resource {} maps to GQL schema {}", queryResource.getPath(), schemaDef);
+ final GraphQLSchema schema =
+ buildSchema(schemaDef, dataFetcherSelector, scalarsProvider, queryResource);
+ ExecutionInput executionInput = ExecutionInput.newExecutionInput()
.query(query)
.variables(variables)
.build();
- final ExecutionResult result = graphQL.execute(ei);
- LOGGER.debug("ExecutionResult.isDataPresent={}", result.isDataPresent());
- return result;
- } catch (ScriptException e) {
- throw e;
- } catch(Exception e) {
- final ScriptException up = new ScriptException(
- String.format("Query failed for Resource %s: schema=%s, query=%s", r.getPath(), schemaDef, query));
- up.initCause(e);
- LOGGER.info("GraphQL Query Exception", up);
- throw up;
+ return !ParseAndValidate.parseAndValidate(schema, executionInput).isFailure();
+ } catch (Exception e) {
+ LOGGER.error(String.format("Invalid query: %s.", query), e);
+ return false;
}
}
- public static boolean isQueryValid(@NotNull SchemaProvider schemaProvider,
- @NotNull SlingDataFetcherSelector fetchersSelector,
- @NotNull SlingScalarsProvider scalarsProvider,
- @NotNull Resource r,
- @NotNull String[] requestSelectors,
- @NotNull String query, Map<String, Object> variables) {
-
+ @Override
+ public @NotNull JsonObject execute(@NotNull String query, @NotNull Map<String, Object> variables, @NotNull Resource queryResource,
+ @NotNull String[] selectors) {
+ String schemaDef = null;
try {
- String schemaDef = prepareSchemaDefinition(schemaProvider, r, requestSelectors);
- LOGGER.debug("Resource {} maps to GQL schema {}", r.getPath(), schemaDef);
- final GraphQLSchema schema =
- buildSchema(schemaDef, fetchersSelector, scalarsProvider, r);
- ExecutionInput executionInput = ExecutionInput.newExecutionInput()
+ schemaDef = prepareSchemaDefinition(schemaProvider, queryResource, selectors);
+ LOGGER.debug("Resource {} maps to GQL schema {}", queryResource.getPath(), schemaDef);
+ final GraphQLSchema schema = buildSchema(schemaDef, dataFetcherSelector, scalarsProvider, queryResource);
+ final GraphQL graphQL = GraphQL.newGraphQL(schema).build();
+ LOGGER.debug("Executing query\n[{}]\nat [{}] with variables [{}]", query, queryResource.getPath(), variables);
+ ExecutionInput ei = ExecutionInput.newExecutionInput()
.query(query)
.variables(variables)
.build();
- return !ParseAndValidate.parseAndValidate(schema, executionInput).isFailure();
+ final ExecutionResult result = graphQL.execute(ei);
+ if (!result.getErrors().isEmpty()) {
+ StringBuilder errors = new StringBuilder();
+ for (GraphQLError error : result.getErrors()) {
+ errors.append("Error type: ").append(error.getErrorType().toString()).append("; error message: ").append(error.getMessage()).append(System.lineSeparator());
+ }
+ throw new SlingGraphQLException(String.format("Query failed for Resource %s: schema=%s, query=%s%nErrors:%n%s",
+ queryResource.getPath(), schemaDef, query, errors.toString()));
+ }
+ LOGGER.debug("ExecutionResult.isDataPresent={}", result.isDataPresent());
+ Map<String, Object> resultAsMap = result.toSpecification();
+ return Json.createObjectBuilder(resultAsMap).build().asJsonObject();
+ } catch (SlingGraphQLException e) {
+ throw e;
} catch (Exception e) {
- LOGGER.error(String.format("Invalid query: %s.", query), e);
- return false;
+ throw new SlingGraphQLException(
+ String.format("Query failed for Resource %s: schema=%s, query=%s", queryResource.getPath(), schemaDef, query), e);
}
}
- private static GraphQLSchema buildSchema(String sdl, SlingDataFetcherSelector fetchers, SlingScalarsProvider scalarsProvider,
+ private GraphQLSchema buildSchema(String sdl, SlingDataFetcherSelector fetchers, SlingScalarsProvider scalarsProvider,
Resource currentResource) {
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
Iterable<GraphQLScalarType> scalars = scalarsProvider.getCustomScalars(typeRegistry.scalars());
@@ -130,12 +140,11 @@ public class GraphQLResourceQuery {
return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
}
- private static RuntimeWiring buildWiring(TypeDefinitionRegistry typeRegistry, SlingDataFetcherSelector fetchers,
- Iterable<GraphQLScalarType> scalars, Resource r) {
+ private RuntimeWiring buildWiring(TypeDefinitionRegistry typeRegistry, SlingDataFetcherSelector fetchers,
+ Iterable<GraphQLScalarType> scalars, Resource r) {
List<ObjectTypeDefinition> types = typeRegistry.getTypes(ObjectTypeDefinition.class);
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
for (ObjectTypeDefinition type : types) {
-
builder.type(type.getName(), typeWiring -> {
for (FieldDefinition field : type.getFieldDefinitions()) {
try {
@@ -143,8 +152,10 @@ public class GraphQLResourceQuery {
if (fetcher != null) {
typeWiring.dataFetcher(field.getName(), fetcher);
}
- } catch(IOException e) {
- throw new RuntimeException("Exception while building wiring", e);
+ } catch (SlingGraphQLException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new SlingGraphQLException("Exception while building wiring.", e);
}
}
return typeWiring;
@@ -154,7 +165,7 @@ public class GraphQLResourceQuery {
return builder.build();
}
- private static String getDirectiveArgumentValue(Directive d, String name) {
+ private String getDirectiveArgumentValue(Directive d, String name) {
final Argument a = d.getArgument(name);
if(a != null && a.getValue() instanceof StringValue) {
return ((StringValue)a.getValue()).getValue();
@@ -162,19 +173,16 @@ public class GraphQLResourceQuery {
return null;
}
- static String validateFetcherName(String name) throws IOException {
- if(name == null) {
- throw new IOException(FETCHER_NAME + " cannot be null");
- }
- if(!FETCHER_NAME_PATTERN.matcher(name).matches()) {
- throw new IOException(String.format("Invalid fetcher name %s, does not match %s",
- name, FETCHER_NAME_PATTERN));
+ private @NotNull String validateFetcherName(String name) {
+ if (SlingDataFetcherSelector.nameMatchesPattern(name)) {
+ return name;
}
- return name;
+ throw new SlingGraphQLException(String.format("Invalid fetcher name %s, does not match %s",
+ name, SlingDataFetcherSelector.FETCHER_NAME_PATTERN));
}
- private static DataFetcher<Object> getDataFetcher(FieldDefinition field,
- SlingDataFetcherSelector fetchers, Resource currentResource) throws IOException {
+ private DataFetcher<Object> getDataFetcher(FieldDefinition field, SlingDataFetcherSelector fetchers, Resource currentResource)
+ {
DataFetcher<Object> result = null;
final Directive d =field.getDirective(FETCHER_DIRECTIVE);
if(d != null) {
@@ -189,8 +197,8 @@ public class GraphQLResourceQuery {
return result;
}
- private static @Nullable String prepareSchemaDefinition(@NotNull SchemaProvider schemaProvider,
- @NotNull Resource resource,
+ private @Nullable String prepareSchemaDefinition(@NotNull SchemaProvider schemaProvider,
+ @NotNull org.apache.sling.api.resource.Resource resource,
@NotNull String[] selectors) throws ScriptException {
try {
return schemaProvider.getSchema(resource, selectors);
diff --git a/src/main/java/org/apache/sling/graphql/core/engine/ScriptedDataFetcherProvider.java b/src/main/java/org/apache/sling/graphql/core/engine/ScriptedDataFetcherProvider.java
index a4b81f0..2cf294f 100644
--- a/src/main/java/org/apache/sling/graphql/core/engine/ScriptedDataFetcherProvider.java
+++ b/src/main/java/org/apache/sling/graphql/core/engine/ScriptedDataFetcherProvider.java
@@ -22,10 +22,8 @@ package org.apache.sling.graphql.core.engine;
import javax.servlet.Servlet;
-import org.apache.sling.api.resource.AbstractResource;
import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceMetadata;
-import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.SyntheticResource;
import org.apache.sling.api.scripting.SlingScript;
import org.apache.sling.api.servlets.ServletResolver;
import org.apache.sling.graphql.api.SlingDataFetcher;
@@ -45,43 +43,10 @@ public class ScriptedDataFetcherProvider {
@Reference
private ServletResolver servletResolver;
- private static class FakeResource extends AbstractResource {
-
- private final String resourceType;
-
- FakeResource(String resourceType) {
- this.resourceType = resourceType;
- }
-
- @Override
- public String getPath() {
- return "FAKE_RESOURCE_PATH";
- }
-
- @Override
- public String getResourceType() {
- return resourceType;
- }
-
- @Override
- public String getResourceSuperType() {
- return null;
- }
-
- @Override
- public ResourceMetadata getResourceMetadata() {
- throw new UnsupportedOperationException("Shouldn't be needed");
- }
-
- @Override
- public ResourceResolver getResourceResolver() {
- throw new UnsupportedOperationException("Shouldn't be needed");
- }
- }
-
@Nullable
SlingDataFetcher<Object> getDataFetcher(@NotNull String name) {
- final Resource r = new FakeResource(FAKE_RESOURCE_TYPE_PREFIX + name);
+ final Resource r = new SyntheticResource(null,
+ "FAKE_RESOURCE_PATH", FAKE_RESOURCE_TYPE_PREFIX + name);
final Servlet s = servletResolver.resolveServlet(r, SCRIPT_NAME);
if(s instanceof SlingScript) {
return new SlingScriptWrapper((SlingScript)s);
diff --git a/src/main/java/org/apache/sling/graphql/core/engine/SlingDataFetcherSelector.java b/src/main/java/org/apache/sling/graphql/core/engine/SlingDataFetcherSelector.java
index a4666bc..def4e91 100644
--- a/src/main/java/org/apache/sling/graphql/core/engine/SlingDataFetcherSelector.java
+++ b/src/main/java/org/apache/sling/graphql/core/engine/SlingDataFetcherSelector.java
@@ -19,24 +19,39 @@
package org.apache.sling.graphql.core.engine;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.graphql.api.SlingDataFetcher;
+import org.apache.sling.graphql.core.osgi.ServiceReferenceObjectTuple;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
-
-import java.io.IOException;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/** Selects a SlingDataFetcher used to retrieve data, based
* on a name specified by a GraphQL schema directive.
*/
-@Component(service=SlingDataFetcherSelector.class)
+@Component(
+ service=SlingDataFetcherSelector.class
+)
public class SlingDataFetcherSelector {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SlingDataFetcherSelector.class);
+ static final Pattern FETCHER_NAME_PATTERN = Pattern.compile("\\w+(/\\w+)+");
+
+ private final Map<String, TreeSet<ServiceReferenceObjectTuple<SlingDataFetcher<Object>>>> dataFetchers = new HashMap<>();
+
/** Fetchers which have a name starting with this prefix must be
* under the {#link RESERVED_PACKAGE_PREFIX} package.
*/
@@ -47,62 +62,92 @@ public class SlingDataFetcherSelector {
*/
public static final String RESERVED_PACKAGE_PREFIX = "org.apache.sling.";
- private BundleContext bundleContext;
-
@Reference
private ScriptedDataFetcherProvider scriptedDataFetcherProvider;
- @Activate
- public void activate(BundleContext ctx) {
- bundleContext = ctx;
+ /** @return a SlingDataFetcher, or null if none available. First tries to get an
+ * OSGi SlingDataFetcher service, and if not found tries to find a scripted SlingDataFetcher.
+ */
+ @Nullable
+ public SlingDataFetcher<Object> getSlingFetcher(@NotNull String name) {
+ SlingDataFetcher<Object> result = getOsgiServiceFetcher(name);
+ if(result == null) {
+ result = scriptedDataFetcherProvider.getDataFetcher(name);
+ }
+ return result;
}
- /** Return a SlingFetcher from the available OSGi services, if there's one
- * registered with the supplied name.
- */
- @SuppressWarnings("unchecked")
- private SlingDataFetcher<Object> getOsgiServiceFetcher(@NotNull String name) throws IOException {
- SlingDataFetcher<Object> result = null;
- final String filter = String.format("(%s=%s)", SlingDataFetcher.NAME_SERVICE_PROPERTY, name);
- ServiceReference<?>[] refs= null;
- try {
- refs = bundleContext.getServiceReferences(SlingDataFetcher.class.getName(), filter);
- } catch(InvalidSyntaxException ise) {
- throw new IOException("Invalid OSGi filter syntax", ise);
+ /**
+ * Returns a SlingFetcher from the available OSGi services, if there's one registered with the supplied name.
+ */
+ private SlingDataFetcher<Object> getOsgiServiceFetcher(@NotNull String name) {
+ TreeSet<ServiceReferenceObjectTuple<SlingDataFetcher<Object>>> fetcherSet = dataFetchers.get(name);
+ if (fetcherSet != null && !fetcherSet.isEmpty()) {
+ return fetcherSet.last().getServiceObject();
}
- if(refs != null) {
- // SlingFetcher services must have a unique name
- if(refs.length > 1) {
- throw new IOException(String.format("Got %d services for %s, expected just one", refs.length, filter));
+ return null;
+ }
+
+ private boolean hasValidName(@NotNull ServiceReference<SlingDataFetcher<Object>> serviceReference, @NotNull SlingDataFetcher<Object> fetcher) {
+ String name = PropertiesUtil.toString(serviceReference.getProperty(SlingDataFetcher.NAME_SERVICE_PROPERTY), null);
+ if (StringUtils.isNotEmpty(name)) {
+ if (!nameMatchesPattern(name)) {
+ LOGGER.error("Invalid SlingDataFetcher {}: fetcher name is not namespaced (e.g. ns/myFetcher)",
+ fetcher.getClass().getName());
+ return false;
+ }
+ if (name.startsWith(RESERVED_NAME_PREFIX)) {
+ final String className = fetcher.getClass().getName();
+ if (!fetcher.getClass().getName().startsWith(RESERVED_PACKAGE_PREFIX)) {
+ LOGGER.error(
+ "Invalid SlingDataFetcher {}: fetcher names starting with '{}' are reserved for Apache Sling Java packages",
+ className, RESERVED_NAME_PREFIX);
+ return false;
+ }
}
- result = (SlingDataFetcher<Object>)bundleContext.getService(refs[0]);
- validateResult(name, result);
+ } else {
+ LOGGER.error("Invalid {} implementation: fetcher {} is missing the mandatory value for its {} service property.",
+ SlingDataFetcher.class.getName(), fetcher.getClass().getName(), SlingDataFetcher.NAME_SERVICE_PROPERTY);
+ return false;
}
- return result;
+ return true;
+ }
+
+ static boolean nameMatchesPattern(String name) {
+ if (StringUtils.isNotEmpty(name)) {
+ return FETCHER_NAME_PATTERN.matcher(name).matches();
+ }
+ return false;
}
- private void validateResult(String name, SlingDataFetcher<?> fetcher) throws IOException {
- if(name.startsWith(RESERVED_NAME_PREFIX)) {
- final String className = fetcher.getClass().getName();
- if(!fetcher.getClass().getName().startsWith(RESERVED_PACKAGE_PREFIX)) {
- throw new IOException(
- String.format(
- "Invalid SlingDataFetcher %s:"
- + " fetcher names starting with '%s' are reserved for Apache Sling Java packages",
- className, RESERVED_NAME_PREFIX));
+ @Reference(
+ service = SlingDataFetcher.class,
+ cardinality = ReferenceCardinality.MULTIPLE,
+ policy = ReferencePolicy.DYNAMIC
+ )
+ private void bindSlingDataFetcher(ServiceReference<SlingDataFetcher<Object>> reference, SlingDataFetcher<Object> slingDataFetcher) {
+ if (hasValidName(reference, slingDataFetcher)) {
+ synchronized (dataFetchers) {
+ String name = (String) reference.getProperty(SlingDataFetcher.NAME_SERVICE_PROPERTY);
+ TreeSet<ServiceReferenceObjectTuple<SlingDataFetcher<Object>>> fetchers = dataFetchers.computeIfAbsent(name,
+ key -> new TreeSet<>());
+ fetchers.add(new ServiceReferenceObjectTuple<>(reference, slingDataFetcher));
}
}
}
- /** @return a SlingDataFetcher, or null if none available. First tries to get an
- * OSGi SlingDataFetcher service, and if not found tries to find a scripted SlingDataFetcher.
- */
- @Nullable
- public SlingDataFetcher<Object> getSlingFetcher(@NotNull String name) throws IOException {
- SlingDataFetcher<Object> result = getOsgiServiceFetcher(name);
- if(result == null) {
- result = scriptedDataFetcherProvider.getDataFetcher(name);
+ @SuppressWarnings("unused")
+ private void unbindSlingDataFetcher(ServiceReference<SlingDataFetcher<Object>> reference) {
+ synchronized (dataFetchers) {
+ String name = (String) reference.getProperty(SlingDataFetcher.NAME_SERVICE_PROPERTY);
+ if (StringUtils.isNotEmpty(name)) {
+ TreeSet<ServiceReferenceObjectTuple<SlingDataFetcher<Object>>> fetchers = dataFetchers.get(name);
+ if (fetchers != null) {
+ Optional<ServiceReferenceObjectTuple<SlingDataFetcher<Object>>> tupleToRemove =
+ fetchers.stream().filter(tuple -> reference.equals(tuple.getServiceReference())).findFirst();
+ tupleToRemove.ifPresent(fetchers::remove);
+ }
+ }
}
- return result;
}
}
diff --git a/src/main/java/org/apache/sling/graphql/core/json/JsonSerializer.java b/src/main/java/org/apache/sling/graphql/core/json/JsonSerializer.java
deleted file mode 100644
index 46e8ad2..0000000
--- a/src/main/java/org/apache/sling/graphql/core/json/JsonSerializer.java
+++ /dev/null
@@ -1,68 +0,0 @@
-
-/*
- * 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.json;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Writer;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.cedarsoftware.util.io.JsonReader;
-import com.cedarsoftware.util.io.JsonWriter;
-
-import org.apache.commons.io.output.WriterOutputStream;
-
-import graphql.ExecutionResult;
-
-/** All conversions between JSON and Objects should
- * happen in this class, in case we want to replace
- * the underlying converter library later */
-public class JsonSerializer {
-
- public static final Map<String, Object> WRITER_OPTIONS;
-
- static {
- HashMap<String, Object> writerOptions = new HashMap<>();
- writerOptions.put(JsonWriter.TYPE, false);
- WRITER_OPTIONS = Collections.unmodifiableMap(writerOptions);
- }
-
- public void sendJSON(Writer out, ExecutionResult result) throws IOException {
- final Object data = result.toSpecification();
- if (data == null) {
- throw new IOException("No data");
- }
- try(JsonWriter w = new JsonWriter(new WriterOutputStream(out), WRITER_OPTIONS)) {
- w.write(data);
- }
- }
-
- public String toJSON(Object data) {
- return JsonWriter.objectToJson(data, WRITER_OPTIONS);
- }
-
- @SuppressWarnings("unchecked")
- public Map<String, Object> jsonToMaps(InputStream input) {
- return JsonReader.jsonToMaps(input, null);
- }
-}
diff --git a/src/main/java/org/apache/sling/graphql/core/osgi/ServiceReferenceObjectTuple.java b/src/main/java/org/apache/sling/graphql/core/osgi/ServiceReferenceObjectTuple.java
new file mode 100644
index 0000000..49d05a2
--- /dev/null
+++ b/src/main/java/org/apache/sling/graphql/core/osgi/ServiceReferenceObjectTuple.java
@@ -0,0 +1,65 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.osgi;
+
+import java.util.Objects;
+
+import org.jetbrains.annotations.NotNull;
+import org.osgi.framework.ServiceReference;
+
+public class ServiceReferenceObjectTuple<T> implements Comparable<ServiceReferenceObjectTuple<T>> {
+
+ private final ServiceReference<T> serviceReference;
+ private final T serviceObject;
+
+ public ServiceReferenceObjectTuple(@NotNull ServiceReference<T> serviceReference, @NotNull T serviceObject) {
+ this.serviceReference = serviceReference;
+ this.serviceObject = serviceObject;
+ }
+
+ public ServiceReference<T> getServiceReference() {
+ return serviceReference;
+ }
+
+ public T getServiceObject() {
+ return serviceObject;
+ }
+
+ @Override
+ public int compareTo(@NotNull ServiceReferenceObjectTuple<T> o) {
+ return serviceReference.compareTo(o.serviceReference);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(serviceReference, serviceObject);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ServiceReferenceObjectTuple) {
+ ServiceReferenceObjectTuple<T> other = (ServiceReferenceObjectTuple<T>) obj;
+ if (this == other) {
+ return true;
+ }
+ return Objects.equals(serviceReference, other.serviceReference) && Objects.equals(serviceObject, other.serviceObject);
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/org/apache/sling/graphql/core/scalars/SlingScalarsProvider.java b/src/main/java/org/apache/sling/graphql/core/scalars/SlingScalarsProvider.java
index a78fff4..eb181fd 100644
--- a/src/main/java/org/apache/sling/graphql/core/scalars/SlingScalarsProvider.java
+++ b/src/main/java/org/apache/sling/graphql/core/scalars/SlingScalarsProvider.java
@@ -20,18 +20,23 @@
package org.apache.sling.graphql.core.scalars;
+import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
+import java.util.TreeSet;
import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
import org.apache.sling.graphql.api.SlingScalarConverter;
-import org.apache.sling.graphql.core.engine.SlingGraphQLException;
-import org.osgi.framework.BundleContext;
+import org.apache.sling.graphql.api.SlingGraphQLException;
+import org.apache.sling.graphql.core.osgi.ServiceReferenceObjectTuple;
import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
import graphql.language.ScalarTypeDefinition;
import graphql.schema.GraphQLScalarType;
@@ -45,41 +50,50 @@ import graphql.schema.idl.ScalarInfo;
Constants.SERVICE_VENDOR + "=The Apache Software Foundation" })
public class SlingScalarsProvider {
- private BundleContext bundleContext;
-
- @Activate
- public void activate(BundleContext ctx) {
- bundleContext = ctx;
+ private final Map<String, TreeSet<ServiceReferenceObjectTuple<SlingScalarConverter<Object, Object>>>> scalars = new HashMap<>();
+
+ @Reference(
+ service = SlingScalarConverter.class,
+ cardinality = ReferenceCardinality.MULTIPLE,
+ policy = ReferencePolicy.DYNAMIC
+ )
+ private void bindSlingScalarConverter(ServiceReference<SlingScalarConverter<Object, Object>> serviceReference,
+ SlingScalarConverter<Object, Object> scalarConverter) {
+ String name = (String) serviceReference.getProperty(SlingScalarConverter.NAME_SERVICE_PROPERTY);
+ if (StringUtils.isNotEmpty(name)) {
+ synchronized (scalars) {
+ TreeSet<ServiceReferenceObjectTuple<SlingScalarConverter<Object, Object>>> set =
+ scalars.computeIfAbsent(name, key -> new TreeSet<>());
+ set.add(new ServiceReferenceObjectTuple<>(serviceReference, scalarConverter));
+ }
+ }
}
- @SuppressWarnings("unchecked")
- private GraphQLScalarType getScalar(String name) {
+ @SuppressWarnings("unused")
+ private void unbindSlingScalarConverter(ServiceReference<SlingScalarConverter<Object, Object>> serviceReference) {
+ String name = (String) serviceReference.getProperty(SlingScalarConverter.NAME_SERVICE_PROPERTY);
+ if (StringUtils.isNotEmpty(name)) {
+ synchronized (scalars) {
+ TreeSet<ServiceReferenceObjectTuple<SlingScalarConverter<Object, Object>>> set = scalars.get(name);
+ if (set != null) {
+ Optional<ServiceReferenceObjectTuple<SlingScalarConverter<Object, Object>>> tupleToRemove =
+ set.stream().filter(tuple -> serviceReference.equals(tuple.getServiceReference())).findFirst();
+ tupleToRemove.ifPresent(set::remove);
+ }
+ }
+ }
+ }
+ private GraphQLScalarType getScalar(String name) {
// Ignore standard scalars
if(ScalarInfo.isGraphqlSpecifiedScalar(name)) {
return null;
}
-
- SlingScalarConverter<Object, Object> converter = null;
- final String filter = String.format("(%s=%s)", SlingScalarConverter.NAME_SERVICE_PROPERTY, name);
- ServiceReference<?>[] refs= null;
- try {
- refs = bundleContext.getServiceReferences(SlingScalarConverter.class.getName(), filter);
- } catch(InvalidSyntaxException ise) {
- throw new SlingGraphQLException("Invalid OSGi filter syntax:" + filter);
- }
- if(refs != null) {
- // SlingScalarConverter services must have a unique name for now
- // (we might use a namespacing @directive in the schema to allow multiple ones with the same name)
- if(refs.length > 1) {
- throw new SlingGraphQLException(String.format("Got %d services for %s, expected just one", refs.length, filter));
- }
- converter = (SlingScalarConverter<Object, Object>)bundleContext.getService(refs[0]);
- }
-
- if(converter == null) {
+ TreeSet<ServiceReferenceObjectTuple<SlingScalarConverter<Object, Object>>> set = scalars.get(name);
+ if (set == null || set.isEmpty()) {
throw new SlingGraphQLException("SlingScalarConverter with name '" + name + "' not found");
}
+ SlingScalarConverter<Object, Object> converter = set.last().getServiceObject();
return GraphQLScalarType.newScalar()
.name(name)
diff --git a/src/main/java/org/apache/sling/graphql/core/schema/DefaultSchemaProvider.java b/src/main/java/org/apache/sling/graphql/core/schema/DefaultSchemaProvider.java
index 9464ea3..bcf7bf8 100644
--- a/src/main/java/org/apache/sling/graphql/core/schema/DefaultSchemaProvider.java
+++ b/src/main/java/org/apache/sling/graphql/core/schema/DefaultSchemaProvider.java
@@ -44,7 +44,7 @@ public class DefaultSchemaProvider implements SchemaProvider {
private final Logger log = LoggerFactory.getLogger(getClass());
- public static final int SERVICE_RANKING = Integer.MAX_VALUE - 100;
+ public static final int SERVICE_RANKING = Integer.MIN_VALUE + 1000;
public static final String SCHEMA_EXTENSION = "GQLschema";
public static final String DEFAULT_SCHEMA = "";
diff --git a/src/main/java/org/apache/sling/graphql/core/schema/RankedSchemaProviders.java b/src/main/java/org/apache/sling/graphql/core/schema/RankedSchemaProviders.java
index dd856ac..b67de45 100644
--- a/src/main/java/org/apache/sling/graphql/core/schema/RankedSchemaProviders.java
+++ b/src/main/java/org/apache/sling/graphql/core/schema/RankedSchemaProviders.java
@@ -43,7 +43,7 @@ import org.osgi.service.component.annotations.ReferencePolicyOption;
@Component(service = RankedSchemaProviders.class)
public class RankedSchemaProviders implements SchemaProvider {
- final RankedServices<SchemaProvider> providers = new RankedServices<>(Order.ASCENDING);
+ final RankedServices<SchemaProvider> providers = new RankedServices<>(Order.DESCENDING);
@Override
public @Nullable String getSchema(@NotNull final Resource r, @Nullable final String[] selectors) throws IOException {
diff --git a/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngine.java b/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngine.java
index 85681a4..5f4ac7f 100644
--- a/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngine.java
+++ b/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngine.java
@@ -1,4 +1,3 @@
-
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -28,6 +27,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonWriter;
import javax.script.AbstractScriptEngine;
import javax.script.Bindings;
import javax.script.ScriptContext;
@@ -37,16 +39,10 @@ import javax.script.ScriptException;
import org.apache.commons.io.IOUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.scripting.SlingBindings;
-import org.apache.sling.graphql.core.engine.GraphQLResourceQuery;
-import org.apache.sling.graphql.core.json.JsonSerializer;
-
-import graphql.ExecutionResult;
public class GraphQLScriptEngine extends AbstractScriptEngine {
private final GraphQLScriptEngineFactory factory;
- private final JsonSerializer jsonSerializer = new JsonSerializer();
- public static final int JSON_INDENT_SPACES = 2;
public GraphQLScriptEngine(GraphQLScriptEngineFactory factory) {
this.factory = factory;
@@ -59,15 +55,13 @@ public class GraphQLScriptEngine extends AbstractScriptEngine {
@Override
public Object eval(Reader reader, ScriptContext context) throws ScriptException {
- try {
+ try (JsonWriter writer = Json.createWriter((PrintWriter) context.getBindings(ScriptContext.ENGINE_SCOPE).get(SlingBindings.OUT))) {
final Resource resource = (Resource) context.getBindings(ScriptContext.ENGINE_SCOPE)
.get(SlingBindings.RESOURCE);
final String [] selectors = getRequestSelectors(resource);
- final ExecutionResult result = GraphQLResourceQuery.executeQuery(factory.getSchemaProviders(), factory.getdataFetcherSelector(),
- factory.getScalarsProvider(), resource, selectors, IOUtils.toString(reader), Collections.emptyMap());
- final PrintWriter out = (PrintWriter) context.getBindings(ScriptContext.ENGINE_SCOPE).get(SlingBindings.OUT);
- jsonSerializer.sendJSON(out, result);
+ JsonObject json = factory.getQueryExecutor().execute(IOUtils.toString(reader), Collections.emptyMap(), resource, selectors);
+ writer.writeObject(json);
} catch(Exception e) {
throw new ScriptException(e);
}
diff --git a/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngineFactory.java b/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngineFactory.java
index 72e1d75..59c4294 100644
--- a/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngineFactory.java
+++ b/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngineFactory.java
@@ -22,10 +22,8 @@ package org.apache.sling.graphql.core.scripting;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
+import org.apache.sling.graphql.api.engine.QueryExecutor;
import org.apache.sling.scripting.api.AbstractScriptEngineFactory;
-import org.apache.sling.graphql.core.engine.SlingDataFetcherSelector;
-import org.apache.sling.graphql.core.scalars.SlingScalarsProvider;
-import org.apache.sling.graphql.core.schema.RankedSchemaProviders;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
@@ -51,13 +49,7 @@ public class GraphQLScriptEngineFactory extends AbstractScriptEngineFactory {
public static final String LANGUAGE_VERSION = "Sling:GraphQL:0.1";
@Reference
- private RankedSchemaProviders schemaProviders;
-
- @Reference
- private SlingDataFetcherSelector dataFetcherSelector;
-
- @Reference
- private SlingScalarsProvider scalarsProvider;
+ private QueryExecutor queryExecutor;
@Activate
private void activate(final GraphQLScriptEngineFactoryConfiguration config, final BundleContext ctx) {
@@ -81,16 +73,7 @@ public class GraphQLScriptEngineFactory extends AbstractScriptEngineFactory {
return new GraphQLScriptEngine(this);
}
- RankedSchemaProviders getSchemaProviders() {
- return schemaProviders;
+ QueryExecutor getQueryExecutor() {
+ return queryExecutor;
}
-
- SlingDataFetcherSelector getdataFetcherSelector() {
- return dataFetcherSelector;
- }
-
- SlingScalarsProvider getScalarsProvider() {
- return scalarsProvider;
- }
-
}
diff --git a/src/main/java/org/apache/sling/graphql/core/servlet/GraphQLServlet.java b/src/main/java/org/apache/sling/graphql/core/servlet/GraphQLServlet.java
index 3d2daca..49bd8b4 100644
--- a/src/main/java/org/apache/sling/graphql/core/servlet/GraphQLServlet.java
+++ b/src/main/java/org/apache/sling/graphql/core/servlet/GraphQLServlet.java
@@ -25,6 +25,9 @@ import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonWriter;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletResponse;
@@ -38,11 +41,7 @@ import org.apache.sling.commons.metrics.Counter;
import org.apache.sling.commons.metrics.MetricsService;
import org.apache.sling.commons.metrics.Timer;
import org.apache.sling.graphql.api.cache.GraphQLCacheProvider;
-import org.apache.sling.graphql.core.engine.GraphQLResourceQuery;
-import org.apache.sling.graphql.core.engine.SlingDataFetcherSelector;
-import org.apache.sling.graphql.core.json.JsonSerializer;
-import org.apache.sling.graphql.core.scalars.SlingScalarsProvider;
-import org.apache.sling.graphql.core.schema.RankedSchemaProviders;
+import org.apache.sling.graphql.api.engine.QueryExecutor;
import org.jetbrains.annotations.NotNull;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
@@ -56,7 +55,6 @@ import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricRegistry;
-import graphql.ExecutionResult;
/** Servlet that can be activated to implement the standard
* GraphQL "protocol" as per https://graphql.org/learn/serving-over-http/
@@ -124,13 +122,7 @@ public class GraphQLServlet extends SlingAllMethodsServlet {
}
@Reference
- private RankedSchemaProviders schemaProviders;
-
- @Reference
- private SlingDataFetcherSelector dataFetcherSelector;
-
- @Reference
- private SlingScalarsProvider scalarsProvider;
+ private QueryExecutor queryExecutor;
@Reference
private GraphQLCacheProvider cacheProvider;
@@ -144,7 +136,6 @@ public class GraphQLServlet extends SlingAllMethodsServlet {
private String suffixPersisted;
private Pattern patternGetPersistedQuery;
private int cacheControlMaxAge;
- private final JsonSerializer jsonSerializer = new JsonSerializer();
private Counter cacheHits;
private Counter cacheMisses;
@@ -286,9 +277,8 @@ public class GraphQLServlet extends SlingAllMethodsServlet {
throws IOException {
String rawQuery = IOUtils.toString(request.getReader());
QueryParser.Result query = QueryParser.fromJSON(rawQuery);
- if (GraphQLResourceQuery.isQueryValid(schemaProviders, dataFetcherSelector, scalarsProvider, request.getResource(),
- request.getRequestPathInfo().getSelectors(), query.getQuery(), query.getVariables())) {
-
+ if (queryExecutor
+ .isValid(query.getQuery(), query.getVariables(), request.getResource(), request.getRequestPathInfo().getSelectors())) {
String hash = cacheProvider.cacheQuery(rawQuery, request.getResource().getResourceType(),
request.getRequestPathInfo().getSelectorString());
if (hash != null) {
@@ -316,10 +306,10 @@ public class GraphQLServlet extends SlingAllMethodsServlet {
return;
}
- try {
- final ExecutionResult executionResult = GraphQLResourceQuery.executeQuery(schemaProviders, dataFetcherSelector, scalarsProvider,
- resource, request.getRequestPathInfo().getSelectors(), query, result.getVariables());
- jsonSerializer.sendJSON(response.getWriter(), executionResult);
+ try (JsonWriter writer = Json.createWriter(response.getWriter())) {
+ final JsonObject json = queryExecutor.execute(query, result.getVariables(), resource,
+ request.getRequestPathInfo().getSelectors());
+ writer.writeObject(json);
} catch(Exception ex) {
throw new IOException(ex);
}
@@ -328,12 +318,12 @@ public class GraphQLServlet extends SlingAllMethodsServlet {
private void execute(@NotNull String persistedQuery, SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
- try {
+ try (JsonWriter writer = Json.createWriter(response.getWriter())) {
final QueryParser.Result result = QueryParser.fromJSON(persistedQuery);
- final ExecutionResult executionResult = GraphQLResourceQuery.executeQuery(schemaProviders, dataFetcherSelector, scalarsProvider,
- request.getResource(), request.getRequestPathInfo().getSelectors(), result.getQuery(), result.getVariables());
- jsonSerializer.sendJSON(response.getWriter(), executionResult);
- } catch(Exception ex) {
+ final JsonObject json = queryExecutor
+ .execute(result.getQuery(), result.getVariables(), request.getResource(), request.getRequestPathInfo().getSelectors());
+ writer.writeObject(json);
+ } catch (Exception ex) {
throw new IOException(ex);
}
}
diff --git a/src/main/java/org/apache/sling/graphql/core/servlet/QueryParser.java b/src/main/java/org/apache/sling/graphql/core/servlet/QueryParser.java
index d5482bf..5a8585d 100644
--- a/src/main/java/org/apache/sling/graphql/core/servlet/QueryParser.java
+++ b/src/main/java/org/apache/sling/graphql/core/servlet/QueryParser.java
@@ -20,14 +20,17 @@
package org.apache.sling.graphql.core.servlet;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
+import java.io.StringReader;
import java.util.Collections;
import java.util.Map;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.input.ReaderInputStream;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+
+import org.apache.johnzon.mapper.Mapper;
+import org.apache.johnzon.mapper.MapperBuilder;
import org.apache.sling.api.SlingHttpServletRequest;
-import org.apache.sling.graphql.core.json.JsonSerializer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -57,21 +60,23 @@ public class QueryParser {
}
private static final String MIME_TYPE_JSON = "application/json";
- private static final JsonSerializer jsonSerializer = new JsonSerializer();
private static final String JSON_KEY_QUERY = "query";
private static final String JSON_KEY_VARIABLES = "variables";
+ private static final Mapper MAPPER = new MapperBuilder().build();
@Nullable
public static Result fromRequest(@NotNull SlingHttpServletRequest request) throws IOException {
String query = null;
Map<String, Object> variables = null;
if (request.getMethod().equalsIgnoreCase("POST") && MIME_TYPE_JSON.equals(request.getContentType())) {
- Map<String, Object> requestJson = getInputJson(request);
- query = (String) requestJson.get(JSON_KEY_QUERY);
- if (query != null) {
+ try (JsonReader reader = Json.createReader(request.getReader())) {
+ JsonObject input = reader.readObject();
+ query = input.getString(JSON_KEY_QUERY);
query = query.replace("\\n", "\n");
+ if (input.containsKey(JSON_KEY_VARIABLES)) {
+ variables = MAPPER.readObject(input.get(JSON_KEY_VARIABLES), Map.class);
+ }
}
- variables = (Map<String, Object>) requestJson.get(JSON_KEY_VARIABLES);
}
if (query == null) {
@@ -88,21 +93,19 @@ public class QueryParser {
}
public static Result fromJSON(String json) throws IOException {
- Map<String, Object> jsonMap = jsonSerializer.jsonToMaps(IOUtils.toInputStream(json, StandardCharsets.UTF_8));
- String query = (String) jsonMap.get(JSON_KEY_QUERY);
- if (query != null) {
- Map<String, Object> variables = (Map<String, Object>) jsonMap.get(JSON_KEY_VARIABLES);
- if (variables == null) {
- variables = Collections.emptyMap();
+ try (JsonReader reader = Json.createReader(new StringReader(json))) {
+ JsonObject jsonInput = reader.readObject();
+ String query = jsonInput.getString(JSON_KEY_QUERY);
+ if (query != null) {
+ Map<String, Object> variables = null;
+ if (jsonInput.containsKey(JSON_KEY_VARIABLES)) {
+ variables= MAPPER.readObject(jsonInput.get(JSON_KEY_VARIABLES), Map.class);
+ } else {
+ variables = Collections.emptyMap();
+ }
+ return new Result(query, variables);
}
- return new Result(query, variables);
+ throw new IOException("The provided JSON structure does not contain a query.");
}
- throw new IOException("The provided JSON structure does not contain a query.");
-
- }
-
- private static Map<String, Object> getInputJson(SlingHttpServletRequest req) throws IOException {
- return jsonSerializer.jsonToMaps(new ReaderInputStream(req.getReader()));
}
-
}
diff --git a/src/test/java/org/apache/sling/graphql/core/engine/GraphQLResourceQueryTest.java b/src/test/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutorTest.java
similarity index 71%
rename from src/test/java/org/apache/sling/graphql/core/engine/GraphQLResourceQueryTest.java
rename to src/test/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutorTest.java
index 251e9d2..41a0e4b 100644
--- a/src/test/java/org/apache/sling/graphql/core/engine/GraphQLResourceQueryTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutorTest.java
@@ -1,46 +1,49 @@
-/*
- * 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.
- */
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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 static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
-
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.nullValue;
-
-import java.io.IOException;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Hashtable;
+import org.apache.sling.graphql.api.SchemaProvider;
+import org.apache.sling.graphql.api.SlingGraphQLException;
+import org.apache.sling.graphql.api.engine.QueryExecutor;
import org.apache.sling.graphql.core.mocks.DigestDataFetcher;
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.junit.Test;
+import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
-public class GraphQLResourceQueryTest extends ResourceQueryTestBase {
-
+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.assertNotNull;
+import static org.junit.Assert.assertThat;
+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);
@@ -51,7 +54,7 @@ public class GraphQLResourceQueryTest extends ResourceQueryTestBase {
TestUtil.registerSlingDataFetcher(context.bundleContext(), "test/fortyTwo", new EchoDataFetcher(42));
TestUtil.registerSlingDataFetcher(context.bundleContext(), "sling/digest", new DigestDataFetcher());
}
-
+
@Test
public void basicTest() throws Exception {
final String json = queryJSON("{ currentResource { path resourceType } }");
@@ -90,13 +93,14 @@ public class GraphQLResourceQueryTest extends ResourceQueryTestBase {
}
@Test
- public void dataFetcherFailureTest() throws Exception {
+ public void dataFetcherFailureTest() {
try {
final String stmt = "{ currentResource { failure } }";
- GraphQLResourceQuery.executeQuery(schemaProvider, dataFetchersSelector, scalarsProvider, resource, new String[] {}, stmt,
- Collections.emptyMap());
+ QueryExecutor queryExecutor = context.getService(QueryExecutor.class);
+ assertNotNull(queryExecutor);
+ queryExecutor.execute(stmt, Collections.emptyMap(), resource, new String[] {});
} catch(RuntimeException rex) {
- assertThat(rex.getMessage(), equalTo("FailureDataFetcher"));
+ assertThat(rex.getMessage(), containsString("DataFetchingException; error message: Exception while fetching data (/currentResource/failure) : FailingDataFetcher"));
}
}
@@ -111,14 +115,15 @@ public class GraphQLResourceQueryTest extends ResourceQueryTestBase {
}
@Test
- public void invalidFetcherNamesTest() throws Exception {
- schemaProvider = new MockSchemaProvider("failing-schema");
+ public void invalidFetcherNamesTest() {
+ context.registerService(SchemaProvider.class, new MockSchemaProvider("failing-schema"), Constants.SERVICE_RANKING,
+ Integer.MAX_VALUE);
final ServiceRegistration<?> reg = TestUtil.registerSlingDataFetcher(context.bundleContext(), "missingSlash", new EchoDataFetcher(42));
try {
queryJSON("{ currentResource { missingSlash } }", new String[] {});
fail("Expected query to fail");
} catch(Exception e) {
- TestUtil.assertNestedException(e, IOException.class, "does not match");
+ TestUtil.assertNestedException(e, SlingGraphQLException.class, "does not match");
} finally {
reg.unregister();
}
diff --git a/src/test/java/org/apache/sling/graphql/core/engine/ResourceQueryTestBase.java b/src/test/java/org/apache/sling/graphql/core/engine/ResourceQueryTestBase.java
index d9bfb0a..c5b3095 100644
--- a/src/test/java/org/apache/sling/graphql/core/engine/ResourceQueryTestBase.java
+++ b/src/test/java/org/apache/sling/graphql/core/engine/ResourceQueryTestBase.java
@@ -18,29 +18,28 @@
*/
package org.apache.sling.graphql.core.engine;
-import static org.junit.Assert.assertTrue;
-
import java.util.Collections;
import java.util.UUID;
+import javax.json.JsonObject;
+
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.servlets.ServletResolver;
import org.apache.sling.graphql.api.SchemaProvider;
-import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
-import org.apache.sling.graphql.core.json.JsonSerializer;
+import org.apache.sling.graphql.api.engine.QueryExecutor;
import org.apache.sling.graphql.core.mocks.MockSchemaProvider;
import org.apache.sling.graphql.core.mocks.MockScriptServlet;
import org.apache.sling.graphql.core.scalars.SlingScalarsProvider;
+import org.apache.sling.graphql.core.schema.RankedSchemaProviders;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
import org.junit.Before;
import org.junit.Rule;
import org.mockito.Mockito;
-import graphql.ExecutionResult;
+import static org.junit.Assert.assertNotNull;
public abstract class ResourceQueryTestBase {
- protected SchemaProvider schemaProvider;
- protected SlingDataFetcherSelector dataFetchersSelector;
- protected SlingScalarsProvider scalarsProvider;
+
protected Resource resource;
@Rule
@@ -48,7 +47,8 @@ public abstract class ResourceQueryTestBase {
@Before
public void setup() {
- schemaProvider = new MockSchemaProvider(getTestSchemaName());
+ SchemaProvider schemaProvider = new MockSchemaProvider(getTestSchemaName());
+ context.registerService(SchemaProvider.class, schemaProvider);
final String resourceType = "RT-" + UUID.randomUUID();
final String path = "/some/path/" + UUID.randomUUID();
resource = Mockito.mock(Resource.class);
@@ -66,20 +66,20 @@ public abstract class ResourceQueryTestBase {
context.registerInjectActivateService(new ScriptedDataFetcherProvider());
context.registerInjectActivateService(new SlingDataFetcherSelector());
- dataFetchersSelector = context.getService(SlingDataFetcherSelector.class);
context.registerInjectActivateService(new SlingScalarsProvider());
- scalarsProvider = context.getService(SlingScalarsProvider.class);
+ context.registerInjectActivateService(new RankedSchemaProviders());
+ context.registerInjectActivateService(new DefaultQueryExecutor());
}
protected String queryJSON(String stmt) throws Exception {
return queryJSON(stmt, new String[]{});
}
- protected String queryJSON(String stmt, String [] selectors) throws Exception {
- final ExecutionResult result = GraphQLResourceQuery.executeQuery(schemaProvider,
- dataFetchersSelector, scalarsProvider, resource, selectors, stmt, Collections.emptyMap());
- assertTrue("Expecting no errors: " + result.getErrors(), result.getErrors().isEmpty());
- return new JsonSerializer().toJSON(result);
+ protected String queryJSON(String stmt, String [] selectors) {
+ final QueryExecutor queryExecutor = context.getService(QueryExecutor.class);
+ assertNotNull(queryExecutor);
+ JsonObject json = queryExecutor.execute(stmt, Collections.emptyMap(), resource, selectors);
+ return json.toString();
}
protected void setupAdditionalServices() {
diff --git a/src/test/java/org/apache/sling/graphql/core/engine/SlingDataFetcherNameValidationTest.java b/src/test/java/org/apache/sling/graphql/core/engine/SlingDataFetcherNameValidationTest.java
index bd14552..45508c4 100644
--- a/src/test/java/org/apache/sling/graphql/core/engine/SlingDataFetcherNameValidationTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/engine/SlingDataFetcherNameValidationTest.java
@@ -59,14 +59,8 @@ public class SlingDataFetcherNameValidationTest {
@Test
public void testValidation() {
- boolean valid = true;
- try {
- GraphQLResourceQuery.validateFetcherName(name);
- } catch(IOException ioe) {
- valid = false;
- }
final String msg = String.format("Expecting '%s' to be %s", name, expectValid ? "valid" : "invalid");
- assertEquals(msg, valid, expectValid);
+ assertEquals(msg, SlingDataFetcherSelector.nameMatchesPattern(name), expectValid);
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/org/apache/sling/graphql/core/engine/SlingDataFetcherSelectorTest.java b/src/test/java/org/apache/sling/graphql/core/engine/SlingDataFetcherSelectorTest.java
index 3795e02..bc81723 100644
--- a/src/test/java/org/apache/sling/graphql/core/engine/SlingDataFetcherSelectorTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/engine/SlingDataFetcherSelectorTest.java
@@ -20,14 +20,19 @@ package org.apache.sling.graphql.core.engine;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
import java.io.IOException;
import com.example.fetchers.DoNothingFetcher;
import org.apache.sling.graphql.api.SlingDataFetcher;
+import org.apache.sling.graphql.api.SlingDataFetcherEnvironment;
import org.apache.sling.graphql.core.mocks.DigestDataFetcher;
import org.apache.sling.graphql.core.mocks.EchoDataFetcher;
import org.apache.sling.graphql.core.mocks.TestUtil;
@@ -46,7 +51,7 @@ public class SlingDataFetcherSelectorTest {
@Before
public void setup() {
- final ScriptedDataFetcherProvider sdfp = Mockito.mock(ScriptedDataFetcherProvider.class);
+ final ScriptedDataFetcherProvider sdfp = mock(ScriptedDataFetcherProvider.class);
context.bundleContext().registerService(ScriptedDataFetcherProvider.class, sdfp, null);
context.registerInjectActivateService(new SlingDataFetcherSelector());
selector = context.getService(SlingDataFetcherSelector.class);
@@ -54,8 +59,9 @@ public class SlingDataFetcherSelectorTest {
TestUtil.registerSlingDataFetcher(context.bundleContext(), "sling/digest", new DigestDataFetcher());
TestUtil.registerSlingDataFetcher(context.bundleContext(), "sling/shouldFail", new DoNothingFetcher());
TestUtil.registerSlingDataFetcher(context.bundleContext(), "example/ok", new DoNothingFetcher());
- TestUtil.registerSlingDataFetcher(context.bundleContext(), "sling/duplicate", new EchoDataFetcher(451));
- TestUtil.registerSlingDataFetcher(context.bundleContext(), "sling/duplicate", new DigestDataFetcher());
+ TestUtil.registerSlingDataFetcher(context.bundleContext(), "sling/duplicate", 0, new DigestDataFetcher());
+ TestUtil.registerSlingDataFetcher(context.bundleContext(), "sling/duplicate", 10, new EchoDataFetcher(451));
+ TestUtil.registerSlingDataFetcher(context.bundleContext(), "sling/duplicate", 5, new EchoDataFetcher(452));
}
@Test
@@ -71,18 +77,15 @@ public class SlingDataFetcherSelectorTest {
}
@Test
- public void reservedNameError() throws Exception {
- try {
- selector.getSlingFetcher("sling/shouldFail");
- fail("Expected getSlingFetcher to fail");
- } catch(Exception e) {
- TestUtil.assertNestedException(e, IOException.class, DoNothingFetcher.class.getName());
- TestUtil.assertNestedException(e, IOException.class, "starting with 'sling/' are reserved for Apache Sling");
- }
+ public void reservedNameError() {
+ assertNull(selector.getSlingFetcher("sling/shouldFail"));
}
- @Test(expected=IOException.class)
- public void duplicateFetcherError() throws Exception {
+ @Test
+ public void sameNameFetcher() throws Exception {
final SlingDataFetcher<Object> sdf = selector.getSlingFetcher("sling/duplicate");
+ assertNotNull(sdf);
+ assertEquals(EchoDataFetcher.class, sdf.getClass());
+ assertEquals(451, sdf.get(mock(SlingDataFetcherEnvironment.class)));
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/org/apache/sling/graphql/core/it/GraphQLCoreTestSupport.java b/src/test/java/org/apache/sling/graphql/core/it/GraphQLCoreTestSupport.java
index 71c948a..9b4712c 100644
--- a/src/test/java/org/apache/sling/graphql/core/it/GraphQLCoreTestSupport.java
+++ b/src/test/java/org/apache/sling/graphql/core/it/GraphQLCoreTestSupport.java
@@ -18,15 +18,24 @@
*/
package org.apache.sling.graphql.core.it;
-import javax.inject.Inject;
+import java.io.Reader;
+import java.io.StringReader;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
-import com.cedarsoftware.util.io.JsonWriter;
+import javax.inject.Inject;
-import org.apache.commons.io.output.WriterOutputStream;
import org.apache.commons.lang3.StringUtils;
+import org.apache.johnzon.mapper.Mapper;
+import org.apache.johnzon.mapper.MapperBuilder;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
-import org.apache.sling.graphql.core.json.JsonSerializer;
+import org.apache.sling.engine.SlingRequestProcessor;
import org.apache.sling.graphql.core.mocks.TestDataFetcherComponent;
import org.apache.sling.servlethelpers.MockSlingHttpServletResponse;
import org.apache.sling.servlethelpers.internalrequests.SlingInternalRequest;
@@ -39,32 +48,20 @@ import org.ops4j.pax.exam.options.ModifiableCompositeOption;
import org.ops4j.pax.exam.options.extra.VMOption;
import org.ops4j.pax.tinybundles.core.TinyBundle;
import org.osgi.framework.Constants;
-import org.apache.sling.engine.SlingRequestProcessor;
import static org.apache.sling.testing.paxexam.SlingOptions.slingCommonsMetrics;
-import static org.junit.Assert.fail;
import static org.apache.sling.testing.paxexam.SlingOptions.slingQuickstartOakTar;
import static org.apache.sling.testing.paxexam.SlingOptions.slingResourcePresence;
import static org.apache.sling.testing.paxexam.SlingOptions.slingScripting;
import static org.apache.sling.testing.paxexam.SlingOptions.slingScriptingJsp;
+import static org.junit.Assert.fail;
import static org.ops4j.pax.exam.CoreOptions.composite;
import static org.ops4j.pax.exam.CoreOptions.junitBundles;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
-import static org.ops4j.pax.exam.CoreOptions.vmOption;
-import static org.ops4j.pax.exam.CoreOptions.when;
import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.exam.CoreOptions.when;
import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
-import java.io.Reader;
-import java.io.StringReader;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
public abstract class GraphQLCoreTestSupport extends TestSupport {
private final static int STARTUP_WAIT_SECONDS = 30;
@@ -95,12 +92,13 @@ public abstract class GraphQLCoreTestSupport extends TestSupport {
slingQuickstart(),
graphQLJava(),
testBundle("bundle.filename"),
- buildBundleWithExportedPackages(JsonSerializer.class, WriterOutputStream.class),
newConfiguration("org.apache.sling.jcr.base.internal.LoginAdminWhitelist")
.put("whitelist.bundles.regexp", "^PAXEXAM.*$")
.asOption(),
mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.servlet-helpers").versionAsInProject(),
- mavenBundle().groupId("com.cedarsoftware").artifactId("json-io").versionAsInProject(),
+ mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.commons.johnzon").versionAsInProject(),
+ mavenBundle().groupId("org.apache.johnzon").artifactId("johnzon-core").versionAsInProject(),
+ mavenBundle().groupId("org.apache.johnzon").artifactId("johnzon-mapper").versionAsInProject(),
slingResourcePresence(),
slingCommonsMetrics(),
jsonPath(),
@@ -227,7 +225,8 @@ public abstract class GraphQLCoreTestSupport extends TestSupport {
}
protected String toJSON(Object source) {
- return JsonWriter.objectToJson(source, JsonSerializer.WRITER_OPTIONS);
+ Mapper mapper = new MapperBuilder().build();
+ return mapper.toStructure(source).toString();
}
protected Map<String, Object> toMap(String ...keyValuePairs) {
diff --git a/src/test/java/org/apache/sling/graphql/core/it/GraphQLServletIT.java b/src/test/java/org/apache/sling/graphql/core/it/GraphQLServletIT.java
index c685c88..a185058 100644
--- a/src/test/java/org/apache/sling/graphql/core/it/GraphQLServletIT.java
+++ b/src/test/java/org/apache/sling/graphql/core/it/GraphQLServletIT.java
@@ -250,8 +250,8 @@ public class GraphQLServletIT extends GraphQLCoreTestSupport {
@Test
public void testMultipleSchemaProviders() throws Exception {
- new ReplacingSchemaProvider("currentResource", "REPLACED").register(bundleContext, defaultSchemaProvider, 1);
- new ReplacingSchemaProvider("currentResource", "NOT_THIS_ONE").register(bundleContext, defaultSchemaProvider, Integer.MAX_VALUE);
+ new ReplacingSchemaProvider("currentResource", "REPLACED").register(bundleContext, defaultSchemaProvider, Integer.MAX_VALUE);
+ new ReplacingSchemaProvider("currentResource", "NOT_THIS_ONE").register(bundleContext, defaultSchemaProvider, 1);
final String json = getContent("/graphql/two.gql", "query", "{ REPLACED { resourceType name } }");
assertThat(json, hasJsonPath("$.data.REPLACED.resourceType", equalTo("graphql/test/two")));
assertThat(json, hasJsonPath("$.data.REPLACED.name", equalTo("two")));
diff --git a/src/test/java/org/apache/sling/graphql/core/it/ServerSideQueryIT.java b/src/test/java/org/apache/sling/graphql/core/it/ServerSideQueryIT.java
index a1509b5..e8965dd 100644
--- a/src/test/java/org/apache/sling/graphql/core/it/ServerSideQueryIT.java
+++ b/src/test/java/org/apache/sling/graphql/core/it/ServerSideQueryIT.java
@@ -84,8 +84,8 @@ public class ServerSideQueryIT extends GraphQLCoreTestSupport {
@Test
public void testMultipleSchemaProviders() throws Exception {
- new ReplacingSchemaProvider("scriptedSchemaResource", "REPLACED").register(bundleContext, defaultSchemaProvider, 1);
- new ReplacingSchemaProvider("scriptedSchemaResource", "NOT_THIS_ONE").register(bundleContext, defaultSchemaProvider, Integer.MAX_VALUE);
+ new ReplacingSchemaProvider("scriptedSchemaResource", "REPLACED").register(bundleContext, defaultSchemaProvider, Integer.MAX_VALUE);
+ new ReplacingSchemaProvider("scriptedSchemaResource", "NOT_THIS_ONE").register(bundleContext, defaultSchemaProvider, 1);
assertDefaultContent(".REPLACED", "REPLACED");
}
diff --git a/src/test/java/org/apache/sling/graphql/core/json/JsonSerializerTest.java b/src/test/java/org/apache/sling/graphql/core/json/JsonSerializerTest.java
deleted file mode 100644
index c2b9d5b..0000000
--- a/src/test/java/org/apache/sling/graphql/core/json/JsonSerializerTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.json;
-
-import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.Matchers.nullValue;
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import graphql.ExecutionResult;
-
-public class JsonSerializerTest {
- private final JsonSerializer serializer = new JsonSerializer();
-
- private static final Map<String, Object> TEST_MAP = toMap("A", "bc", "farenheit", 451, "really", true, "itsNothing",
- null, "map", toMap("one", 1, "two", toMap("three", 3)));
-
- private static Map<String, Object> toMap(Object... keyValuePairs) {
- final Map<String, Object> result = new HashMap<>();
- for (int i = 0; i < keyValuePairs.length; i += 2) {
- result.put(keyValuePairs[i].toString(), keyValuePairs[i + 1]);
- }
- return result;
- }
-
- private void assertHasTestData(String json) {
- assertThat(json, hasJsonPath("A", equalTo("bc")));
- assertThat(json, hasJsonPath("farenheit", equalTo(451)));
- assertThat(json, hasJsonPath("really", equalTo(true)));
- assertThat(json, hasJsonPath("itsNothing", equalTo(null)));
- assertThat(json, hasJsonPath("map.one", equalTo(1)));
- assertThat(json, hasJsonPath("map.two.three", equalTo(3)));
- }
-
- @Test
- public void testSendJSON() throws IOException {
- final ExecutionResult r = Mockito.mock(ExecutionResult.class);
- Mockito.when(r.toSpecification()).thenReturn(TEST_MAP);
- final StringWriter w = new StringWriter();
- serializer.sendJSON(w, r);
- assertHasTestData(w.toString());
- }
-
- @Test
- public void testToJSON() {
- assertHasTestData(serializer.toJSON(TEST_MAP));
- }
-
- @Test
- public void testToMap() throws UnsupportedEncodingException {
- final String json = serializer.toJSON(TEST_MAP);
- final Map<String, Object> map = serializer.jsonToMaps(new ByteArrayInputStream(json.getBytes("UTF-8")));
- assertThat(map.get("A"), is("bc"));
- assertThat(map.get("farenheit"), is(451L));
- assertThat(map.get("really"), is(true));
- assertThat(map.get("itsNothing"), nullValue());
- assertThat(map.get("map").toString(), equalTo("{one=1, two={three=3}}"));
- }
-}
diff --git a/src/test/java/org/apache/sling/graphql/core/mocks/TestUtil.java b/src/test/java/org/apache/sling/graphql/core/mocks/TestUtil.java
index 2f85566..a68d3cb 100644
--- a/src/test/java/org/apache/sling/graphql/core/mocks/TestUtil.java
+++ b/src/test/java/org/apache/sling/graphql/core/mocks/TestUtil.java
@@ -27,12 +27,20 @@ import java.util.Hashtable;
import org.apache.sling.graphql.api.SlingDataFetcher;
import org.apache.sling.graphql.api.SlingScalarConverter;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
public class TestUtil {
+
public static ServiceRegistration<?> registerSlingDataFetcher(BundleContext bc, String name, SlingDataFetcher<?> f) {
+ return registerSlingDataFetcher(bc, name, 0, f);
+ }
+
+ public static ServiceRegistration<?> registerSlingDataFetcher(BundleContext bc, String name, int serviceRanking,
+ SlingDataFetcher<?> f) {
final Dictionary<String, Object> props = new Hashtable<>();
props.put(SlingDataFetcher.NAME_SERVICE_PROPERTY, name);
+ props.put(Constants.SERVICE_RANKING, serviceRanking);
return bc.registerService(SlingDataFetcher.class, f, props);
}
@@ -56,4 +64,4 @@ public class TestUtil {
clazz.getName(), messageContains));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/org/apache/sling/graphql/core/osgi/ServiceReferenceObjectTupleTest.java b/src/test/java/org/apache/sling/graphql/core/osgi/ServiceReferenceObjectTupleTest.java
new file mode 100644
index 0000000..c0d57b5
--- /dev/null
+++ b/src/test/java/org/apache/sling/graphql/core/osgi/ServiceReferenceObjectTupleTest.java
@@ -0,0 +1,80 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.osgi;
+
+import java.util.HashMap;
+import java.util.TreeSet;
+
+import javax.servlet.Servlet;
+
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+public class ServiceReferenceObjectTupleTest {
+
+ @Rule
+ public OsgiContext osgiContext = new OsgiContext();
+
+ @Test
+ public void testEqualsAndOrdering() throws InvalidSyntaxException {
+ Servlet servlet1 = mock(Servlet.class);
+ Servlet servlet2 = mock(Servlet.class);
+ osgiContext.registerService(Servlet.class, servlet1, Constants.SERVICE_RANKING, 1);
+ osgiContext.registerService(Servlet.class, servlet2, Constants.SERVICE_RANKING, 2);
+
+ ServiceReference<Servlet> sr1 =
+ osgiContext.bundleContext().getServiceReferences(Servlet.class, "(service.ranking=1)").stream().findFirst().orElse(null);
+
+ ServiceReference<Servlet> sr2 =
+ osgiContext.bundleContext().getServiceReferences(Servlet.class, "(service.ranking=2)").stream().findFirst().orElse(null);
+
+ ServiceReference<Servlet> sr3 =
+ osgiContext.bundleContext().getServiceReferences(Servlet.class, "(service.ranking=1)").stream().findFirst().orElse(null);
+
+ ServiceReferenceObjectTuple<Servlet> t1 = new ServiceReferenceObjectTuple(sr1, servlet1);
+ ServiceReferenceObjectTuple<Servlet> t3 = new ServiceReferenceObjectTuple(sr1, servlet1);
+ ServiceReferenceObjectTuple<Servlet> t2 = new ServiceReferenceObjectTuple(sr2, servlet2);
+
+ assertNotEquals(t1, t2);
+ assertEquals(t1, t3);
+
+ TreeSet<ServiceReferenceObjectTuple<Servlet>> treeSet = new TreeSet<>();
+ assertTrue(treeSet.add(t1));
+ assertTrue(treeSet.add(t2));
+ assertFalse(treeSet.add(t3));
+
+ HashMap<ServiceReferenceObjectTuple<Servlet>, Integer> hashMap = new HashMap<>();
+ assertNull(hashMap.put(t1, 1));
+ assertNull(hashMap.put(t2, 2));
+ Integer previous = hashMap.put(t3, 3);
+ assertTrue(previous != null && previous == 1);
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/graphql/core/schema/RankedSchemaProvidersTest.java b/src/test/java/org/apache/sling/graphql/core/schema/RankedSchemaProvidersTest.java
index 2e83ae7..15dd73b 100644
--- a/src/test/java/org/apache/sling/graphql/core/schema/RankedSchemaProvidersTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/schema/RankedSchemaProvidersTest.java
@@ -80,26 +80,29 @@ public class RankedSchemaProvidersTest {
public void providerPriorities() throws IOException {
assertProvider("Beginning", DEFAULT_SCHEMA_PROVIDER_OUTPUT);
- registerProvider("Y", DEFAULT_SERVICE_RANKING + 1);
- assertProvider("After Y", DEFAULT_SCHEMA_PROVIDER_OUTPUT);
+ final ServiceRegistration<?> y = registerProvider("Y", DEFAULT_SERVICE_RANKING + 1);
+ assertProvider("After Default", "Y");
final ServiceRegistration<?> z = registerProvider("Z", DEFAULT_SERVICE_RANKING - 1);
- assertProvider("After Z", "Z");
+ assertProvider("Before Default", "Y");
final ServiceRegistration<?> a = registerProvider("A", 1);
assertProvider("After A", "A");
final ServiceRegistration<?> b = registerProvider("B", 2);
- assertProvider("After B", "A");
+ assertProvider("After B", "B");
a.unregister();
assertProvider("After removing A", "B");
b.unregister();
- assertProvider("After removing B", "Z");
+ assertProvider("After removing B", "Y");
z.unregister();
- assertProvider("After removing Z", DEFAULT_SCHEMA_PROVIDER_OUTPUT);
+ assertProvider("After removing Z", "Y");
+
+ y.unregister();
+ assertProvider("After removing Y", DEFAULT_SCHEMA_PROVIDER_OUTPUT);
}
@Test
@@ -110,4 +113,4 @@ public class RankedSchemaProvidersTest {
registerProvider("A", 1);
assertProvider("After A", "A");
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/org/apache/sling/graphql/core/schema/SchemaDescriptionsTest.java b/src/test/java/org/apache/sling/graphql/core/schema/SchemaDescriptionsTest.java
index 82cbf66..0b247a2 100644
--- a/src/test/java/org/apache/sling/graphql/core/schema/SchemaDescriptionsTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/schema/SchemaDescriptionsTest.java
@@ -18,34 +18,34 @@
*/
package org.apache.sling.graphql.core.schema;
-import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
-
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
import java.util.Collections;
import java.util.UUID;
+import javax.json.JsonObject;
+
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.servlets.ServletResolver;
import org.apache.sling.graphql.api.SchemaProvider;
-import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
-import org.hamcrest.CustomMatcher;
-import org.hamcrest.Matcher;
-import org.apache.sling.graphql.core.engine.GraphQLResourceQuery;
+import org.apache.sling.graphql.api.engine.QueryExecutor;
+import org.apache.sling.graphql.core.engine.DefaultQueryExecutor;
import org.apache.sling.graphql.core.engine.ScriptedDataFetcherProvider;
import org.apache.sling.graphql.core.engine.SlingDataFetcherSelector;
-import org.apache.sling.graphql.core.json.JsonSerializer;
import org.apache.sling.graphql.core.mocks.MockSchemaProvider;
import org.apache.sling.graphql.core.scalars.SlingScalarsProvider;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.hamcrest.CustomMatcher;
+import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mockito;
-import graphql.ExecutionResult;
import net.minidev.json.JSONArray;
+import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
/**
* Test the fields descriptions which are part of the schema as per
* http://spec.graphql.org/June2018/#sec-Descriptions
@@ -55,9 +55,6 @@ import net.minidev.json.JSONArray;
* containing just a hash character.
*/
public class SchemaDescriptionsTest {
- private SchemaProvider schemaProvider = new MockSchemaProvider("test-schema");
- private SlingDataFetcherSelector dataFetchersSelector;
- private SlingScalarsProvider scalarsProvider;
private Resource resource;
private String schemaJson;
@@ -107,17 +104,18 @@ public class SchemaDescriptionsTest {
context.bundleContext().registerService(ServletResolver.class, servletResolver, null);
context.registerInjectActivateService(new ScriptedDataFetcherProvider());
context.registerInjectActivateService(new SlingDataFetcherSelector());
- dataFetchersSelector = context.getService(SlingDataFetcherSelector.class);
context.registerInjectActivateService(new SlingScalarsProvider());
- scalarsProvider = context.getService(SlingScalarsProvider.class);
+ context.registerService(SchemaProvider.class, new MockSchemaProvider("test-schema"));
+ context.registerInjectActivateService(new RankedSchemaProviders());
+ context.registerInjectActivateService(new DefaultQueryExecutor());
schemaJson = queryJSON(SCHEMA_QUERY);
}
private String queryJSON(String stmt) throws Exception {
- final ExecutionResult result = GraphQLResourceQuery.executeQuery(schemaProvider,
- dataFetchersSelector, scalarsProvider, resource, new String[] {}, stmt, Collections.emptyMap());
- assertTrue("Expecting no errors: " + result.getErrors(), result.getErrors().isEmpty());
- return new JsonSerializer().toJSON(result);
+ QueryExecutor queryExecutor = context.getService(QueryExecutor.class);
+ assertNotNull(queryExecutor);
+ JsonObject json = queryExecutor.execute(stmt, Collections.emptyMap(), resource, new String[] {});
+ return json.toString();
}
private void assertTypeDescription(String typeName, String expected) {
diff --git a/src/test/java/org/apache/sling/graphql/core/servlet/GraphQLServletTest.java b/src/test/java/org/apache/sling/graphql/core/servlet/GraphQLServletTest.java
index 100bdc0..0c1ab0c 100644
--- a/src/test/java/org/apache/sling/graphql/core/servlet/GraphQLServletTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/servlet/GraphQLServletTest.java
@@ -30,12 +30,8 @@ import org.apache.sling.api.servlets.ServletResolverConstants;
import org.apache.sling.commons.metrics.Counter;
import org.apache.sling.commons.metrics.MetricsService;
import org.apache.sling.commons.metrics.Timer;
-import org.apache.sling.graphql.api.SchemaProvider;
+import org.apache.sling.graphql.api.engine.QueryExecutor;
import org.apache.sling.graphql.core.cache.SimpleGraphQLCacheProvider;
-import org.apache.sling.graphql.core.engine.GraphQLResourceQuery;
-import org.apache.sling.graphql.core.engine.SlingDataFetcherSelector;
-import org.apache.sling.graphql.core.scalars.SlingScalarsProvider;
-import org.apache.sling.graphql.core.schema.RankedSchemaProviders;
import org.apache.sling.testing.mock.sling.junit.SlingContext;
import org.apache.sling.testing.mock.sling.servlet.MockRequestPathInfo;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest;
@@ -44,7 +40,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.MockedStatic;
import org.mockito.junit.MockitoJUnitRunner;
import com.codahale.metrics.MetricRegistry;
@@ -52,9 +47,7 @@ import com.codahale.metrics.MetricRegistry;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
@@ -66,30 +59,23 @@ public class GraphQLServletTest {
@Before
public void setUp() {
MetricsService metricsService = mock(MetricsService.class);
- when(metricsService.counter(anyString())).thenReturn(mock(Counter.class));
+ when(metricsService.counter(any(String.class))).thenReturn(mock(Counter.class));
Timer timer = mock(Timer.class);
when(timer.time()).thenReturn(mock(Timer.Context.class));
- when(metricsService.timer(anyString())).thenReturn(timer);
+ when(metricsService.timer(any(String.class))).thenReturn(timer);
context.registerService(MetricsService.class, metricsService);
MetricRegistry metricRegistry = mock(MetricRegistry.class);
context.registerService(MetricRegistry.class, metricRegistry, "name", "sling");
+
+ QueryExecutor queryExecutor = mock(QueryExecutor.class);
+ when(queryExecutor.isValid(any(String.class), any(Map.class), any(Resource.class), any(String[].class))).thenReturn(true);
+ context.registerService(QueryExecutor.class, queryExecutor);
}
@Test
public void testCachingErrors() throws IOException {
- try (MockedStatic<GraphQLResourceQuery> graphQLResourceQueryMockedStatic = mockStatic(GraphQLResourceQuery.class)) {
- graphQLResourceQueryMockedStatic.when(() -> GraphQLResourceQuery.isQueryValid(any(SchemaProvider.class),
- any(SlingDataFetcherSelector.class), any(SlingScalarsProvider.class), any(Resource.class), any(String[].class),
- anyString(), any(Map.class))).thenReturn(true);
- RankedSchemaProviders rankedSchemaProviders = mock(RankedSchemaProviders.class);
- context.registerService(rankedSchemaProviders);
- SlingDataFetcherSelector slingDataFetcherSelector = mock(SlingDataFetcherSelector.class);
- context.registerService(slingDataFetcherSelector);
- SlingScalarsProvider slingScalarsProvider = mock(SlingScalarsProvider.class);
- context.registerService(slingScalarsProvider);
-
context.registerInjectActivateService(new SimpleGraphQLCacheProvider(), "maxMemory", 10);
context.registerInjectActivateService(new GraphQLServlet(), ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES, "a/b/c",
@@ -114,17 +100,10 @@ public class GraphQLServletTest {
servlet.doPost(request, response);
assertEquals(500, response.getStatus());
- }
}
@Test
public void testDisabledSuffix() throws IOException {
- RankedSchemaProviders rankedSchemaProviders = mock(RankedSchemaProviders.class);
- context.registerService(rankedSchemaProviders);
- SlingDataFetcherSelector slingDataFetcherSelector = mock(SlingDataFetcherSelector.class);
- context.registerService(slingDataFetcherSelector);
- SlingScalarsProvider slingScalarsProvider = mock(SlingScalarsProvider.class);
- context.registerService(slingScalarsProvider);
context.registerInjectActivateService(new SimpleGraphQLCacheProvider());
context.registerInjectActivateService(new GraphQLServlet(), ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES, "a/b/c",
"persistedQueries.suffix", "");