This is an automated email from the ASF dual-hosted git repository.

bdelacretaz pushed a commit to branch feature/SLING-9550
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-core.git


The following commit(s) were added to refs/heads/feature/SLING-9550 by this 
push:
     new 1f61560  SLING-9550 - Use SlingScalarConverter services
1f61560 is described below

commit 1f615603fae31595aecb98027ec857dca0282500
Author: Bertrand Delacretaz <[email protected]>
AuthorDate: Fri Jun 26 15:55:22 2020 +0200

    SLING-9550 - Use SlingScalarConverter services
---
 ...ge-info.java => ScalarConversionException.java} |  29 ++++--
 .../sling/graphql/api/SlingScalarConverter.java    |  46 +++++++++
 .../org/apache/sling/graphql/api/package-info.java |   2 +-
 .../graphql/core/engine/GraphQLResourceQuery.java  |   2 +-
 .../engine/SlingGraphQLException.java}             |  20 ++--
 .../graphql/core/scalars/SlingCoercingWrapper.java |  74 ++++++++++++++
 .../graphql/core/scalars/SlingScalarsProvider.java | 109 ++++++++-------------
 .../graphql/core/engine/CustomScalarsTest.java     |  19 ++--
 .../core/engine/GraphQLResourceQueryTest.java      |   2 +-
 .../graphql/core/engine/ResourceQueryTestBase.java |   8 +-
 .../apache/sling/graphql/core/mocks/TestUtil.java  |   7 ++
 .../graphql/core/mocks/URLScalarConverter.java     |  44 +++++++++
 .../core/mocks/UppercaseScalarConverter.java}      |  25 +++--
 13 files changed, 288 insertions(+), 99 deletions(-)

diff --git a/src/main/java/org/apache/sling/graphql/api/package-info.java 
b/src/main/java/org/apache/sling/graphql/api/ScalarConversionException.java
similarity index 59%
copy from src/main/java/org/apache/sling/graphql/api/package-info.java
copy to 
src/main/java/org/apache/sling/graphql/api/ScalarConversionException.java
index c4934ad..bbb51e5 100644
--- a/src/main/java/org/apache/sling/graphql/api/package-info.java
+++ b/src/main/java/org/apache/sling/graphql/api/ScalarConversionException.java
@@ -17,10 +17,27 @@
  * under the License.
  */
 
- /**
-  * This package contains APIs which are independent of
-  * a specific implementation of the underlying graphQL engine.
-  */
-@Version("3.0.1")
 package org.apache.sling.graphql.api;
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+
+import javax.script.ScriptException;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Exception thrown by Scalar converters
+ */
+public class ScalarConversionException extends ScriptException {
+    private static final long serialVersionUID = 1L;
+
+    public ScalarConversionException(@NotNull String reason, @Nullable 
Throwable cause) {
+        super(reason);
+        if(cause != null) {
+            initCause(cause);
+        }
+    }
+
+    public ScalarConversionException(@NotNull String reason) {
+        super(reason);
+    }
+}
diff --git 
a/src/main/java/org/apache/sling/graphql/api/SlingScalarConverter.java 
b/src/main/java/org/apache/sling/graphql/api/SlingScalarConverter.java
new file mode 100644
index 0000000..1d51b8b
--- /dev/null
+++ b/src/main/java/org/apache/sling/graphql/api/SlingScalarConverter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.jetbrains.annotations.Nullable;
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * A service that parses and serializes a custom GraphQL Scalar by 
+ * converting between an eXternal type X an an inTernal one T.
+ * 
+ * Instances of this service must have a {@link SlingScalarConverter.NAME}
+ * service property which is the name of the scalar type.
+ */
+@ConsumerType
+public interface SlingScalarConverter<T, X> {
+    
+    String NAME_SERVICE_PROPERTY = "name";
+
+    /** Parse an external value (a query argument for example) into its 
internal representation */
+    @Nullable 
+    T parseValue(@Nullable X input) throws ScalarConversionException;
+
+    /** Serialize an internal value (provided by a {@link SlingDataFetcher} 
into its
+     *  external representation.
+     */
+    @Nullable
+    X serialize(@Nullable T value) throws ScalarConversionException;
+}
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 c4934ad..496bec5 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.0.1")
+@Version("3.1.0")
 package org.apache.sling.graphql.api;
 import org.osgi.annotation.versioning.Version;
