Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scalar type for UUID #18

Merged
merged 1 commit into from
Feb 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ And example query might look like:
}

```
## ID Scalars

* `UUID`
* A universally unique identifier scalar that accepts uuid values like `2423f0a0-3b81-4115-a189-18df8b35e8fc` and produces
`java.util.UUID` type at runtime

## Object / JSON Scalars

Expand Down
9 changes: 9 additions & 0 deletions src/main/java/graphql/scalars/ExtendedScalars.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import graphql.scalars.datetime.DateScalar;
import graphql.scalars.datetime.DateTimeScalar;
import graphql.scalars.datetime.TimeScalar;
import graphql.scalars.id.UUIDScalar;
import graphql.scalars.numeric.NegativeFloatScalar;
import graphql.scalars.numeric.NegativeIntScalar;
import graphql.scalars.numeric.NonNegativeFloatScalar;
Expand All @@ -20,6 +21,8 @@
import graphql.scalars.locale.LocaleScalar;
import graphql.schema.GraphQLScalarType;

import java.util.UUID;

/**
* This is the API entry point for all the extended scalars
*/
Expand Down Expand Up @@ -118,6 +121,12 @@ public class ExtendedScalars {
*/
public static GraphQLScalarType Locale = new LocaleScalar();

/**
* A UUID scalar that accepts a universally unique identifier and produces {@link
* java.util.UUID} objects at runtime.
*/
public static GraphQLScalarType UUID = new UUIDScalar();

/**
* An `Int` scalar that MUST be greater than zero
*
Expand Down
87 changes: 87 additions & 0 deletions src/main/java/graphql/scalars/id/UUIDScalar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package graphql.scalars.id;

import graphql.Internal;
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 java.time.DateTimeException;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

import static graphql.scalars.util.Kit.typeName;

/**
* Access this via {@link graphql.scalars.ExtendedScalars#UUID}
*/
@Internal
public class UUIDScalar extends GraphQLScalarType {

public UUIDScalar() {
super("UUID", "A universally unique identifier compliant UUID Scalar", new Coercing<UUID, String>() {
@Override
public String serialize(Object input) throws CoercingSerializeException {
if (input instanceof String) {
try {
return (UUID.fromString((String)input)).toString();
} catch ( IllegalArgumentException ex) {
throw new CoercingSerializeException(
"Expected a UUID value that can be converted : '" + ex.getMessage() + "'."
);
}
}
else if(input instanceof UUID) {
return input.toString();
}
else {
throw new CoercingSerializeException(
"Expected something we can convert to 'java.util.UUID' but was '" + typeName(input) + "'."
);
}
}

@Override
public UUID parseValue(Object input) throws CoercingParseValueException {
if(input instanceof String) {
try {
return UUID.fromString((String) input);
} catch (IllegalArgumentException ex) {
throw new CoercingParseValueException(
"Expected a 'String' of UUID type but was '" + typeName(input) + "'."
);
}
}
else if(input instanceof UUID) {
return (UUID) input;
}
else {
throw new CoercingParseValueException(
"Expected a 'String' or 'UUID' type but was '" + typeName(input) + "'."
);
}
}

@Override
public UUID parseLiteral(Object input) throws CoercingParseLiteralException {
if (!(input instanceof StringValue)) {
throw new CoercingParseLiteralException(
"Expected a 'java.util.UUID' AST type object but was '" + typeName(input) + "'."
);
}
try {
return UUID.fromString(((StringValue) input).getValue());
} catch (IllegalArgumentException ex) {
throw new CoercingParseLiteralException(
"Expected something that we can convert to a UUID but was invalid"
);
}

}

});
}

}
90 changes: 90 additions & 0 deletions src/test/groovy/graphql/scalars/id/UUIDScalarTest.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package graphql.scalars.id

import graphql.language.StringValue
import graphql.schema.CoercingParseLiteralException
import graphql.schema.CoercingParseValueException
import graphql.schema.CoercingSerializeException
import spock.lang.Specification
import spock.lang.Unroll

import static graphql.scalars.util.TestKit.*

class UUIDScalarTest extends Specification {

def coercing = new UUIDScalar().getCoercing()

@Unroll
def "UUID parseValue"() {

when:
def result = coercing.parseValue(input)
then:
result == expectedValue
where:
input | expectedValue
"43f20307-603c-4ad1-83c6-6010d224fabf" | mkUUIDValue("43f20307-603c-4ad1-83c6-6010d224fabf")
"787dbc2b-3ddb-4098-ad1d-63d026bac111" | mkUUIDValue("787dbc2b-3ddb-4098-ad1d-63d026bac111")
}

@Unroll
def "UUID parseValue bad inputs"() {

when:
coercing.parseValue(input)
then:
thrown(expectedValue)
where:
input | expectedValue
"a-string-that-is-not-uuid" | CoercingParseValueException
100 | CoercingParseValueException
"1985-04-12" | CoercingParseValueException
}

def "UUID AST literal"() {

when:
def result = coercing.parseLiteral(input)
then:
result == expectedValue
where:
input | expectedValue
new StringValue("6972117d-3963-4214-ab2c-fa973d7e996b") | mkUUIDValue("6972117d-3963-4214-ab2c-fa973d7e996b")
}

def "UUID AST literal bad inputs"() {

when:
coercing.parseLiteral(input)
then:
thrown(expectedValue)
where:
input | expectedValue
new StringValue("a-string-that-us-not-uuid") | CoercingParseLiteralException
}

def "UUID serialization"() {

when:
def result = coercing.serialize(input)
then:
result == expectedValue
where:
input | expectedValue
"42287d47-c5bd-45e4-b470-53e426d3d503" | "42287d47-c5bd-45e4-b470-53e426d3d503"
"423df0f3-cf05-4eb5-b708-ae2f4b4a052d" | "423df0f3-cf05-4eb5-b708-ae2f4b4a052d"
mkUUIDValue("6a90b1e6-20f3-43e5-a7ba-34db8010c071") | "6a90b1e6-20f3-43e5-a7ba-34db8010c071"
}

def "UUID serialization bad inputs"() {

when:
coercing.serialize(input)
then:
thrown(expectedValue)
where:
input | expectedValue
"1985-04-12" | CoercingSerializeException
100 | CoercingSerializeException
}

}
4 changes: 4 additions & 0 deletions src/test/groovy/graphql/scalars/util/TestKit.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,8 @@ class TestKit {
return new FloatValue(new BigDecimal(d))
}

static UUID mkUUIDValue(String s) {
return UUID.fromString(s)
}

}