diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8655013
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+Thumbs.db
+.DS_Store
+.gradle/
+build/
+.idea/
+*.iml
+*.ipr
+*.iws
+.project
+.settings
+.classpath
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..54ef8ff
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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.
+ */
+
+/**
+ * @author Alexey Zhokhov
+ */
+buildscript {
+ repositories {
+ mavenLocal()
+ jcenter()
+ maven { url 'https://plugins.gradle.org/m2/' }
+ maven { url 'http://repo.spring.io/plugins-release' }
+ }
+ dependencies {
+ classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
+ }
+}
+
+subprojects {
+ apply plugin: 'idea'
+ apply plugin: 'java'
+ apply plugin: 'maven'
+ apply plugin: 'maven-publish'
+ apply plugin: 'com.jfrog.bintray'
+ apply plugin: 'groovy'
+
+ version = projectVersion
+ group = 'com.graphql-java'
+
+ repositories {
+ mavenLocal()
+ jcenter()
+ maven { url 'https://dl.bintray.com/andimarek/graphql-java/' }
+ }
+
+ dependencies {
+ testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
+ testCompile 'org.codehaus.groovy:groovy-all:2.4.12'
+ }
+
+ idea {
+ module {
+ downloadJavadoc = true
+ downloadSources = true
+ }
+ }
+
+ compileJava {
+ sourceCompatibility = 1.8
+ targetCompatibility = 1.8
+ }
+
+ if (it.name != 'graphql-datetime-sample-app') {
+ jar {
+ from 'LICENSE'
+ }
+
+ task sourcesJar(type: Jar) {
+ dependsOn classes
+ classifier 'sources'
+ from sourceSets.main.allSource
+ }
+
+ task javadocJar(type: Jar, dependsOn: javadoc) {
+ classifier = 'javadoc'
+ from javadoc.destinationDir
+ }
+
+ artifacts {
+ archives sourcesJar
+ archives javadocJar
+ }
+
+ publishing {
+ publications {
+ mainProjectPublication(MavenPublication) {
+ version version
+ from components.java
+
+ artifact sourcesJar {
+ classifier 'sources'
+ }
+ artifact javadocJar {
+ classifier 'javadoc'
+ }
+
+ pom.withXml {
+ asNode().children().last() + {
+ resolveStrategy = Closure.DELEGATE_FIRST
+ name projectName
+ description projectDescription
+ url projectGitRepoUrl
+ scm {
+ url projectGitRepoUrl
+ connection projectGitRepoUrl
+ developerConnection projectGitRepoUrl
+ }
+ licenses {
+ license {
+ name projectLicense
+ url projectLicenseUrl
+ distribution 'repo'
+ }
+ }
+ developers {
+ developer {
+ id 'donbeave'
+ name 'Alexey Zhokhov'
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ bintray {
+ user = System.env.BINTRAY_USER ?: project.findProperty('BINTRAY_USER') ?: ''
+ key = System.env.BINTRAY_KEY ?: project.findProperty('BINTRAY_KEY') ?: ''
+ publications = ['mainProjectPublication']
+ publish = true
+ pkg {
+ repo = 'maven'
+ name = projectName
+ desc = projectDescription
+ licenses = [projectLicense]
+ vcsUrl = projectGitRepoUrl
+ version {
+ name = project.version
+ }
+ }
+ }
+ */
+ }
+}
+
+task wrapper(type: Wrapper) {
+ gradleVersion = gradleWrapperVersion
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..b6d7072
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,19 @@
+gradleWrapperVersion=4.0
+graphqlJavaVersion=2017-07-06T09-33-35
+graphqlSpringBootVersion=3.5.0
+graphqlJavaToolsVersion=3.1.3
+springBootVersion=1.5.4.RELEASE
+#
+projectVersion=1.0.0-SNAPSHOT
+projectName=graphql-java-datetime
+projectDescription=The set of RFC 3339 compliant date/time scalar types for GraphQL Java implementation
+projectGitRepoUrl=https://github.com/donbeave/graphql-java-datetime
+projectLicense=Apache-2.0
+projectLicenseUrl=https://github.com/donbeave/graphql-java-datetime/blob/master/LICENSE
+#
+org.gradle.caching=true
+org.gradle.daemon=true
+org.gradle.parallel=true
+org.gradle.parallel.intra=true
+org.gradle.configureondemand=true
+org.gradle.jvmargs=-Dfile.encoding=UTF-8
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..9cdc604
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..9c153ac
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun Jul 09 18:32:24 HKT 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..4453cce
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save ( ) {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/graphql-datetime-autoconfigure/build.gradle b/graphql-datetime-autoconfigure/build.gradle
new file mode 100644
index 0000000..cf55b86
--- /dev/null
+++ b/graphql-datetime-autoconfigure/build.gradle
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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.
+ */
+
+/**
+ * @author Alexey Zhokhov
+ */
+dependencies {
+ compile "com.graphql-java:graphql-spring-boot-autoconfigure:$graphqlSpringBootVersion"
+ compile "com.graphql-java:graphql-spring-boot-starter:$graphqlSpringBootVersion"
+ compile "com.graphql-java:graphql-java-tools:$graphqlJavaToolsVersion"
+ compile(project(':graphql-java-datetime'))
+
+ testCompile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
+ testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
+ testCompile "org.springframework.boot:spring-boot-test:$springBootVersion"
+}
diff --git a/graphql-datetime-autoconfigure/src/main/java/graphql/datetime/boot/GraphQLDateTimeAutoConfiguration.java b/graphql-datetime-autoconfigure/src/main/java/graphql/datetime/boot/GraphQLDateTimeAutoConfiguration.java
new file mode 100644
index 0000000..7b873ba
--- /dev/null
+++ b/graphql-datetime-autoconfigure/src/main/java/graphql/datetime/boot/GraphQLDateTimeAutoConfiguration.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime.boot;
+
+import com.oembedler.moon.graphql.boot.GraphQLJavaToolsAutoConfiguration;
+import graphql.datetime.GraphQLDate;
+import graphql.datetime.GraphQLLocalDate;
+import graphql.datetime.GraphQLLocalDateTime;
+import graphql.datetime.GraphQLLocalTime;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Alexey Zhokhov
+ */
+@Configuration
+@AutoConfigureBefore({GraphQLJavaToolsAutoConfiguration.class})
+public class GraphQLDateTimeAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public GraphQLDate graphQLDate() {
+ return new GraphQLDate();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public GraphQLLocalDate graphQLLocalDate() {
+ return new GraphQLLocalDate();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public GraphQLLocalDateTime graphQLLocalDateTime() {
+ return new GraphQLLocalDateTime();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public GraphQLLocalTime graphQLLocalTime() {
+ return new GraphQLLocalTime();
+ }
+
+}
diff --git a/graphql-datetime-autoconfigure/src/main/resources/META-INF/spring.factories b/graphql-datetime-autoconfigure/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..faeeec9
--- /dev/null
+++ b/graphql-datetime-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+graphql.datetime.boot.GraphQLDateTimeAutoConfiguration
diff --git a/graphql-datetime-autoconfigure/src/test/groovy/graphql/datetime/boot/test/GraphQLDateTimeAutoConfigurationSpec.groovy b/graphql-datetime-autoconfigure/src/test/groovy/graphql/datetime/boot/test/GraphQLDateTimeAutoConfigurationSpec.groovy
new file mode 100644
index 0000000..98b5bfe
--- /dev/null
+++ b/graphql-datetime-autoconfigure/src/test/groovy/graphql/datetime/boot/test/GraphQLDateTimeAutoConfigurationSpec.groovy
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime.boot.test
+
+import graphql.GraphQL
+import graphql.datetime.GraphQLDate
+import graphql.datetime.GraphQLLocalDate
+import graphql.datetime.GraphQLLocalDateTime
+import graphql.datetime.GraphQLLocalTime
+import graphql.schema.GraphQLSchema
+import org.springframework.context.support.AbstractApplicationContext
+import spock.lang.Specification
+
+/**
+ * @author Alexey Zhokhov
+ */
+class GraphQLDateTimeAutoConfigurationSpec extends Specification {
+
+ AbstractApplicationContext context
+
+ def setup() {
+ context = ContextHelper.load()
+ }
+
+ def cleanup() {
+ if (context) {
+ context.close()
+ context = null
+ }
+ }
+
+ def "test"() {
+ given:
+ String query = """
+{
+ echo {
+ date
+ localDate
+ localDateTime
+ localTime
+ }
+}
+"""
+
+ when:
+ this.context = ContextHelper.load()
+
+ then:
+ context.getBean(GraphQLSchema.class)
+ context.getBean(GraphQLDate.class)
+ context.getBean(GraphQLLocalDate.class)
+ context.getBean(GraphQLLocalDateTime.class)
+ context.getBean(GraphQLLocalTime.class)
+
+ when:
+ GraphQL graphQL = GraphQL.newGraphQL(context.getBean(GraphQLSchema.class)).build()
+ Map result = graphQL.execute(query).getData()
+
+ then:
+ result == [
+ echo: [
+ date : '2017-07-10T06:12:46.754Z',
+ localDate : '2017-01-01',
+ localDateTime: '2017-01-01T00:00:00Z',
+ localTime : '00:00:00'
+ ]
+ ]
+ }
+
+}
diff --git a/graphql-datetime-autoconfigure/src/test/groovy/graphql/datetime/boot/test/GraphQLServletSpec.groovy b/graphql-datetime-autoconfigure/src/test/groovy/graphql/datetime/boot/test/GraphQLServletSpec.groovy
new file mode 100644
index 0000000..0c28d61
--- /dev/null
+++ b/graphql-datetime-autoconfigure/src/test/groovy/graphql/datetime/boot/test/GraphQLServletSpec.groovy
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime.boot.test
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import graphql.schema.GraphQLSchema
+import graphql.servlet.GraphQLServlet
+import graphql.servlet.SimpleGraphQLServlet
+import org.springframework.context.support.AbstractApplicationContext
+import org.springframework.mock.web.MockHttpServletRequest
+import org.springframework.mock.web.MockHttpServletResponse
+import spock.lang.Shared
+import spock.lang.Specification
+
+/**
+ * @author Alexey Zhokhov
+ */
+class GraphQLServletSpec extends Specification {
+
+ public static final String CONTENT_TYPE_JSON_UTF8 = 'application/json;charset=UTF-8'
+
+ @Shared
+ ObjectMapper mapper = new ObjectMapper()
+
+ AbstractApplicationContext context
+ MockHttpServletRequest request
+ MockHttpServletResponse response
+
+ def setup() {
+ context = ContextHelper.load()
+ request = new MockHttpServletRequest()
+ response = new MockHttpServletResponse()
+ }
+
+ def cleanup() {
+ if (context) {
+ context.close()
+ context = null
+ }
+ }
+
+ def "test"() {
+ given:
+ GraphQLSchema graphQLSchema = context.getBean(GraphQLSchema.class)
+ GraphQLServlet servlet = new SimpleGraphQLServlet(graphQLSchema)
+
+ String query = """
+{
+ echo {
+ date
+ localDate
+ localDateTime
+ localTime
+ }
+}
+"""
+
+ when:
+ servlet.executeQuery(query)
+
+ then:
+ response.getStatus() == 200
+ response.getContentType() == CONTENT_TYPE_JSON_UTF8
+ responseContent.data == [
+ echo: [
+ date : '2017-07-10T06:12:46.754Z',
+ localDate : '2017-01-01',
+ localDateTime: '2017-01-01T00:00:00Z',
+ localTime : '00:00:00'
+ ]
+ ]
+ }
+
+ private Map getResponseContent() {
+ mapper.readValue(response.getContentAsByteArray(), Map)
+ }
+
+}
diff --git a/graphql-datetime-autoconfigure/src/test/java/graphql/datetime/boot/test/ContextHelper.java b/graphql-datetime-autoconfigure/src/test/java/graphql/datetime/boot/test/ContextHelper.java
new file mode 100644
index 0000000..4ea0c10
--- /dev/null
+++ b/graphql-datetime-autoconfigure/src/test/java/graphql/datetime/boot/test/ContextHelper.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime.boot.test;
+
+import com.oembedler.moon.graphql.boot.GraphQLJavaToolsAutoConfiguration;
+import graphql.datetime.GraphQLDate;
+import graphql.datetime.GraphQLLocalDate;
+import graphql.datetime.GraphQLLocalDateTime;
+import graphql.datetime.GraphQLLocalTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigRegistry;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.AbstractApplicationContext;
+
+/**
+ * @author Alexey Zhokhov
+ */
+public class ContextHelper {
+
+ @Configuration
+ @ComponentScan("graphql.datetime.boot")
+ static class BaseConfiguration {
+
+ // initialize date time types here
+ @Autowired(required = false) GraphQLDate graphQLDate;
+ @Autowired(required = false) GraphQLLocalDate graphQLLocalDate;
+ @Autowired(required = false) GraphQLLocalDateTime graphQLLocalDateTime;
+ @Autowired(required = false) GraphQLLocalTime graphQLLocalTime;
+
+ }
+
+ static public AbstractApplicationContext load() {
+ AbstractApplicationContext context;
+
+ try {
+ context = AnnotationConfigApplicationContext.class.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+
+ AnnotationConfigRegistry registry = (AnnotationConfigRegistry) context;
+
+ registry.register(BaseConfiguration.class);
+ registry.register(GraphQLJavaToolsAutoConfiguration.class);
+
+ context.refresh();
+
+ return context;
+ }
+
+}
diff --git a/graphql-datetime-autoconfigure/src/test/java/graphql/datetime/boot/test/resolvers/Query.java b/graphql-datetime-autoconfigure/src/test/java/graphql/datetime/boot/test/resolvers/Query.java
new file mode 100644
index 0000000..3c4526c
--- /dev/null
+++ b/graphql-datetime-autoconfigure/src/test/java/graphql/datetime/boot/test/resolvers/Query.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime.boot.test.resolvers;
+
+import com.coxautodev.graphql.tools.GraphQLRootResolver;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Alexey Zhokhov
+ */
+@Component
+public class Query implements GraphQLRootResolver {
+
+ public ResponseType echo() {
+ return new ResponseType();
+ }
+
+}
\ No newline at end of file
diff --git a/graphql-datetime-autoconfigure/src/test/java/graphql/datetime/boot/test/resolvers/ResponseType.java b/graphql-datetime-autoconfigure/src/test/java/graphql/datetime/boot/test/resolvers/ResponseType.java
new file mode 100644
index 0000000..17427b7
--- /dev/null
+++ b/graphql-datetime-autoconfigure/src/test/java/graphql/datetime/boot/test/resolvers/ResponseType.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime.boot.test.resolvers;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Date;
+
+/**
+ * @author Alexey Zhokhov
+ */
+public class ResponseType {
+
+ private Date date;
+ private LocalDate localDate;
+ private LocalDateTime localDateTime;
+ private LocalTime localTime;
+
+ public ResponseType() {
+ date = new Date(1499667166754L);
+ localDate = LocalDate.of(2017, 1, 1);
+ localTime = LocalTime.MIDNIGHT;
+ localDateTime = LocalDateTime.of(localDate, localTime);
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public LocalDate getLocalDate() {
+ return localDate;
+ }
+
+ public LocalDateTime getLocalDateTime() {
+ return localDateTime;
+ }
+
+ public LocalTime getLocalTime() {
+ return localTime;
+ }
+
+}
diff --git a/graphql-datetime-autoconfigure/src/test/resources/graphqlDateTime.graphqls b/graphql-datetime-autoconfigure/src/test/resources/graphqlDateTime.graphqls
new file mode 100644
index 0000000..ce8cb43
--- /dev/null
+++ b/graphql-datetime-autoconfigure/src/test/resources/graphqlDateTime.graphqls
@@ -0,0 +1,31 @@
+#
+# Copyright 2017 Alexey Zhokhov
+#
+# Licensed 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.
+#
+
+scalar Date
+scalar LocalDate
+scalar LocalDateTime
+scalar LocalTime
+
+type Query {
+ echo: Response
+}
+
+type Response {
+ date: Date
+ localDate: LocalDate
+ localDateTime: LocalDateTime
+ localTime: LocalTime
+}
diff --git a/graphql-datetime-autoconfigure/src/test/resources/logback-spring.xml b/graphql-datetime-autoconfigure/src/test/resources/logback-spring.xml
new file mode 100644
index 0000000..f9b15fc
--- /dev/null
+++ b/graphql-datetime-autoconfigure/src/test/resources/logback-spring.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/graphql-datetime-spring-boot-starter/build.gradle b/graphql-datetime-spring-boot-starter/build.gradle
new file mode 100644
index 0000000..ecaa391
--- /dev/null
+++ b/graphql-datetime-spring-boot-starter/build.gradle
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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.
+ */
+
+/**
+ * @author Alexey Zhokhov
+ */
+dependencies {
+ compile(project(':graphql-datetime-autoconfigure'))
+}
diff --git a/graphql-java-datetime/build.gradle b/graphql-java-datetime/build.gradle
new file mode 100644
index 0000000..34f96e4
--- /dev/null
+++ b/graphql-java-datetime/build.gradle
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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.
+ */
+
+/**
+ * @author Alexey Zhokhov
+ */
+dependencies {
+ compile "com.graphql-java:graphql-java:$graphqlJavaVersion"
+}
diff --git a/graphql-java-datetime/src/main/java/graphql/datetime/DateTimeHelper.java b/graphql-java-datetime/src/main/java/graphql/datetime/DateTimeHelper.java
new file mode 100644
index 0000000..168b1f7
--- /dev/null
+++ b/graphql-java-datetime/src/main/java/graphql/datetime/DateTimeHelper.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Alexey Zhokhov
+ */
+public class DateTimeHelper {
+
+ public static final CopyOnWriteArrayList DATE_FORMATTERS = new CopyOnWriteArrayList<>();
+
+ static {
+ DATE_FORMATTERS.add(DateTimeFormatter.ISO_INSTANT.withZone(ZoneOffset.UTC));
+ DATE_FORMATTERS.add(DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneOffset.UTC));
+ DATE_FORMATTERS.add(DateTimeFormatter.ISO_LOCAL_DATE.withZone(ZoneOffset.UTC));
+ }
+
+ // ISO_8601
+ public static String toISOString(LocalDateTime dateTime) {
+ Objects.requireNonNull(dateTime, "dateTime");
+
+ return DateTimeFormatter.ISO_INSTANT.format(ZonedDateTime.of(dateTime, ZoneOffset.UTC));
+ }
+
+ public static String toISOString(LocalDate date) {
+ Objects.requireNonNull(date, "date");
+
+ return DateTimeFormatter.ISO_LOCAL_DATE.format(date);
+ }
+
+ public static String toISOString(LocalTime time) {
+ Objects.requireNonNull(time, "time");
+
+ return DateTimeFormatter.ISO_LOCAL_TIME.format(time);
+ }
+
+ public static String toISOString(Date date) {
+ Objects.requireNonNull(date, "date");
+
+ return toISOString(toLocalDateTime(date));
+ }
+
+ public static LocalDateTime toLocalDateTime(Date date) {
+ Objects.requireNonNull(date, "date");
+
+ return date.toInstant().atZone(ZoneOffset.UTC).toLocalDateTime();
+ }
+
+ public static Date toDate(LocalDate date) {
+ Objects.requireNonNull(date, "date");
+
+ return toDate(date.atStartOfDay());
+ }
+
+ public static Date toDate(LocalDateTime dateTime) {
+ Objects.requireNonNull(dateTime, "dateTime");
+
+ return Date.from(dateTime.atZone(ZoneOffset.UTC).toInstant());
+ }
+
+ public static LocalDateTime parseDate(String date) {
+ Objects.requireNonNull(date, "date");
+
+ for (DateTimeFormatter formatter : DATE_FORMATTERS) {
+ try {
+ // equals ISO_LOCAL_DATE
+ if (formatter.equals(DATE_FORMATTERS.get(2))) {
+ LocalDate localDate = LocalDate.parse(date, formatter);
+
+ return localDate.atStartOfDay();
+ } else {
+ return LocalDateTime.parse(date, formatter);
+ }
+ } catch (java.time.format.DateTimeParseException ignored) {
+ }
+ }
+
+ return null;
+ }
+
+ public static Date createDate(int year, int month, int day) {
+ return createDate(year, month, day, 0, 0, 0, 0);
+ }
+
+ public static Date createDate(int year, int month, int day, int hours, int min, int sec) {
+ return createDate(year, month, day, hours, min, sec, 0);
+ }
+
+ public static Date createDate(int year, int month, int day, int hours, int min, int sec, int millis) {
+ long nanos = TimeUnit.MILLISECONDS.toNanos(millis);
+ LocalDateTime localDateTime = LocalDateTime.of(year, month, day, hours, min, sec, (int) nanos);
+ return DateTimeHelper.toDate(localDateTime);
+ }
+
+}
diff --git a/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLDate.java b/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLDate.java
new file mode 100644
index 0000000..ad9bac7
--- /dev/null
+++ b/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLDate.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime;
+
+import graphql.language.StringValue;
+import graphql.schema.Coercing;
+import graphql.schema.CoercingParseValueException;
+import graphql.schema.CoercingSerializeException;
+import graphql.schema.GraphQLScalarType;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+import static graphql.datetime.DateTimeHelper.parseDate;
+import static graphql.datetime.DateTimeHelper.toDate;
+import static graphql.datetime.DateTimeHelper.toISOString;
+
+/**
+ * @author Alexey Zhokhov
+ */
+public class GraphQLDate extends GraphQLScalarType {
+
+ public GraphQLDate() {
+ super("Date", "Date type", new Coercing() {
+ private Date convertImpl(Object input) {
+ if (input instanceof String) {
+ LocalDateTime localDateTime = parseDate((String) input);
+
+ if (localDateTime != null) {
+ return toDate(localDateTime);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String serialize(Object input) {
+ if (input instanceof Date) {
+ return toISOString((Date) input);
+ } else {
+ Date result = convertImpl(input);
+ if (result == null) {
+ throw new CoercingSerializeException("Invalid value '" + input + "' for Date");
+ }
+ return toISOString(result);
+ }
+ }
+
+ @Override
+ public Date parseValue(Object input) {
+ Date result = convertImpl(input);
+ if (result == null) {
+ throw new CoercingParseValueException("Invalid value '" + input + "' for Date");
+ }
+ return result;
+ }
+
+ @Override
+ public Date parseLiteral(Object input) {
+ if (!(input instanceof StringValue)) return null;
+ String value = ((StringValue) input).getValue();
+ Date result = convertImpl(value);
+ return result;
+ }
+ });
+ }
+
+}
diff --git a/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLLocalDate.java b/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLLocalDate.java
new file mode 100644
index 0000000..2628857
--- /dev/null
+++ b/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLLocalDate.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime;
+
+import graphql.language.StringValue;
+import graphql.schema.Coercing;
+import graphql.schema.CoercingParseValueException;
+import graphql.schema.CoercingSerializeException;
+import graphql.schema.GraphQLScalarType;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+import static graphql.datetime.DateTimeHelper.parseDate;
+import static graphql.datetime.DateTimeHelper.toISOString;
+
+/**
+ * @author Alexey Zhokhov
+ */
+public class GraphQLLocalDate extends GraphQLScalarType {
+
+ public GraphQLLocalDate() {
+ super("LocalDate", "Local Date type", new Coercing() {
+ private LocalDate convertImpl(Object input) {
+ if (input instanceof String) {
+ LocalDateTime localDateTime = parseDate((String) input);
+
+ if (localDateTime != null) {
+ return localDateTime.toLocalDate();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String serialize(Object input) {
+ if (input instanceof LocalDate) {
+ return toISOString((LocalDate) input);
+ } else {
+ LocalDate result = convertImpl(input);
+ if (result == null) {
+ throw new CoercingSerializeException("Invalid value '" + input + "' for LocalDate");
+ }
+ return toISOString(result);
+ }
+ }
+
+ @Override
+ public LocalDate parseValue(Object input) {
+ LocalDate result = convertImpl(input);
+ if (result == null) {
+ throw new CoercingParseValueException("Invalid value '" + input + "' for LocalDate");
+ }
+ return result;
+ }
+
+ @Override
+ public LocalDate parseLiteral(Object input) {
+ if (!(input instanceof StringValue)) return null;
+ String value = ((StringValue) input).getValue();
+ LocalDate result = convertImpl(value);
+ return result;
+ }
+ });
+ }
+
+}
diff --git a/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLLocalDateTime.java b/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLLocalDateTime.java
new file mode 100644
index 0000000..c254081
--- /dev/null
+++ b/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLLocalDateTime.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime;
+
+import graphql.language.StringValue;
+import graphql.schema.Coercing;
+import graphql.schema.CoercingParseValueException;
+import graphql.schema.CoercingSerializeException;
+import graphql.schema.GraphQLScalarType;
+
+import java.time.LocalDateTime;
+
+import static graphql.datetime.DateTimeHelper.parseDate;
+import static graphql.datetime.DateTimeHelper.toISOString;
+
+/**
+ * @author Alexey Zhokhov
+ */
+public class GraphQLLocalDateTime extends GraphQLScalarType {
+
+ public GraphQLLocalDateTime() {
+ super("LocalDateTime", "Local Date Time type", new Coercing() {
+ private LocalDateTime convertImpl(Object input) {
+ if (input instanceof String) {
+ LocalDateTime localDateTime = parseDate((String) input);
+
+ return localDateTime;
+ }
+ return null;
+ }
+
+ @Override
+ public String serialize(Object input) {
+ if (input instanceof LocalDateTime) {
+ return toISOString((LocalDateTime) input);
+ } else {
+ LocalDateTime result = convertImpl(input);
+ if (result == null) {
+ throw new CoercingSerializeException("Invalid value '" + input + "' for LocalDateTime");
+ }
+ return toISOString(result);
+ }
+ }
+
+ @Override
+ public LocalDateTime parseValue(Object input) {
+ LocalDateTime result = convertImpl(input);
+ if (result == null) {
+ throw new CoercingParseValueException("Invalid value '" + input + "' for LocalDateTime");
+ }
+ return result;
+ }
+
+ @Override
+ public LocalDateTime parseLiteral(Object input) {
+ if (!(input instanceof StringValue)) return null;
+ String value = ((StringValue) input).getValue();
+ LocalDateTime result = convertImpl(value);
+ return result;
+ }
+ });
+ }
+
+}
diff --git a/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLLocalTime.java b/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLLocalTime.java
new file mode 100644
index 0000000..042ff7e
--- /dev/null
+++ b/graphql-java-datetime/src/main/java/graphql/datetime/GraphQLLocalTime.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime;
+
+import graphql.language.StringValue;
+import graphql.schema.Coercing;
+import graphql.schema.CoercingParseValueException;
+import graphql.schema.CoercingSerializeException;
+import graphql.schema.GraphQLScalarType;
+
+import java.time.LocalTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+import static graphql.datetime.DateTimeHelper.toISOString;
+
+/**
+ * @author Alexey Zhokhov
+ */
+public class GraphQLLocalTime extends GraphQLScalarType {
+
+ public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_TIME.withZone(ZoneOffset.UTC);
+
+ public GraphQLLocalTime() {
+ super("LocalTime", "Local Time type", new Coercing() {
+ private LocalTime convertImpl(Object input) {
+ if (input instanceof String) {
+ try {
+ return LocalTime.parse((String) input, FORMATTER);
+ } catch (DateTimeParseException ignored) {
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String serialize(Object input) {
+ if (input instanceof LocalTime) {
+ return toISOString((LocalTime) input);
+ } else {
+ LocalTime result = convertImpl(input);
+ if (result == null) {
+ throw new CoercingSerializeException("Invalid value '" + input + "' for LocalTime");
+ }
+ return toISOString(result);
+ }
+ }
+
+ @Override
+ public LocalTime parseValue(Object input) {
+ LocalTime result = convertImpl(input);
+ if (result == null) {
+ throw new CoercingParseValueException("Invalid value '" + input + "' for LocalTime");
+ }
+ return result;
+ }
+
+ @Override
+ public LocalTime parseLiteral(Object input) {
+ if (!(input instanceof StringValue)) return null;
+ String value = ((StringValue) input).getValue();
+ LocalTime result = convertImpl(value);
+ return result;
+ }
+ });
+ }
+
+}
diff --git a/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLDateTest.groovy b/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLDateTest.groovy
new file mode 100644
index 0000000..cba11fa
--- /dev/null
+++ b/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLDateTest.groovy
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime
+
+import graphql.language.StringValue
+import graphql.schema.CoercingParseValueException
+import graphql.schema.CoercingSerializeException
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static DateTimeHelper.createDate
+
+/**
+ * @author Alexey Zhokhov
+ */
+class GraphQLDateTest extends Specification {
+
+ @Unroll
+ def "Date parse literal #literal.value as #result"() {
+ expect:
+ new GraphQLDate().getCoercing().parseLiteral(literal) == result
+
+ where:
+ literal | result
+ new StringValue('2017-07-09T11:54:42.277Z') | createDate(2017, 7, 9, 11, 54, 42, 277)
+ new StringValue('2017-07-09T13:14:45.947Z') | createDate(2017, 7, 9, 13, 14, 45, 947)
+ new StringValue('2017-07-09T11:54:42Z') | createDate(2017, 7, 9, 11, 54, 42)
+ new StringValue('2017-07-09') | createDate(2017, 7, 9)
+ }
+
+ @Unroll
+ def "Date returns null for invalid #literal"() {
+ expect:
+ new GraphQLDate().getCoercing().parseLiteral(literal) == null
+
+ where:
+ literal | _
+ new StringValue("not a date") | _
+ }
+
+ @Unroll
+ def "Date serialize #value into #result (#result.class)"() {
+ expect:
+ new GraphQLDate().getCoercing().serialize(value) == result
+
+ where:
+ value | result
+ createDate(2017, 7, 9, 11, 54, 42, 277) | '2017-07-09T11:54:42.277Z'
+ createDate(2017, 7, 9, 13, 14, 45, 947) | '2017-07-09T13:14:45.947Z'
+ createDate(2017, 7, 9, 11, 54, 42) | '2017-07-09T11:54:42Z'
+ createDate(2017, 7, 9) | '2017-07-09T00:00:00Z'
+ }
+
+ @Unroll
+ def "serialize throws exception for invalid input #value"() {
+ when:
+ new GraphQLDate().getCoercing().serialize(value)
+ then:
+ thrown(CoercingSerializeException)
+
+ where:
+ value | _
+ '' | _
+ 'not a date' | _
+ new Object() | _
+ }
+
+ @Unroll
+ def "Date parse #value into #result (#result.class)"() {
+ expect:
+ new GraphQLDate().getCoercing().parseValue(value) == result
+
+ where:
+ value | result
+ '2017-07-09T11:54:42.277Z' | createDate(2017, 7, 9, 11, 54, 42, 277)
+ '2017-07-09T13:14:45.947Z' | createDate(2017, 7, 9, 13, 14, 45, 947)
+ '2017-07-09T11:54:42Z' | createDate(2017, 7, 9, 11, 54, 42)
+ '2017-07-09' | createDate(2017, 7, 9)
+ }
+
+ @Unroll
+ def "parseValue throws exception for invalid input #value"() {
+ when:
+ new GraphQLDate().getCoercing().parseValue(value)
+ then:
+ thrown(CoercingParseValueException)
+
+ where:
+ value | _
+ '' | _
+ 'not a date' | _
+ new Object() | _
+ }
+
+}
diff --git a/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLLocalDateTest.groovy b/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLLocalDateTest.groovy
new file mode 100644
index 0000000..500bc49
--- /dev/null
+++ b/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLLocalDateTest.groovy
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime
+
+import graphql.language.StringValue
+import graphql.schema.CoercingParseValueException
+import graphql.schema.CoercingSerializeException
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import java.time.LocalDate
+
+/**
+ * @author Alexey Zhokhov
+ */
+class GraphQLLocalDateTest extends Specification {
+
+ @Unroll
+ def "Date parse literal #literal.value as #result"() {
+ expect:
+ new GraphQLLocalDate().getCoercing().parseLiteral(literal) == result
+
+ where:
+ literal | result
+ new StringValue('2017-07-09') | LocalDate.of(2017, 7, 9)
+ }
+
+ @Unroll
+ def "Date returns null for invalid #literal"() {
+ expect:
+ new GraphQLLocalDate().getCoercing().parseLiteral(literal) == null
+
+ where:
+ literal | _
+ new StringValue("not a date") | _
+ }
+
+ @Unroll
+ def "Date serialize #value into #result (#result.class)"() {
+ expect:
+ new GraphQLLocalDate().getCoercing().serialize(value) == result
+
+ where:
+ value | result
+ LocalDate.of(2017, 7, 9) | '2017-07-09'
+ }
+
+ @Unroll
+ def "serialize throws exception for invalid input #value"() {
+ when:
+ new GraphQLLocalDate().getCoercing().serialize(value)
+ then:
+ thrown(CoercingSerializeException)
+
+ where:
+ value | _
+ '' | _
+ 'not a date' | _
+ new Object() | _
+ }
+
+ @Unroll
+ def "Date parse #value into #result (#result.class)"() {
+ expect:
+ new GraphQLLocalDate().getCoercing().parseValue(value) == result
+
+ where:
+ value | result
+ '2017-07-09' | LocalDate.of(2017, 7, 9)
+ }
+
+ @Unroll
+ def "parseValue throws exception for invalid input #value"() {
+ when:
+ new GraphQLLocalDate().getCoercing().parseValue(value)
+ then:
+ thrown(CoercingParseValueException)
+
+ where:
+ value | _
+ '' | _
+ 'not a date' | _
+ new Object() | _
+ }
+
+}
diff --git a/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLLocalDateTimeTest.groovy b/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLLocalDateTimeTest.groovy
new file mode 100644
index 0000000..11e6fab
--- /dev/null
+++ b/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLLocalDateTimeTest.groovy
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime
+
+import graphql.language.StringValue
+import graphql.schema.CoercingParseValueException
+import graphql.schema.CoercingSerializeException
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.LocalTime
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS
+
+/**
+ * @author Alexey Zhokhov
+ */
+class GraphQLLocalDateTimeTest extends Specification {
+
+ @Unroll
+ def "Date parse literal #literal.value as #result"() {
+ expect:
+ new GraphQLLocalDateTime().getCoercing().parseLiteral(literal) == result
+
+ where:
+ literal | result
+ new StringValue('2017-07-09T11:54:42.277Z') | LocalDateTime.of(2017, 7, 9, 11, 54, 42, (int) MILLISECONDS.toNanos(277))
+ new StringValue('2017-07-09T13:14:45.947Z') | LocalDateTime.of(2017, 7, 9, 13, 14, 45, (int) MILLISECONDS.toNanos(947))
+ new StringValue('2017-07-09T11:54:42Z') | LocalDateTime.of(2017, 7, 9, 11, 54, 42)
+ new StringValue('2017-07-09') | LocalDateTime.of(LocalDate.of(2017, 7, 9), LocalTime.MIDNIGHT)
+ }
+
+ @Unroll
+ def "Date returns null for invalid #literal"() {
+ expect:
+ new GraphQLLocalDateTime().getCoercing().parseLiteral(literal) == null
+
+ where:
+ literal | _
+ new StringValue("not a date") | _
+ }
+
+ @Unroll
+ def "Date serialize #value into #result (#result.class)"() {
+ expect:
+ new GraphQLLocalDateTime().getCoercing().serialize(value) == result
+
+ where:
+ value | result
+ LocalDateTime.of(2017, 7, 9, 11, 54, 42, (int) MILLISECONDS.toNanos(277)) | '2017-07-09T11:54:42.277Z'
+ LocalDateTime.of(2017, 7, 9, 13, 14, 45, (int) MILLISECONDS.toNanos(947)) | '2017-07-09T13:14:45.947Z'
+ LocalDateTime.of(2017, 7, 9, 11, 54, 42) | '2017-07-09T11:54:42Z'
+ LocalDateTime.of(LocalDate.of(2017, 7, 9), LocalTime.MIDNIGHT) | '2017-07-09T00:00:00Z'
+ }
+
+ @Unroll
+ def "serialize throws exception for invalid input #value"() {
+ when:
+ new GraphQLLocalDateTime().getCoercing().serialize(value)
+ then:
+ thrown(CoercingSerializeException)
+
+ where:
+ value | _
+ '' | _
+ 'not a date' | _
+ new Object() | _
+ }
+
+ @Unroll
+ def "Date parse #value into #result (#result.class)"() {
+ expect:
+ new GraphQLLocalDateTime().getCoercing().parseValue(value) == result
+
+ where:
+ value | result
+ '2017-07-09T11:54:42.277Z' | LocalDateTime.of(2017, 7, 9, 11, 54, 42, (int) MILLISECONDS.toNanos(277))
+ '2017-07-09T13:14:45.947Z' | LocalDateTime.of(2017, 7, 9, 13, 14, 45, (int) MILLISECONDS.toNanos(947))
+ '2017-07-09T11:54:42Z' | LocalDateTime.of(2017, 7, 9, 11, 54, 42)
+ '2017-07-09' | LocalDateTime.of(LocalDate.of(2017, 7, 9), LocalTime.MIDNIGHT)
+ }
+
+ @Unroll
+ def "parseValue throws exception for invalid input #value"() {
+ when:
+ new GraphQLLocalDateTime().getCoercing().parseValue(value)
+ then:
+ thrown(CoercingParseValueException)
+
+ where:
+ value | _
+ '' | _
+ 'not a date' | _
+ new Object() | _
+ }
+
+}
diff --git a/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLLocalTimeTest.groovy b/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLLocalTimeTest.groovy
new file mode 100644
index 0000000..aadff46
--- /dev/null
+++ b/graphql-java-datetime/src/test/groovy/graphql/datetime/GraphQLLocalTimeTest.groovy
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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 graphql.datetime
+
+import graphql.language.StringValue
+import graphql.schema.CoercingParseValueException
+import graphql.schema.CoercingSerializeException
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import java.time.LocalTime
+import java.util.concurrent.TimeUnit
+
+/**
+ * @author Alexey Zhokhov
+ */
+class GraphQLLocalTimeTest extends Specification {
+
+ @Unroll
+ def "Date parse literal #literal.value as #result"() {
+ expect:
+ new GraphQLLocalTime().getCoercing().parseLiteral(literal) == result
+
+ where:
+ literal | result
+ new StringValue('00:00:00') | LocalTime.MIDNIGHT
+ new StringValue('10:15:30') | LocalTime.of(10, 15, 30)
+ new StringValue('17:59:59') | LocalTime.of(17, 59, 59)
+ }
+
+ @Unroll
+ def "Date returns null for invalid #literal"() {
+ expect:
+ new GraphQLLocalTime().getCoercing().parseLiteral(literal) == null
+
+ where:
+ literal | _
+ new StringValue("not a time") | _
+ }
+
+ @Unroll
+ def "Date serialize #value into #result (#result.class)"() {
+ expect:
+ new GraphQLLocalTime().getCoercing().serialize(value) == result
+
+ where:
+ value | result
+ LocalTime.MIDNIGHT | '00:00:00'
+ LocalTime.of(10, 15, 30) | '10:15:30'
+ LocalTime.of(17, 59, 59) | '17:59:59'
+ LocalTime.of(17, 59, 59, (int) TimeUnit.MILLISECONDS.toNanos(277)) | '17:59:59.277'
+ }
+
+ @Unroll
+ def "serialize throws exception for invalid input #value"() {
+ when:
+ new GraphQLLocalTime().getCoercing().serialize(value)
+ then:
+ thrown(CoercingSerializeException)
+
+ where:
+ value | _
+ '' | _
+ 'not a time' | _
+ new Object() | _
+ }
+
+ @Unroll
+ def "Date parse #value into #result (#result.class)"() {
+ expect:
+ new GraphQLLocalTime().getCoercing().parseValue(value) == result
+
+ where:
+ value | result
+ '00:00:00' | LocalTime.MIDNIGHT
+ '10:15:30' | LocalTime.of(10, 15, 30)
+ '17:59:59' | LocalTime.of(17, 59, 59)
+ '17:59:59.277' | LocalTime.of(17, 59, 59, (int) TimeUnit.MILLISECONDS.toNanos(277))
+ }
+
+ @Unroll
+ def "parseValue throws exception for invalid input #value"() {
+ when:
+ new GraphQLLocalTime().getCoercing().parseValue(value)
+ then:
+ thrown(CoercingParseValueException)
+
+ where:
+ value | _
+ '' | _
+ 'not a time' | _
+ new Object() | _
+ }
+
+}
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..4e0f05a
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017 Alexey Zhokhov
+ *
+ * Licensed 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.
+ */
+
+/**
+ * @author Alexey Zhokhov
+ */
+rootProject.name = projectName
+
+include ':graphql-java-datetime'
+include ':graphql-datetime-autoconfigure'
+include ':graphql-datetime-spring-boot-starter'