\ No newline at end of file
diff --git 
a/src/main/java/org/apache/sling/graphql/core/engine/GraphQLResourceQuery.java 
b/src/main/java/org/apache/sling/graphql/core/engine/GraphQLResourceQuery.java
index 9bdc733..711561d 100644
--- 
a/src/main/java/org/apache/sling/graphql/core/engine/GraphQLResourceQuery.java
+++ 
b/src/main/java/org/apache/sling/graphql/core/engine/GraphQLResourceQuery.java
@@ -116,7 +116,7 @@ public class GraphQLResourceQuery {
 
     private GraphQLSchema buildSchema(String sdl, SlingDataFetcherSelector 
fetchers, SlingScalarsProvider scalarsProvider, Resource currentResource) 
throws IOException {
         TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
-        Iterable<GraphQLScalarType> scalars = 
scalarsProvider.getScalars(typeRegistry.scalars());
+        Iterable<GraphQLScalarType> scalars = 
scalarsProvider.getCustomScalars(typeRegistry.scalars());
         RuntimeWiring runtimeWiring = buildWiring(typeRegistry, fetchers, 
scalars, currentResource);
         SchemaGenerator schemaGenerator = new SchemaGenerator();
         return schemaGenerator.makeExecutableSchema(typeRegistry, 
runtimeWiring);
diff --git a/src/main/java/org/apache/sling/graphql/api/package-info.java 
b/src/main/java/org/apache/sling/graphql/core/engine/SlingGraphQLException.java
similarity index 69%
copy from src/main/java/org/apache/sling/graphql/api/package-info.java
copy to 
src/main/java/org/apache/sling/graphql/core/engine/SlingGraphQLException.java
index c4934ad..9e213f1 100644
--- a/src/main/java/org/apache/sling/graphql/api/package-info.java
+++ 
b/src/main/java/org/apache/sling/graphql/core/engine/SlingGraphQLException.java
@@ -17,10 +17,16 @@
  * under the License.
  */
 
- /**
-  * This package contains APIs which are independent of
-  * a specific implementation of the underlying graphQL engine.
-  */
-@Version("3.0.1")
-package org.apache.sling.graphql.api;
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+package org.apache.sling.graphql.core.engine;
+
+public class SlingGraphQLException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    public SlingGraphQLException(String reason, Throwable cause) {
+        super(reason, cause);
+    }
+
+    public SlingGraphQLException(String reason) {
+        super(reason);
+    }
+}
\ No newline at end of file
diff --git 
a/src/main/java/org/apache/sling/graphql/core/scalars/SlingCoercingWrapper.java 
b/src/main/java/org/apache/sling/graphql/core/scalars/SlingCoercingWrapper.java
new file mode 100644
index 0000000..4ff19dd
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/graphql/core/scalars/SlingCoercingWrapper.java
@@ -0,0 +1,74 @@
+
+/*
+ * 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.scalars;
+
+import org.apache.sling.graphql.api.ScalarConversionException;
+import org.apache.sling.graphql.api.SlingScalarConverter;
+
+import graphql.language.StringValue;
+import graphql.schema.Coercing;
+import graphql.schema.CoercingParseLiteralException;
+import graphql.schema.CoercingParseValueException;
+import graphql.schema.CoercingSerializeException;
+
+/** Wraps {@link SlingScalarConverter} into a GraphQL-java Coercing */
+class SlingCoercingWrapper implements Coercing<Object, Object> {
+    private final SlingScalarConverter<Object, Object> converter;
+
+    SlingCoercingWrapper(SlingScalarConverter<Object, Object> c) {
+        converter = c;
+    }
+
+    @Override
+    public Object serialize(Object dataFetcherResult) throws 
CoercingSerializeException {
+        try {
+            return converter.serialize(dataFetcherResult);
+        } catch(ScalarConversionException sce) {
+            throw new CoercingSerializeException(sce);
+        }
+    }
+
+    @Override
+    public Object parseValue(Object input) throws CoercingParseValueException {
+        try {
+            return converter.parseValue(input);
+        } catch(ScalarConversionException sce) {
+            throw new CoercingSerializeException(sce);
+        }
+    }
+
+    @Override
+    public Object parseLiteral(Object input) throws 
CoercingParseLiteralException {
+        // This is called when parsing objects from the GraphQL Abstract 
Syntax Tree
+        // So far we handle StringValue only and unfortunately there's no 
common
+        // interface for the getValue() method.
+        try {
+            if(input instanceof StringValue) {
+                return converter.parseValue(((StringValue)input).getValue());
+            } else {
+                return converter.parseValue(input);
+            }
+        } catch(ScalarConversionException sce) {
+            throw new CoercingSerializeException(sce);
+        }
+    }
+
+ }
\ No newline at end of file
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 4fa3ac7..8ffde2e 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,20 +20,23 @@
 
 package org.apache.sling.graphql.core.scalars;
 
-import java.net.URL;
 import java.util.Map;
 import java.util.stream.Collectors;
 
+import javax.script.ScriptException;
+
+import org.apache.sling.graphql.api.SlingScalarConverter;
+import org.apache.sling.graphql.core.engine.SlingGraphQLException;
+import org.osgi.framework.BundleContext;
 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 graphql.language.ScalarTypeDefinition;
-import graphql.language.StringValue;
-import graphql.schema.Coercing;
-import graphql.schema.CoercingParseLiteralException;
-import graphql.schema.CoercingParseValueException;
-import graphql.schema.CoercingSerializeException;
 import graphql.schema.GraphQLScalarType;
+import graphql.schema.idl.ScalarInfo;
 
 /**
  * Provides GraphQL Scalars (leaf data types) for query execution
@@ -43,78 +46,50 @@ import graphql.schema.GraphQLScalarType;
     Constants.SERVICE_VENDOR + "=The Apache Software Foundation" })
 public class SlingScalarsProvider {
 
-    static class URLCoercing implements Coercing<URL, String> {
-        static final String NAME = "URL";
-
-        @Override
-        public String serialize(Object dataFetcherResult) throws 
CoercingSerializeException {
-            if(dataFetcherResult instanceof URL) {
-                return getClass().getSimpleName() + " says:" + 
((URL)dataFetcherResult).toExternalForm(); 
-            }
-            throw new CoercingSerializeException("Unexpected serialize input " 
+ dataFetcherResult);
-        }
-
-        @Override
-        public URL parseValue(Object input) throws CoercingParseValueException 
{
-            try {
-                return new URL(String.valueOf(input));
-            } catch(Exception e) {
-                throw new CoercingParseValueException("URL parsing failed ", 
e);
-            }
-        }
-
-        @Override
-        public URL parseLiteral(Object input) throws 
CoercingParseLiteralException {
-            if(input instanceof StringValue) {
-                return parseValue(((StringValue)input).getValue());
-
-            }
-            throw new CoercingSerializeException("Unexpected parse input " + 
input);
-        }
-
+    private BundleContext bundleContext;
+    
+    @Activate
+    public void activate(BundleContext ctx) {
+        bundleContext = ctx;
     }
 
-    static class UppercaseStringCoercing implements Coercing<String, String> {
-        static final String NAME = "UppercaseString";
+    @SuppressWarnings("unchecked")
+    private GraphQLScalarType getScalar(String name) {
 
-        @Override
-        public String serialize(Object dataFetcherResult) throws 
CoercingSerializeException {
-            return String.valueOf(dataFetcherResult).toUpperCase();
+        // Ignore standard scalars
+        if(ScalarInfo.STANDARD_SCALAR_DEFINITIONS.containsKey(name)) {
+            return null;
         }
 
-        @Override
-        public String parseValue(Object input) throws 
CoercingParseValueException {
-            throw new CoercingParseValueException("Parsing not implemented");
+        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);
         }
-
-        @Override
-        public String parseLiteral(Object input) throws 
CoercingParseLiteralException {
-            throw new CoercingParseValueException("Parsing not implemented");
+        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]);
         }
 
-    }
-
-    private GraphQLScalarType getScalar(String name) {
-        if(URLCoercing.NAME.equals(name)) {
-            return GraphQLScalarType.newScalar()
-                .name(URLCoercing.NAME)
-                .description("TODO should be an OSGi service - hardcoded for 
initial tests")
-                .coercing(new URLCoercing())
-                .build()
-            ;
+        if(converter == null) {
+            throw new SlingGraphQLException("SlingScalarConverter with name '" 
+ name + "' not found");
         }
-        if(UppercaseStringCoercing.NAME.equals(name)) {
-            return GraphQLScalarType.newScalar()
-                .name(UppercaseStringCoercing.NAME)
-                .description("TODO should be an OSGi service - hardcoded for 
initial tests")
-                .coercing(new UppercaseStringCoercing())
-                .build()
-            ;
-        }
-        return null;
+
+        return GraphQLScalarType.newScalar()
+            .name(name)
+            .description(converter.toString())
+            .coercing(new SlingCoercingWrapper(converter))
+            .build();
     }
 
-    public Iterable<GraphQLScalarType> 
getScalars(Map<String,ScalarTypeDefinition> schemaScalars) {
+    public Iterable<GraphQLScalarType> 
getCustomScalars(Map<String,ScalarTypeDefinition> schemaScalars) {
         // Using just the names for now, not sure why we'd need the 
ScalarTypeDefinitions
         return schemaScalars.keySet().stream()
             .map(this::getScalar)
diff --git 
a/src/test/java/org/apache/sling/graphql/core/engine/CustomScalarsTest.java 
b/src/test/java/org/apache/sling/graphql/core/engine/CustomScalarsTest.java
index f3a17fc..c633da4 100644
--- a/src/test/java/org/apache/sling/graphql/core/engine/CustomScalarsTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/engine/CustomScalarsTest.java
@@ -22,12 +22,13 @@ import static 
com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
 
 import static org.junit.Assert.assertThat;
 
-import javax.script.ScriptException;
-
 import static org.hamcrest.Matchers.equalTo;
 
+import org.apache.sling.graphql.api.ScalarConversionException;
 import org.apache.sling.graphql.core.mocks.AddressDataFetcher;
 import org.apache.sling.graphql.core.mocks.TestUtil;
+import org.apache.sling.graphql.core.mocks.URLScalarConverter;
+import org.apache.sling.graphql.core.mocks.UppercaseScalarConverter;
 import org.junit.Test;
 
 public class CustomScalarsTest extends ResourceQueryTestBase {
@@ -35,8 +36,10 @@ public class CustomScalarsTest extends ResourceQueryTestBase 
{
         return "scalars-schema";
     }
 
-    protected void setupDataFetchers() {
+    protected void setupAdditionalServices() {
         TestUtil.registerSlingDataFetcher(context.bundleContext(), 
"scalars/address", new AddressDataFetcher());
+        TestUtil.registerSlingScalarConverter(context.bundleContext(), "URL", 
new URLScalarConverter());
+        TestUtil.registerSlingScalarConverter(context.bundleContext(), 
"UppercaseString", new UppercaseScalarConverter());
     }
 
     @Test
@@ -45,14 +48,18 @@ public class CustomScalarsTest extends 
ResourceQueryTestBase {
         final String query = String.format("{ address (url: \"%s\") { url 
hostname } }", url);
         final String json = queryJSON(query);
         assertThat(json, hasJsonPath("$.data.address.hostname", 
equalTo("WWW.PERDU.COM")));
-        assertThat(json, hasJsonPath("$.data.address.url", 
equalTo("URLCoercing says:" + url)));
+        assertThat(json, hasJsonPath("$.data.address.url", 
equalTo("URLScalarConverter says:" + url)));
     }
 
-    @Test(expected = ScriptException.class)
+    @Test
     public void urlSyntaxError() throws Exception {
         final String url = "This is not an URL!";
         final String query = String.format("{ address (url: \"%s\") { url 
hostname } }", url);
-        queryJSON(query);
+        try {
+            queryJSON(query);
+        } catch(Exception e) {
+            TestUtil.assertNestedException(e, ScalarConversionException.class, 
URLScalarConverter.class.getSimpleName() + ":Invalid URL:" + url);
+        }
     }
 
 }
\ No newline at end of file
diff --git 
a/src/test/java/org/apache/sling/graphql/core/engine/GraphQLResourceQueryTest.java
 
b/src/test/java/org/apache/sling/graphql/core/engine/GraphQLResourceQueryTest.java
index 6cc707c..a11ad64 100644
--- 
a/src/test/java/org/apache/sling/graphql/core/engine/GraphQLResourceQueryTest.java
+++ 
b/src/test/java/org/apache/sling/graphql/core/engine/GraphQLResourceQueryTest.java
@@ -40,7 +40,7 @@ import org.osgi.framework.ServiceRegistration;
 
 public class GraphQLResourceQueryTest extends ResourceQueryTestBase {
     
-    protected void setupDataFetchers() {
+    protected void setupAdditionalServices() {
         final Dictionary<String, Object> staticData = new Hashtable<>();
         staticData.put("test", true);
 
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 6df9d72..5c6504d 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
@@ -44,7 +44,7 @@ import graphql.ExecutionResult;
 public abstract class ResourceQueryTestBase {
     protected SchemaProvider schemaProvider;
     protected SlingDataFetcherSelector dataFetchersSelector;
-    protected SlingScalarsProvider scalarsProvider = new 
SlingScalarsProvider();
+    protected SlingScalarsProvider scalarsProvider;
     protected Resource resource;
 
     @Rule
@@ -59,7 +59,7 @@ public abstract class ResourceQueryTestBase {
         Mockito.when(resource.getPath()).thenReturn(path);
         Mockito.when(resource.getResourceType()).thenReturn(resourceType);
 
-        setupDataFetchers();
+        setupAdditionalServices();
 
         // Our MockScriptServlet to simulates a script for unit tests, for the
         // integration tests we use a real script
@@ -71,6 +71,8 @@ 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);
     }
 
     protected String queryJSON(String stmt) throws Exception {
@@ -84,7 +86,7 @@ public abstract class ResourceQueryTestBase {
         return new JsonSerializer().toJSON(result);
     }
 
-    protected void setupDataFetchers() {
+    protected void setupAdditionalServices() {
     }
 
     protected String getTestSchemaName() {
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 7679108..2f85566 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
@@ -25,6 +25,7 @@ import java.util.Dictionary;
 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.ServiceRegistration;
 
@@ -35,6 +36,12 @@ public class TestUtil {
         return bc.registerService(SlingDataFetcher.class, f, props);
     }
 
+    public static ServiceRegistration<?> 
registerSlingScalarConverter(BundleContext bc, String name, 
SlingScalarConverter<?,?> c) {
+        final Dictionary<String, Object> props = new Hashtable<>();
+        props.put(SlingScalarConverter.NAME_SERVICE_PROPERTY, name);
+        return bc.registerService(SlingScalarConverter.class, c, props);
+    }
+
     public static void assertNestedException(Throwable t, Class<?> clazz, 
String messageContains) {
         boolean found = false;
         while(t != null) {
diff --git 
a/src/test/java/org/apache/sling/graphql/core/mocks/URLScalarConverter.java 
b/src/test/java/org/apache/sling/graphql/core/mocks/URLScalarConverter.java
new file mode 100644
index 0000000..7a12c52
--- /dev/null
+++ b/src/test/java/org/apache/sling/graphql/core/mocks/URLScalarConverter.java
@@ -0,0 +1,44 @@
+/*
+ * 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.mocks;
+
+import java.net.URL;
+
+import org.apache.sling.graphql.api.ScalarConversionException;
+import org.apache.sling.graphql.api.SlingScalarConverter;
+import org.jetbrains.annotations.Nullable;
+
+public class URLScalarConverter implements SlingScalarConverter<URL, String> {
+
+    @Override
+    public @Nullable URL parseValue(@Nullable String input) throws 
ScalarConversionException {
+        try {
+            return new URL(input);
+        } catch(Exception e) {
+            throw new ScalarConversionException(getClass().getSimpleName() + 
":Invalid URL:" + input, e);
+        }
+    }
+
+    @Override
+    public @Nullable String serialize(@Nullable URL value) throws 
ScalarConversionException {
+        final String testPrefix = getClass().getSimpleName() + " says:";
+        return testPrefix + (value == null ? null : value.toExternalForm());
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/graphql/api/package-info.java 
b/src/test/java/org/apache/sling/graphql/core/mocks/UppercaseScalarConverter.java
similarity index 54%
copy from src/main/java/org/apache/sling/graphql/api/package-info.java
copy to 
src/test/java/org/apache/sling/graphql/core/mocks/UppercaseScalarConverter.java
index c4934ad..dd0c2fd 100644
--- a/src/main/java/org/apache/sling/graphql/api/package-info.java
+++ 
b/src/test/java/org/apache/sling/graphql/core/mocks/UppercaseScalarConverter.java
@@ -17,10 +17,21 @@
  * under the License.
  */
 
- /**
-  * This package contains APIs which are independent of
-  * a specific implementation of the underlying graphQL engine.
-  */
-@Version("3.0.1")
-package org.apache.sling.graphql.api;
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+package org.apache.sling.graphql.core.mocks;
+
+import org.apache.sling.graphql.api.ScalarConversionException;
+import org.apache.sling.graphql.api.SlingScalarConverter;
+import org.jetbrains.annotations.Nullable;
+
+public class UppercaseScalarConverter implements SlingScalarConverter<String, 
String> {
+
+    @Override
+    public @Nullable String parseValue(@Nullable String input) throws 
ScalarConversionException {
+        throw new ScalarConversionException("This converter is meant for 
output only");
+    }
+
+    @Override
+    public @Nullable String serialize(@Nullable String value) throws 
ScalarConversionException {
+        return value == null ? null : value.toUpperCase();
+    }
+}
\ No newline at end of file

Reply via email to