Skip to content

Commit 335c46b

Browse files
committed
Copy URL scalar
1 parent 9146d38 commit 335c46b

File tree

3 files changed

+235
-0
lines changed

3 files changed

+235
-0
lines changed

src/main/java/graphql/scalars/ExtendedScalars.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import graphql.scalars.object.JsonScalar;
2929
import graphql.scalars.object.ObjectScalar;
3030
import graphql.scalars.regex.RegexScalar;
31+
import graphql.scalars.uri.UriScalar;
3132
import graphql.scalars.url.UrlScalar;
3233
import graphql.schema.GraphQLScalarType;
3334

@@ -209,6 +210,11 @@ public class ExtendedScalars {
209210
*/
210211
public static final GraphQLScalarType Json = JsonScalar.INSTANCE;
211212

213+
/**
214+
* A URI scalar that accepts URI strings and produces {@link java.net.URI} objects at runtime
215+
*/
216+
public static final GraphQLScalarType Uri = UriScalar.INSTANCE;
217+
212218
/**
213219
* A URL scalar that accepts URL strings and produces {@link java.net.URL} objects at runtime
214220
*/
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package graphql.scalars.uri;
2+
3+
import graphql.GraphQLContext;
4+
import graphql.Internal;
5+
import graphql.execution.CoercedVariables;
6+
import graphql.language.StringValue;
7+
import graphql.language.Value;
8+
import graphql.schema.Coercing;
9+
import graphql.schema.CoercingParseLiteralException;
10+
import graphql.schema.CoercingParseValueException;
11+
import graphql.schema.CoercingSerializeException;
12+
import graphql.schema.GraphQLScalarType;
13+
14+
import java.io.File;
15+
import java.net.MalformedURLException;
16+
import java.net.URI;
17+
import java.net.URISyntaxException;
18+
import java.net.URL;
19+
import java.util.Locale;
20+
import java.util.Optional;
21+
import java.util.function.Function;
22+
23+
import static graphql.scalars.util.Kit.typeName;
24+
25+
@Internal
26+
public final class UriScalar {
27+
28+
private UriScalar() {
29+
}
30+
31+
public static final GraphQLScalarType INSTANCE;
32+
33+
static {
34+
Coercing<URL, URL> coercing = new Coercing<>() {
35+
@Override
36+
public URL serialize(Object input, GraphQLContext graphQLContext, Locale locale) throws CoercingSerializeException {
37+
Optional<URL> url;
38+
if (input instanceof String) {
39+
url = Optional.of(parseURL(input.toString(), CoercingSerializeException::new));
40+
} else {
41+
url = toURL(input);
42+
}
43+
if (url.isPresent()) {
44+
return url.get();
45+
}
46+
throw new CoercingSerializeException(
47+
"Expected a 'URL' like object but was '" + typeName(input) + "'."
48+
);
49+
}
50+
51+
@Override
52+
public URL parseValue(Object input, GraphQLContext graphQLContext, Locale locale) throws CoercingParseValueException {
53+
String urlStr;
54+
if (input instanceof String) {
55+
urlStr = String.valueOf(input);
56+
} else {
57+
Optional<URL> url = toURL(input);
58+
if (url.isEmpty()) {
59+
throw new CoercingParseValueException(
60+
"Expected a 'URL' like object but was '" + typeName(input) + "'."
61+
);
62+
}
63+
return url.get();
64+
}
65+
return parseURL(urlStr, CoercingParseValueException::new);
66+
}
67+
68+
@Override
69+
public URL parseLiteral(Value<?> input, CoercedVariables variables, GraphQLContext graphQLContext, Locale locale) throws CoercingParseLiteralException {
70+
if (!(input instanceof StringValue)) {
71+
throw new CoercingParseLiteralException(
72+
"Expected AST type 'StringValue' but was '" + typeName(input) + "'."
73+
);
74+
}
75+
return parseURL(((StringValue) input).getValue(), CoercingParseLiteralException::new);
76+
}
77+
78+
@Override
79+
public Value<?> valueToLiteral(Object input, GraphQLContext graphQLContext, Locale locale) {
80+
URL url = serialize(input, graphQLContext, locale);
81+
return StringValue.newStringValue(url.toExternalForm()).build();
82+
}
83+
84+
85+
private URL parseURL(String input, Function<String, RuntimeException> exceptionMaker) {
86+
try {
87+
return new URI(input).toURL();
88+
} catch (URISyntaxException | IllegalArgumentException | MalformedURLException e) {
89+
throw exceptionMaker.apply("Invalid URL value : '" + input + "'.");
90+
}
91+
}
92+
};
93+
94+
INSTANCE = GraphQLScalarType.newScalar()
95+
.name("Url")
96+
.description("A Url scalar")
97+
.coercing(coercing)
98+
.build();
99+
}
100+
101+
private static Optional<URL> toURL(Object input) {
102+
if (input instanceof URL) {
103+
return Optional.of((URL) input);
104+
} else if (input instanceof URI) {
105+
try {
106+
return Optional.of(((URI) input).toURL());
107+
} catch (MalformedURLException ignored) {
108+
}
109+
} else if (input instanceof File) {
110+
try {
111+
return Optional.of(((File) input).toURI().toURL());
112+
} catch (MalformedURLException ignored) {
113+
}
114+
}
115+
return Optional.empty();
116+
}
117+
118+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package graphql.scalars.uri
2+
3+
4+
import graphql.language.BooleanValue
5+
import graphql.language.StringValue
6+
import graphql.scalars.ExtendedScalars
7+
import graphql.scalars.util.AbstractScalarTest
8+
import graphql.schema.CoercingParseLiteralException
9+
import graphql.schema.CoercingParseValueException
10+
import graphql.schema.CoercingSerializeException
11+
import spock.lang.Unroll
12+
13+
import static graphql.scalars.util.TestKit.mkStringValue
14+
15+
class UriScalarTest extends AbstractScalarTest {
16+
17+
def coercing = ExtendedScalars.Uri.getCoercing()
18+
19+
@Unroll
20+
def "test serialize"() {
21+
22+
when:
23+
def result = coercing.serialize(input, graphQLContext, locale)
24+
then:
25+
result == expectedResult
26+
where:
27+
input | expectedResult
28+
new URL("http://www.graphql-java.com/") | new URL("http://www.graphql-java.com/")
29+
new URI("http://www.graphql-java.com/") | new URL("http://www.graphql-java.com/")
30+
new File("/this/that") | new URL("file:/this/that")
31+
"http://www.graphql-java.com/" | new URL("http://www.graphql-java.com/")
32+
}
33+
34+
@Unroll
35+
def "test valueToLiteral"() {
36+
37+
when:
38+
def result = coercing.valueToLiteral(input, graphQLContext, locale)
39+
then:
40+
result.isEqualTo(expectedResult)
41+
where:
42+
input | expectedResult
43+
new URL("http://www.graphql-java.com/") | mkStringValue("http://www.graphql-java.com/")
44+
new URI("http://www.graphql-java.com/") | mkStringValue("http://www.graphql-java.com/")
45+
new File("/this/that") | mkStringValue("file:/this/that")
46+
"http://www.graphql-java.com/" | mkStringValue("http://www.graphql-java.com/")
47+
}
48+
49+
@Unroll
50+
def "test serialize bad inputs"() {
51+
when:
52+
coercing.serialize(input, graphQLContext, locale)
53+
then:
54+
thrown(exceptionClas)
55+
where:
56+
input || exceptionClas
57+
666 || CoercingSerializeException
58+
"not/a/url" || CoercingSerializeException
59+
}
60+
61+
@Unroll
62+
def "test parseValue"() {
63+
when:
64+
def result = coercing.parseValue(input, graphQLContext, locale)
65+
then:
66+
result == expectedResult
67+
where:
68+
input | expectedResult
69+
new URL("http://www.graphql-java.com/") | new URL("http://www.graphql-java.com/")
70+
new URI("http://www.graphql-java.com/") | new URL("http://www.graphql-java.com/")
71+
new File("/this/that") | new URL("file:/this/that")
72+
"http://www.graphql-java.com/" | new URL("http://www.graphql-java.com/")
73+
}
74+
75+
@Unroll
76+
def "test parseValue bad inputs"() {
77+
when:
78+
coercing.parseValue(input, graphQLContext, locale)
79+
then:
80+
thrown(exceptionClas)
81+
where:
82+
input || exceptionClas
83+
666 || CoercingParseValueException
84+
"not/a/url" || CoercingParseValueException
85+
}
86+
87+
@Unroll
88+
def "test parseLiteral"() {
89+
when:
90+
def result = coercing.parseLiteral(input, variables, graphQLContext, locale)
91+
then:
92+
result == expectedResult
93+
where:
94+
input | expectedResult
95+
new StringValue("http://www.graphql-java.com/") | new URL("http://www.graphql-java.com/")
96+
}
97+
98+
@Unroll
99+
def "test parseLiteral bad inputs"() {
100+
when:
101+
coercing.parseLiteral(input, variables, graphQLContext, locale)
102+
then:
103+
thrown(exceptionClas)
104+
where:
105+
input | exceptionClas
106+
new BooleanValue(true) | CoercingParseLiteralException
107+
new StringValue("1:not/a/uri") | CoercingParseLiteralException
108+
}
109+
110+
111+
}

0 commit comments

Comments
 (0)