[
  {
    "path": ".gitignore",
    "content": ".gradle/\n.idea/\nbin/\nbuild/\n*.iml\n.classpath\n.project\n.settings/\nclasses/\nout/\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: java\njdk:\n  - oraclejdk8\ndeploy:\n  provider: bintray\n  file: bintray.json\n  user: jcrygier\n  key:\n    secure: obNe/VH/vvGr3nSpUQW7+E0XfQAA8zvkuFOM86AfRs51pxblIgLnBkGua9iuRZ8iV6s/275j/SgVV4vkoq1Bkt1ndnmEcsn0XbRuRhJH7yxDdIIclLP/kCAgRFh08ADKD2k51HsqCIjiiSs+B8V0QFQx9pcMtRMchqq/63H1I/TGHUHEpVLh/2+HkTkqFe/0LavxPi1jMt5qkqsPzyCwvE7oM5rzqKUmAbCh66yhi1ao0DkwJybtIq0yjgfKHVKydAr2O0So8kYRkRnddBxi1NkTrbcXm9kwcrM+bmS4S7eMgRWiq/oNYBD0sefYP5NySsDh+phzjIqGFxvTQvp/EfzKlupR2mogK3GjHmuyl0tII4qjrxEnlT6Pj08UvUb2jloHvckdqpj0Gz8f3Iq3/HsxVRXBMshm11dEFXqHIs6UbGQFtf5TkgP88iPt3kfh+0yAWh5EVp+YAG6HnkrTure2YC0JgiRmYHFD0lYxFQ0LoGteECNg1NpMu1lU/f0EaEy98XX+aCjW5KPSyWNEpTXo74IZsjIZEqN1mbfxvtyKc1p1ANK1oQW23FeAI0ZjnCT8FEntk1UJIZqazw2QQvCyYVawESgqFCzkpN4D6uJjU15xQKQos7tK9f1Lm6Xl47PcaA82iFYFxQji3VdC1a+vlCXWPP+XKua2OPhuAoQ=\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 John Crygier and Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "GraphQL for JPA\n===============\n\nIf you're already using JPA, then you already have a schema defined...don't define it again just for GraphQL!\n\nThis is a simple project to extend [graphql-java](https://github.com/andimarek/graphql-java) and have it derive the\nschema from a JPA model.  It also implements an execution platform to generate and run JPA queries based on\nGraphQL queries.\n\nWhile limited, there is a lot of power bundled within.  This project takes a somewhat opinionated view of GraphQL, by\nintroducing things like Pagination, Aggregations, and Sorting.\n\nThis project is intended on depending on very little: graphql-java, and some javax annotation packages.  The tests depend\non Spring (with Hibernate for JPA), and Spock for testing.  These tests are a good illustration of how this library might\nbe used, but any stack (with JPA) should be able to be utilized.\n\nSchema Generation\n-----------------\n\nUsing a JPA Entity Manager, the models are introspected, and a GraphQL Schema is built.  With this GraphQL schema,\ngraphql-java does most of the work, except for querying.\n\nSchema Documentation\n--------------------\n\nA major part of GraphQL is the ability to have a well documented schema.  This project takes advantage of this, and produces\ndescriptions for each Entity in the schema.  For the built in types (e.g. PaginationObject) these are rather hard-coded\nwithout much control from the end user.\n\nHowever, for each Entity / Member that is in your JPA schema, you can document what it's for.  These descriptions are controlled\nby the `@SchemaDocumentation` attribute on either a class level, or a field level of your model.\n\nThese descriptions will show up in the GraphiQL browser automatically, and generally helps when providing an API to your\nend-users.  See the GraphiQL section below for more details.\n\nPagination\n----------\n\nGraphQL does not specify any language or idioms for performing Pagination.  Therefore, this library takes an opinionated\nview, similar to that of Spring.\n\nEach model (say Human or Droid - see tests) will have two representations in the generated schema:\n\n- One that models the Entities directly (Human or Droid)\n- One that wraps the Entity in a page request (HumanConnection or DroidConnection)\n\nThis allows you to query for the \"Page\" version of any Entity, and return metadata (like total count) alongside of the\nactual requested data.  For example:\n\n    {\n        HumanConnection(paginationRequest: { page: 1, size: 2 }) {\n            totalPages\n            totalElements\n            content {\n                name\n            }\n        }\n    }\n\nWill return:\n\n    {\n        HumanConnection: {\n            totalPages: 3,\n            totalElements: 5,\n            content: [\n                { name: 'Luke Skywalker' },\n                { name: 'Darth Vader' }\n            ]\n        }\n    }\n\nOf course, an extra query is needed to get the total elements, so if you have not requested 'totalPages' or 'totalElements'\nthis query will not be executed.\n\nNOTE: The \"Connection\" name is used here for further extension (Aggregations, etc...).  The name is borrowed\nfrom suggestions by Facebook developers: https://github.com/facebook/graphql/issues/4\n\nAggregations\n------------\n\nNot yet implemented, but will be similar to Pagination\n\nSorting\n-------\n\nSorting is supported on any field.  Simply pass in an 'orderBy' argument with the value of ASC or DESC.  Here's an example\nof sorting by name for Human objects:\n\n    {\n        Human {\n            name(orderBy: DESC)\n            homePlanet\n        }\n    }\n\nQuery Injectors\n---------------\n\nNot yet implemented.  Main use case would be to intercept query execution for security purposes.\n\nGraphiQL\n--------\n\nGraphiQL (https://github.com/graphql/graphiql) has been introduced for simple testing (in the test package, as I don't\nwant to assume your web stack).  Simply launch TestApplication as a Java Application, and navigate to http://localhost:8080/\nto launch.  You will notice a 'Docs' button at the upper right, that when expanded will show you the running schema (Star\nWars in this demo).\n\nYou can enter GraphQL queries in the left pannel, and hit the run button, and the results should come back in the right\npanel.  If your query has variables, there is a minimized panel at the bottom left.  Simply click on this to expand, and\ntype in your variables as a JSON string (don't forget to quote the keys!).  Enjoy!"
  },
  {
    "path": "bintray.json",
    "content": "{\n    \"package\": {\n        \"name\": \"GraphQL-JPA\",\n        \"repo\": \"maven\",\n        \"subject\": \"jcrygier\"\n    },\n    \"version\": {\n        \"name\": \"0.6\"\n    },\n    \"files\": [\n        {\"includePattern\": \"build/libs/(.*)\", \"excludePattern\": \".*/do-not-deploy/.*\", \"uploadPattern\": \"com/crygier/graphql-jpa/0.6/$1\"}\n    ],\n    \"publish\": true\n}\n"
  },
  {
    "path": "build.gradle",
    "content": "apply plugin: 'groovy'\napply plugin: 'eclipse'\napply plugin: 'idea'\napply plugin: 'maven'\napply plugin: 'maven-publish'\n\ngroup = 'com.crygier'\nversion = '0.6'\n\njar {\n    baseName = 'graphql-jpa'\n    version = project.version\n}\nsourceCompatibility = 1.8\ntargetCompatibility = 1.8\n\nrepositories {\n    mavenCentral()\n    maven {\n        url  \"http://dl.bintray.com/andimarek/graphql-java\"\n    }\n}\n\nconfigurations {\n    provided\n    compile.extendsFrom provided\n}\n\ndependencies {\n    compile 'com.graphql-java:graphql-java:4.2'\n    compile 'javax.transaction:javax.transaction-api:1.2'\n    provided 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'\n\n    testCompile \"org.springframework.boot:spring-boot-starter:1.5.1.RELEASE\"\n    testCompile \"org.springframework.boot:spring-boot-starter-test:1.5.1.RELEASE\"\n    testCompile \"org.springframework.boot:spring-boot-starter-data-jpa:1.5.1.RELEASE\"\n    testCompile \"org.springframework.boot:spring-boot-starter-web:1.5.1.RELEASE\"\n    testCompile 'junit:junit:4.11'\n    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'\n    testCompile 'org.spockframework:spock-spring:1.0-groovy-2.4'\n    testCompile 'org.codehaus.groovy:groovy-all:2.4.4'\n    testCompile 'cglib:cglib-nodep:3.1'\n    testCompile 'org.objenesis:objenesis:2.1'\n\n    testRuntime \"com.h2database:h2:1.4.190\"\n    //testRuntime 'org.hibernate:hibernate-validator:4.3.0.Final'\n}\n\npublishing {\n\tpublications {\n\t\tMyPublication(MavenPublication) {\n\t\t\tfrom components.java\n\t\t\tgroupId 'com.crygier'\n    \t\tartifactId 'graphql-jpa'\n    \t\tversion project.version\n            artifact sourcesJar\n\t\t}\n\t}\n}\n\nmodel {\n    tasks.generatePomFileForMyPublicationPublication {\n        destination = file(\"$buildDir/libs/${project.name}-${version}.pom\")\n    }\n\n\ttasks.assemble {\n\t\tdependsOn tasks.generatePomFileForMyPublicationPublication\n\t}\n}\n\ntask sourcesJar(type: Jar, dependsOn: classes) {\n    classifier = 'sources'\n    from sourceSets.main.allSource\n}\n\nartifacts {\n    archives sourcesJar\n}\n\neclipse {\n    classpath {\n         containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')\n         containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'\n    }\n}\n\ntask wrapper(type: Wrapper) {\n    gradleVersion = '2.3'\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Tue Sep 15 19:06:52 CDT 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.3-bin.zip\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "src/main/java/org/crygier/graphql/AttributeMapper.java",
    "content": "package org.crygier.graphql;\n\nimport graphql.schema.GraphQLType;\n\nimport java.util.Optional;\n\n/**\n * (Functional) Interface to map Classes to GraphQLTypes.\n */\n@FunctionalInterface\npublic interface AttributeMapper {\n\n    /**\n     * Returns the GraphQLType for the given Class.  If this mapper doesn't know how to handle this particular class,\n     * it MUST return an empty Optional.\n     *\n     * @param javaType\n     * @return\n     */\n    Optional<GraphQLType> getBasicAttributeType(Class javaType);\n\n}\n"
  },
  {
    "path": "src/main/java/org/crygier/graphql/ExtendedJpaDataFetcher.java",
    "content": "package org.crygier.graphql;\n\nimport graphql.language.Argument;\nimport graphql.language.Field;\nimport graphql.language.IntValue;\nimport graphql.language.ObjectValue;\nimport graphql.schema.DataFetchingEnvironment;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.TypedQuery;\nimport javax.persistence.criteria.CriteriaBuilder;\nimport javax.persistence.criteria.CriteriaQuery;\nimport javax.persistence.criteria.Predicate;\nimport javax.persistence.criteria.Root;\nimport javax.persistence.metamodel.EntityType;\nimport javax.persistence.metamodel.SingularAttribute;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\npublic class ExtendedJpaDataFetcher extends JpaDataFetcher {\n\n    public ExtendedJpaDataFetcher(EntityManager entityManager, EntityType<?> entityType) {\n        super(entityManager, entityType);\n    }\n\n    @Override\n    public Object get(DataFetchingEnvironment environment) {\n        Field field = environment.getFields().iterator().next();\n        Map<String, Object> result = new LinkedHashMap<>();\n\n        PageInformation pageInformation = extractPageInformation(environment, field);\n\n        // See which fields we're requesting\n        Optional<Field> totalPagesSelection = getSelectionField(field, \"totalPages\");\n        Optional<Field> totalElementsSelection = getSelectionField(field, \"totalElements\");\n        Optional<Field> contentSelection = getSelectionField(field, \"content\");\n\n        if (contentSelection.isPresent())\n            result.put(\"content\", getQuery(environment, contentSelection.get()).setMaxResults(pageInformation.size).setFirstResult((pageInformation.page - 1) * pageInformation.size).getResultList());\n\n        if (totalElementsSelection.isPresent() || totalPagesSelection.isPresent()) {\n            final Long totalElements = contentSelection\n                    .map(contentField -> getCountQuery(environment, contentField).getSingleResult())\n                    // if no \"content\" was selected an empty Field can be used\n                    .orElseGet(() -> getCountQuery(environment, new Field()).getSingleResult());\n\n            result.put(\"totalElements\", totalElements);\n            result.put(\"totalPages\", ((Double) Math.ceil(totalElements / (double) pageInformation.size)).longValue());\n        }\n\n        return result;\n    }\n\n    private TypedQuery<Long> getCountQuery(DataFetchingEnvironment environment, Field field) {\n        CriteriaBuilder cb = entityManager.getCriteriaBuilder();\n        CriteriaQuery<Long> query = cb.createQuery(Long.class);\n        Root root = query.from(entityType);\n\n        SingularAttribute idAttribute = entityType.getId(Object.class);\n        query.select(cb.count(root.get(idAttribute.getName())));\n        List<Predicate> predicates = field.getArguments().stream().map(it -> cb.equal(root.get(it.getName()), convertValue(environment, it, it.getValue()))).collect(Collectors.toList());\n        query.where(predicates.toArray(new Predicate[predicates.size()]));\n\n        return entityManager.createQuery(query);\n    }\n\n    private Optional<Field> getSelectionField(Field field, String fieldName) {\n        return field.getSelectionSet().getSelections().stream().filter(it -> it instanceof Field).map(it -> (Field) it).filter(it -> fieldName.equals(it.getName())).findFirst();\n    }\n\n    private PageInformation extractPageInformation(DataFetchingEnvironment environment, Field field) {\n        Optional<Argument> paginationRequest = field.getArguments().stream().filter(it -> GraphQLSchemaBuilder.PAGINATION_REQUEST_PARAM_NAME.equals(it.getName())).findFirst();\n        if (paginationRequest.isPresent()) {\n            field.getArguments().remove(paginationRequest.get());\n\n            ObjectValue paginationValues = (ObjectValue) paginationRequest.get().getValue();\n            IntValue page = (IntValue) paginationValues.getObjectFields().stream().filter(it -> \"page\".equals(it.getName())).findFirst().get().getValue();\n            IntValue size = (IntValue) paginationValues.getObjectFields().stream().filter(it -> \"size\".equals(it.getName())).findFirst().get().getValue();\n\n            return new PageInformation(page.getValue().intValue(), size.getValue().intValue());\n        }\n\n        return new PageInformation(1, Integer.MAX_VALUE);\n    }\n\n    private static final class PageInformation {\n        public Integer page;\n        public Integer size;\n\n        public PageInformation(Integer page, Integer size) {\n            this.page = page;\n            this.size = size;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/org/crygier/graphql/GraphQLExecutor.java",
    "content": "package org.crygier.graphql;\n\nimport graphql.ExecutionInput;\nimport graphql.ExecutionResult;\nimport graphql.GraphQL;\nimport graphql.schema.GraphQLSchema;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.Resource;\nimport javax.persistence.EntityManager;\nimport javax.transaction.Transactional;\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * A GraphQL executor capable of constructing a {@link GraphQLSchema} from a JPA {@link EntityManager}. The executor\n * uses the constructed schema to execute queries directly from the JPA data source.\n * <p>\n * If the executor is given a mutator function, it is feasible to manipulate the {@link GraphQLSchema}, introducing\n * the option to add mutations, subscriptions etc.\n */\npublic class GraphQLExecutor {\n\n    @Resource\n    private EntityManager entityManager;\n    private GraphQL graphQL;\n    private GraphQLSchema graphQLSchema;\n    private GraphQLSchema.Builder builder;\n\n    protected GraphQLExecutor() {\n        createGraphQL(null);\n    }\n\n    /**\n     * Creates a read-only GraphQLExecutor using the entities discovered from the given {@link EntityManager}.\n     *\n     * @param entityManager The entity manager from which the JPA classes annotated with\n     *                      {@link javax.persistence.Entity} is extracted as {@link GraphQLSchema} objects.\n     */\n    public GraphQLExecutor(EntityManager entityManager) {\n        this.entityManager = entityManager;\n        createGraphQL(null);\n    }\n\n    /**\n     * Creates a read-only GraphQLExecutor using the entities discovered from the given {@link EntityManager}.\n     *\n     * @param entityManager The entity manager from which the JPA classes annotated with\n     *                      {@link javax.persistence.Entity} is extracted as {@link GraphQLSchema} objects.\n     * @param attributeMappers Custom {@link AttributeMapper} list, if you need any non-standard mappings.\n     */\n    public GraphQLExecutor(EntityManager entityManager, Collection<AttributeMapper> attributeMappers) {\n        this.entityManager = entityManager;\n        createGraphQL(attributeMappers);\n    }\n\n    @PostConstruct\n    protected synchronized void createGraphQL() {\n        createGraphQL(null);\n    }\n\n    protected synchronized void createGraphQL(Collection<AttributeMapper> attributeMappers) {\n        if (entityManager != null) {\n            if (builder == null && attributeMappers == null) {\n                this.builder = new GraphQLSchemaBuilder(entityManager);\n            } else if (builder == null) {\n                this.builder = new GraphQLSchemaBuilder(entityManager, attributeMappers);\n            }\n            this.graphQLSchema = builder.build();\n            this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();\n        }\n    }\n\n    /**\n     * @return The {@link GraphQLSchema} used by this executor.\n     */\n    public GraphQLSchema getGraphQLSchema() {\n        return graphQLSchema;\n    }\n\n    @Transactional\n    public ExecutionResult execute(String query) {\n        return graphQL.execute(query);\n    }\n\n    @Transactional\n    public ExecutionResult execute(String query, Map<String, Object> arguments) {\n        if (arguments == null)\n            return graphQL.execute(query);\n        return graphQL.execute(ExecutionInput.newExecutionInput().query(query).variables(arguments).build());\n    }\n\n    /**\n     * Gets the builder that was used to create the Schema that this executor is basing its query executions on. The\n     * builder can be used to update the executor with the {@link #updateSchema(GraphQLSchema.Builder)} method.\n     * @return An instance of a builder.\n     */\n    public GraphQLSchema.Builder getBuilder() {\n        return builder;\n    }\n\n    /**\n     * Returns the schema that this executor bases its queries on.\n     * @return An instance of a {@link GraphQLSchema}.\n     */\n    public GraphQLSchema getSchema() {\n        return graphQLSchema;\n    }\n\n    /**\n     * Uses the given builder to re-create and replace the {@link GraphQLSchema}\n     * that this executor uses to execute its queries.\n     *\n     * @param builder The builder to recreate the current {@link GraphQLSchema} and {@link GraphQL} instances.\n     * @return The same executor but with a new {@link GraphQL} schema.\n     */\n    public GraphQLExecutor updateSchema(GraphQLSchema.Builder builder) {\n        this.builder = builder;\n        createGraphQL(null);\n        return this;\n    }\n\n    /**\n     * Uses the given builder to re-create and replace the {@link GraphQLSchema}\n     * that this executor uses to execute its queries.\n     *\n     * @param builder The builder to recreate the current {@link GraphQLSchema} and {@link GraphQL} instances.\n     * @param attributeMappers Custom {@link AttributeMapper} list, if you need any non-standard mappings.\n     * @return The same executor but with a new {@link GraphQL} schema.\n     */\n    public GraphQLExecutor updateSchema(GraphQLSchema.Builder builder, Collection<AttributeMapper> attributeMappers) {\n        this.builder = builder;\n        createGraphQL(attributeMappers);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/crygier/graphql/GraphQLSchemaBuilder.java",
    "content": "package org.crygier.graphql;\n\nimport graphql.Scalars;\nimport graphql.schema.GraphQLArgument;\nimport graphql.schema.GraphQLEnumType;\nimport graphql.schema.GraphQLFieldDefinition;\nimport graphql.schema.GraphQLInputObjectField;\nimport graphql.schema.GraphQLInputObjectType;\nimport graphql.schema.GraphQLInputType;\nimport graphql.schema.GraphQLList;\nimport graphql.schema.GraphQLObjectType;\nimport graphql.schema.GraphQLOutputType;\nimport graphql.schema.GraphQLScalarType;\nimport graphql.schema.GraphQLSchema;\nimport graphql.schema.GraphQLType;\nimport graphql.schema.GraphQLTypeReference;\nimport org.crygier.graphql.annotation.GraphQLIgnore;\nimport org.crygier.graphql.annotation.SchemaDocumentation;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.metamodel.Attribute;\nimport javax.persistence.metamodel.EmbeddableType;\nimport javax.persistence.metamodel.EntityType;\nimport javax.persistence.metamodel.ManagedType;\nimport javax.persistence.metamodel.PluralAttribute;\nimport javax.persistence.metamodel.SingularAttribute;\nimport javax.persistence.metamodel.Type;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Member;\nimport java.math.BigDecimal;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.UUID;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * A wrapper for the {@link GraphQLSchema.Builder}. In addition to exposing the traditional builder functionality,\n * this class constructs an initial {@link GraphQLSchema} by scanning the given {@link EntityManager} for relevant\n * JPA entities. This happens at construction time.\n *\n * Note: This class should not be accessed outside this library.\n */\npublic class GraphQLSchemaBuilder extends GraphQLSchema.Builder {\n\n    public static final String PAGINATION_REQUEST_PARAM_NAME = \"paginationRequest\";\n    private static final Logger log = LoggerFactory.getLogger(GraphQLSchemaBuilder.class);\n\n    private final EntityManager entityManager;\n    private final Map<Class, GraphQLType> classCache = new HashMap<>();\n    private final Map<EmbeddableType<?>, GraphQLObjectType> embeddableCache = new HashMap<>();\n    private final Map<EntityType, GraphQLObjectType> entityCache = new HashMap<>();\n    private final List<AttributeMapper> attributeMappers = new ArrayList<>();\n\n    /**\n     * Initialises the builder with the given {@link EntityManager} from which we immediately start to scan for\n     * entities to include in the GraphQL schema.\n     * @param entityManager The manager containing the data models to include in the final GraphQL schema.\n     */\n    public GraphQLSchemaBuilder(EntityManager entityManager) {\n        this.entityManager = entityManager;\n\n        populateStandardAttributeMappers();\n\n        super.query(getQueryType());\n    }\n\n    public GraphQLSchemaBuilder(EntityManager entityManager, Collection<AttributeMapper> attributeMappers) {\n        this.entityManager = entityManager;\n\n        this.attributeMappers.addAll(attributeMappers);\n        populateStandardAttributeMappers();\n\n        super.query(getQueryType());\n    }\n\n    private void populateStandardAttributeMappers() {\n        attributeMappers.add(createStandardAttributeMapper(UUID.class, JavaScalars.GraphQLUUID));\n        attributeMappers.add(createStandardAttributeMapper(Date.class, JavaScalars.GraphQLDate));\n        attributeMappers.add(createStandardAttributeMapper(LocalDateTime.class, JavaScalars.GraphQLLocalDateTime));\n        attributeMappers.add(createStandardAttributeMapper(Instant.class, JavaScalars.GraphQLInstant));\n        attributeMappers.add(createStandardAttributeMapper(LocalDate.class, JavaScalars.GraphQLLocalDate));\n    }\n\n    private AttributeMapper createStandardAttributeMapper(final Class<?> assignableClass, final GraphQLType type) {\n        return (javaType) -> {\n            if (assignableClass.isAssignableFrom(javaType))\n                return Optional.of(type);\n\n            return Optional.empty();\n        };\n    }\n\n    /**\n     * @deprecated Use {@link #build()} instead.\n     * @return A freshly built {@link GraphQLSchema}\n     */\n    @Deprecated()\n    public GraphQLSchema getGraphQLSchema() {\n        return super.build();\n    }\n\n    GraphQLObjectType getQueryType() {\n        GraphQLObjectType.Builder queryType = GraphQLObjectType.newObject().name(\"QueryType_JPA\").description(\"All encompassing schema for this JPA environment\");\n        queryType.fields(entityManager.getMetamodel().getEntities().stream().filter(this::isNotIgnored).map(this::getQueryFieldDefinition).collect(Collectors.toList()));\n        queryType.fields(entityManager.getMetamodel().getEntities().stream().filter(this::isNotIgnored).map(this::getQueryFieldPageableDefinition).collect(Collectors.toList()));\n        queryType.fields(entityManager.getMetamodel().getEmbeddables().stream().filter(this::isNotIgnored).map(this::getQueryEmbeddedFieldDefinition).collect(Collectors.toList()));\n\n        return queryType.build();\n    }\n\n    GraphQLFieldDefinition getQueryFieldDefinition(EntityType<?> entityType) {\n        return GraphQLFieldDefinition.newFieldDefinition()\n                .name(entityType.getName())\n                .description(getSchemaDocumentation(entityType.getJavaType()))\n                .type(new GraphQLList(getObjectType(entityType)))\n                .dataFetcher(new JpaDataFetcher(entityManager, entityType))\n                .argument(entityType.getAttributes().stream().filter(this::isValidInput).filter(this::isNotIgnored).flatMap(this::getArgument).collect(Collectors.toList()))\n                .build();\n    }\n    \n    GraphQLFieldDefinition getQueryEmbeddedFieldDefinition(EmbeddableType<?> embeddableType) {\n    \tString embeddedName = embeddableType.getJavaType().getSimpleName();\n        return GraphQLFieldDefinition.newFieldDefinition()\n                .name(embeddedName)\n                .description(getSchemaDocumentation(embeddableType.getJavaType()))\n                .type(new GraphQLList(getObjectType(embeddableType)))\n                .argument(embeddableType.getAttributes().stream().filter(this::isValidInput).filter(this::isNotIgnored).flatMap(this::getArgument).collect(Collectors.toList()))\n                .build();\n    }\n\n    private GraphQLFieldDefinition getQueryFieldPageableDefinition(EntityType<?> entityType) {\n        GraphQLObjectType pageType = GraphQLObjectType.newObject()\n                .name(entityType.getName() + \"Connection\")\n                .description(\"'Connection' response wrapper object for \" + entityType.getName() + \".  When pagination or aggregation is requested, this object will be returned with metadata about the query.\")\n                .field(GraphQLFieldDefinition.newFieldDefinition().name(\"totalPages\").description(\"Total number of pages calculated on the database for this pageSize.\").type(Scalars.GraphQLLong).build())\n                .field(GraphQLFieldDefinition.newFieldDefinition().name(\"totalElements\").description(\"Total number of results on the database for this query.\").type(Scalars.GraphQLLong).build())\n                .field(GraphQLFieldDefinition.newFieldDefinition().name(\"content\").description(\"The actual object results\").type(new GraphQLList(getObjectType(entityType))).build())\n                .build();\n\n        return GraphQLFieldDefinition.newFieldDefinition()\n                .name(entityType.getName() + \"Connection\")\n                .description(\"'Connection' request wrapper object for \" + entityType.getName() + \".  Use this object in a query to request things like pagination or aggregation in an argument.  Use the 'content' field to request actual fields \")\n                .type(pageType)\n                .dataFetcher(new ExtendedJpaDataFetcher(entityManager, entityType))\n                .argument(paginationArgument)\n                .build();\n    }\n\n    private Stream<GraphQLArgument> getArgument(Attribute attribute) {\n        return getAttributeType(attribute)\n                .filter(type -> type instanceof GraphQLInputType)\n                .filter(type -> attribute.getPersistentAttributeType() != Attribute.PersistentAttributeType.EMBEDDED ||\n                        (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED && type instanceof GraphQLScalarType))\n                .map(type -> {\n                    String name = attribute.getName();                   \n\n                    return GraphQLArgument.newArgument()\n                            .name(name)\n                            .type((GraphQLInputType) type)\n                            .build();\n                });\n    }\n\n    GraphQLObjectType getObjectType(EntityType<?> entityType) {\n        if (entityCache.containsKey(entityType))\n            return entityCache.get(entityType);\n\n        GraphQLObjectType answer = GraphQLObjectType.newObject()\n                .name(entityType.getName())\n                .description(getSchemaDocumentation(entityType.getJavaType()))\n                .fields(entityType.getAttributes().stream().filter(this::isNotIgnored).flatMap(this::getObjectField).collect(Collectors.toList()))\n                .build();\n\n        entityCache.put(entityType, answer);\n\n        return answer;\n    }\n    \n    GraphQLObjectType getObjectType(EmbeddableType<?> embeddableType) {\n    \t\n        if (embeddableCache.containsKey(embeddableType))\n            return embeddableCache.get(embeddableType);\n\n        String embeddableName= embeddableType.getJavaType().getSimpleName();\n        GraphQLObjectType answer = GraphQLObjectType.newObject()\n                .name(embeddableName)\n                .description(getSchemaDocumentation(embeddableType.getJavaType()))\n                .fields(embeddableType.getAttributes().stream().filter(this::isNotIgnored).flatMap(this::getObjectField).collect(Collectors.toList()))\n                .build();\n\n        embeddableCache.put(embeddableType, answer);\n\n        return answer;\n    }\n\n    private Stream<GraphQLFieldDefinition> getObjectField(Attribute attribute) {\n        return getAttributeType(attribute)\n                .filter(type -> type instanceof GraphQLOutputType)\n                .map(type -> {\n                    List<GraphQLArgument> arguments = new ArrayList<>();\n                    arguments.add(GraphQLArgument.newArgument().name(\"orderBy\").type(orderByDirectionEnum).build());            // Always add the orderBy argument\n\n                    // Get the fields that can be queried on (i.e. Simple Types, no Sub-Objects)\n                    if (attribute instanceof SingularAttribute\n                            && attribute.getPersistentAttributeType() != Attribute.PersistentAttributeType.BASIC) {\n                        ManagedType foreignType = (ManagedType) ((SingularAttribute) attribute).getType();\n\n                        Stream<Attribute> attributes = findBasicAttributes(foreignType.getAttributes());\n\n                        attributes.forEach(it -> {\n                            arguments.add(GraphQLArgument.newArgument()\n                                    .name(it.getName())\n                                    .type((GraphQLInputType) getAttributeType(it).findFirst().get())\n                                    .build());\n                        });\n                    }\n\n                    String name = attribute.getName();\n                    \n\n                    return GraphQLFieldDefinition.newFieldDefinition()\n                            .name(name)\n                            .description(getSchemaDocumentation(attribute.getJavaMember()))\n                            .type((GraphQLOutputType) type)\n                            .argument(arguments)\n                            .build();\n                });\n    }\n\n    private Stream<Attribute> findBasicAttributes(Collection<Attribute> attributes) {\n        return attributes.stream().filter(this::isNotIgnored).filter(it -> it.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC);\n    }\n\n    private GraphQLType getBasicAttributeType(Class javaType) {\n        // First check our 'standard' and 'customized' Attribute Mappers.  Use them if possible\n        Optional<AttributeMapper> customMapper = attributeMappers.stream()\n                .filter(it -> it.getBasicAttributeType(javaType).isPresent())\n                .findFirst();\n\n        if (customMapper.isPresent())\n            return customMapper.get().getBasicAttributeType(javaType).get();\n        else if (String.class.isAssignableFrom(javaType))\n            return Scalars.GraphQLString;\n        else if (Integer.class.isAssignableFrom(javaType) || int.class.isAssignableFrom(javaType))\n            return Scalars.GraphQLInt;\n        else if (Short.class.isAssignableFrom(javaType) || short.class.isAssignableFrom(javaType))\n            return Scalars.GraphQLShort;\n        else if (Float.class.isAssignableFrom(javaType) || float.class.isAssignableFrom(javaType)\n                || Double.class.isAssignableFrom(javaType) || double.class.isAssignableFrom(javaType))\n            return Scalars.GraphQLFloat;\n        else if (Long.class.isAssignableFrom(javaType) || long.class.isAssignableFrom(javaType))\n            return Scalars.GraphQLLong;\n        else if (Boolean.class.isAssignableFrom(javaType) || boolean.class.isAssignableFrom(javaType))\n            return Scalars.GraphQLBoolean;\n        else if (javaType.isEnum()) {\n            return getTypeFromJavaType(javaType);\n        } else if (BigDecimal.class.isAssignableFrom(javaType)) {\n            return Scalars.GraphQLBigDecimal;\n        }\n\n        throw new UnsupportedOperationException(\n                \"Class could not be mapped to GraphQL: '\" + javaType.getClass().getTypeName() + \"'\");\n    }\n\n    private Stream<GraphQLType> getAttributeType(Attribute attribute) {\n        if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC) {\n            try {\n                return Stream.of(getBasicAttributeType(attribute.getJavaType()));\n            } catch (UnsupportedOperationException e) {\n                //fall through to the exception below\n                //which is more useful because it also contains the declaring member\n            }\n        } else if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_MANY || attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_MANY) {\n            EntityType foreignType = (EntityType) ((PluralAttribute) attribute).getElementType();\n            return Stream.of(new GraphQLList(new GraphQLTypeReference(foreignType.getName())));\n        } else if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_ONE || attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_ONE) {\n            EntityType foreignType = (EntityType) ((SingularAttribute) attribute).getType();\n            return Stream.of(new GraphQLTypeReference(foreignType.getName()));\n        } else if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ELEMENT_COLLECTION) {\n            Type foreignType = ((PluralAttribute) attribute).getElementType();\n            return Stream.of(new GraphQLList(getTypeFromJavaType(foreignType.getJavaType())));\n        } else if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED) {\n            EmbeddableType<?> embeddableType = (EmbeddableType<?>) ((SingularAttribute<?,?>) attribute).getType();\n            return Stream.of(new GraphQLTypeReference(embeddableType.getJavaType().getSimpleName()));\n        }\n\n        final String declaringType = attribute.getDeclaringType().getJavaType().getName(); // fully qualified name of the entity class\n        final String declaringMember = attribute.getJavaMember().getName(); // field name in the entity class\n\n        throw new UnsupportedOperationException(\n                \"Attribute could not be mapped to GraphQL: field '\" + declaringMember + \"' of entity class '\" + declaringType + \"'\");\n    }\n\n    private boolean isValidInput(Attribute attribute) {\n        return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC ||\n                attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ELEMENT_COLLECTION ||\n                attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED;\n    }\n\n    private String getSchemaDocumentation(Member member) {\n        if (member instanceof AnnotatedElement) {\n            return getSchemaDocumentation((AnnotatedElement) member);\n        }\n\n        return null;\n    }\n\n    private String getSchemaDocumentation(AnnotatedElement annotatedElement) {\n        if (annotatedElement != null) {\n            SchemaDocumentation schemaDocumentation = annotatedElement.getAnnotation(SchemaDocumentation.class);\n            return schemaDocumentation != null ? schemaDocumentation.value() : null;\n        }\n\n        return null;\n    }\n\n    private boolean isNotIgnored(Attribute attribute) {\n        return isNotIgnored(attribute.getJavaMember()) && isNotIgnored(attribute.getJavaType());\n    }\n    \n    private boolean isNotIgnored(EmbeddableType<?> embeddableType) {\n        return isNotIgnored(embeddableType.getJavaType());\n    }\n\n    private boolean isNotIgnored(EntityType entityType) {\n        return isNotIgnored(entityType.getJavaType());\n    }\n\n    private boolean isNotIgnored(Member member) {\n        return member instanceof AnnotatedElement && isNotIgnored((AnnotatedElement) member);\n    }\n\n    private boolean isNotIgnored(AnnotatedElement annotatedElement) {\n        if (annotatedElement != null) {\n            GraphQLIgnore schemaDocumentation = annotatedElement.getAnnotation(GraphQLIgnore.class);\n            return schemaDocumentation == null;\n        }\n\n        return false;\n    }\n\n    private GraphQLType getTypeFromJavaType(Class clazz) {\n        if (clazz.isEnum()) {\n            if (classCache.containsKey(clazz))\n                return classCache.get(clazz);\n\n            GraphQLEnumType.Builder enumBuilder = GraphQLEnumType.newEnum().name(clazz.getSimpleName());\n            int ordinal = 0;\n            for (Enum enumValue : ((Class<Enum>) clazz).getEnumConstants())\n                enumBuilder.value(enumValue.name(), ordinal++);\n\n            GraphQLType answer = enumBuilder.build();\n            setIdentityCoercing(answer);\n\n            classCache.put(clazz, answer);\n\n            return answer;\n        }\n\n        return getBasicAttributeType(clazz);\n    }\n\n    /**\n     * A bit of a hack, since JPA will deserialize our Enum's for us...we don't want GraphQL doing it.\n     *\n     * @param type\n     */\n    private void setIdentityCoercing(GraphQLType type) {\n        try {\n            Field coercing = type.getClass().getDeclaredField(\"coercing\");\n            coercing.setAccessible(true);\n            coercing.set(type, new IdentityCoercing());\n        } catch (Exception e) {\n            log.error(\"Unable to set coercing for \" + type, e);\n        }\n    }\n\n    private static final GraphQLArgument paginationArgument =\n            GraphQLArgument.newArgument()\n                    .name(PAGINATION_REQUEST_PARAM_NAME)\n                    .type(GraphQLInputObjectType.newInputObject()\n                            .name(\"PaginationObject\")\n                            .description(\"Query object for Pagination Requests, specifying the requested page, and that page's size.\\n\\nNOTE: 'page' parameter is 1-indexed, NOT 0-indexed.\\n\\nExample: paginationRequest { page: 1, size: 20 }\")\n                            .field(GraphQLInputObjectField.newInputObjectField().name(\"page\").description(\"Which page should be returned, starting with 1 (1-indexed)\").type(Scalars.GraphQLInt).build())\n                            .field(GraphQLInputObjectField.newInputObjectField().name(\"size\").description(\"How many results should this page contain\").type(Scalars.GraphQLInt).build())\n                            .build()\n                    ).build();\n\n    private static final GraphQLEnumType orderByDirectionEnum =\n            GraphQLEnumType.newEnum()\n                    .name(\"OrderByDirection\")\n                    .description(\"Describes the direction (Ascending / Descending) to sort a field.\")\n                    .value(\"ASC\", 0, \"Ascending\")\n                    .value(\"DESC\", 1, \"Descending\")\n                    .build();\n\n\n}\n"
  },
  {
    "path": "src/main/java/org/crygier/graphql/IdentityCoercing.java",
    "content": "package org.crygier.graphql;\n\nimport graphql.schema.Coercing;\n\npublic class IdentityCoercing implements Coercing{\n\n    @Override\n    public Object serialize(Object input) {\n        return input;\n    }\n\n    @Override\n    public Object parseValue(Object input) {\n        return input;\n    }\n\n    @Override\n    public Object parseLiteral(Object input) {\n        return input;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/crygier/graphql/JavaScalars.java",
    "content": "package org.crygier.graphql;\n\nimport graphql.language.IntValue;\nimport graphql.language.StringValue;\nimport graphql.schema.Coercing;\nimport graphql.schema.CoercingSerializeException;\nimport graphql.schema.GraphQLScalarType;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.math.BigInteger;\nimport java.text.DateFormat;\nimport java.text.ParseException;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeParseException;\nimport java.util.Date;\nimport java.util.TimeZone;\nimport java.util.UUID;\n\npublic class JavaScalars {\n    static final Logger log = LoggerFactory.getLogger(JavaScalars.class);\n\n    public static GraphQLScalarType GraphQLLocalDateTime = new GraphQLScalarType(\"LocalDateTime\", \"Date type\", new Coercing() {\n        @Override\n        public Object serialize(Object input) {\n            if (input instanceof String) {\n                return parseStringToLocalDateTime((String) input);\n            } else if (input instanceof LocalDateTime) {\n                return input;\n            } else if (input instanceof Long) {\n                return parseLongToLocalDateTime((Long) input);\n            } else if (input instanceof Integer) {\n                return parseLongToLocalDateTime((Integer) input);\n            }\n            return null;\n        }\n\n        @Override\n        public Object parseValue(Object input) {\n            return serialize(input);\n        }\n\n        @Override\n        public Object parseLiteral(Object input) {\n            if (input instanceof StringValue) {\n                return parseStringToLocalDateTime(((StringValue) input).getValue());\n            } else if (input instanceof IntValue) {\n                BigInteger value = ((IntValue) input).getValue();\n                return parseLongToLocalDateTime(value.longValue());\n            }\n            return null;\n        }\n\n        private LocalDateTime parseLongToLocalDateTime(long input) {\n            return LocalDateTime.ofInstant(Instant.ofEpochSecond(input), TimeZone.getDefault().toZoneId());\n        }\n\n        private LocalDateTime parseStringToLocalDateTime(String input) {\n            try {\n                return LocalDateTime.parse(input);\n            } catch (DateTimeParseException e) {\n                log.warn(\"Failed to parse Date from input: \" + input, e);\n                return null;\n            }\n        }\n    });\n\n    public static GraphQLScalarType GraphQLInstant = new GraphQLScalarType(\"Instant\", \"Date type\", new Coercing<Instant, Long>() {\n\n        @Override\n        public Long serialize(Object input) {\n            if (input instanceof Instant) {\n                return ((Instant) input).getEpochSecond();\n            }\n            throw new CoercingSerializeException(\n                    \"Expected type 'Instant' but was '\" + input.getClass().getSimpleName() + \"'.\");\n        }\n\n        @Override\n        public Instant parseValue(Object input) {\n            if (input instanceof Long) {\n                return Instant.ofEpochSecond((Long) input);\n            } else if (input instanceof Integer) {\n                return Instant.ofEpochSecond((Integer) input);\n            }\n            throw new CoercingSerializeException(\n                    \"Expected type 'Long' or 'Integer' but was '\" + input.getClass().getSimpleName() + \"'.\");\n        }\n\n        @Override\n        public Instant parseLiteral(Object input) {\n            if (input instanceof IntValue) {\n                return Instant.ofEpochSecond(((IntValue) input).getValue().longValue());\n            }\n            return null;\n        }\n\n    });\n\n    public static GraphQLScalarType GraphQLLocalDate = new GraphQLScalarType(\"LocalDate\", \"Date type\", new Coercing() {\n        @Override\n        public Object serialize(Object input) {\n            if (input instanceof String) {\n                return parseStringToLocalDate((String) input);\n            } else if (input instanceof LocalDate) {\n                return input;\n            } else if (input instanceof Long) {\n                return parseLongToLocalDate((Long) input);\n            } else if (input instanceof Integer) {\n                return parseLongToLocalDate((Integer) input);\n            }\n            return null;\n        }\n\n        @Override\n        public Object parseValue(Object input) {\n            return serialize(input);\n        }\n\n        @Override\n        public Object parseLiteral(Object input) {\n            if (input instanceof StringValue) {\n                return parseStringToLocalDate(((StringValue) input).getValue());\n            } else if (input instanceof IntValue) {\n                BigInteger value = ((IntValue) input).getValue();\n                return parseLongToLocalDate(value.longValue());\n            }\n            return null;\n        }\n\n        private LocalDate parseLongToLocalDate(long input) {\n            return LocalDateTime.ofInstant(Instant.ofEpochSecond(input), TimeZone.getDefault().toZoneId()).toLocalDate();\n        }\n\n        private LocalDate parseStringToLocalDate(String input) {\n            try {\n                return LocalDate.parse(input);\n            } catch (DateTimeParseException e) {\n                log.warn(\"Failed to parse Date from input: \" + input, e);\n                return null;\n            }\n        }\n    });\n\n    public static GraphQLScalarType GraphQLDate = new GraphQLScalarType(\"Date\", \"Date type\", new Coercing() {\n\n        @Override\n        public Object serialize(Object input) {\n            if (input instanceof String) {\n                return parseStringToDate((String) input);\n            } else if (input instanceof Date) {\n                return input;\n            } else if (input instanceof Long) {\n                return new Date(((Long) input).longValue());\n            } else if (input instanceof Integer) {\n                return new Date(((Integer) input).longValue());\n            }\n            return null;\n        }\n\n        @Override\n        public Object parseValue(Object input) {\n            return serialize(input);\n        }\n\n        @Override\n        public Object parseLiteral(Object input) {\n            if (input instanceof StringValue) {\n                return parseStringToDate(((StringValue) input).getValue());\n            } else if (input instanceof IntValue) {\n                BigInteger value = ((IntValue) input).getValue();\n                return new Date(value.longValue());\n            }\n            return null;\n        }\n\n        private Date parseStringToDate(String input) {\n            try {\n                return DateFormat.getInstance().parse(input);\n            } catch (ParseException e) {\n                log.warn(\"Failed to parse Date from input: \" + input, e);\n                return null;\n            }\n        }\n    });\n\n    public static GraphQLScalarType GraphQLUUID = new GraphQLScalarType(\"UUID\", \"UUID type\", new Coercing() {\n\n        @Override\n        public Object serialize(Object input) {\n            if (input instanceof UUID) {\n                return  input;\n            }\n            return null;\n        }\n\n        @Override\n        public Object parseValue(Object input) {\n           if (input instanceof String) {\n                return parseStringToUUID((String) input);\n            }\n            return null;\n        }\n\n        @Override\n        public Object parseLiteral(Object input) {\n            if (input instanceof StringValue) {\n                return parseStringToUUID(((StringValue) input).getValue());\n            }\n            return null;\n        }\n\n        private UUID parseStringToUUID(String input) {\n            try {\n                return UUID.fromString(input);\n            } catch (IllegalArgumentException e) {\n                log.warn(\"Failed to parse UUID from input: \" + input, e);\n                return null;\n            }\n        }\n    });\n}\n"
  },
  {
    "path": "src/main/java/org/crygier/graphql/JpaDataFetcher.java",
    "content": "package org.crygier.graphql;\n\nimport graphql.language.*;\nimport graphql.schema.*;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.TypedQuery;\nimport javax.persistence.criteria.*;\nimport javax.persistence.metamodel.Attribute;\nimport javax.persistence.metamodel.EntityType;\nimport javax.persistence.metamodel.PluralAttribute;\nimport javax.persistence.metamodel.SingularAttribute;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class JpaDataFetcher implements DataFetcher {\n\n    protected EntityManager entityManager;\n    protected EntityType<?> entityType;\n\n    public JpaDataFetcher(EntityManager entityManager, EntityType<?> entityType) {\n        this.entityManager = entityManager;\n        this.entityType = entityType;\n    }\n\n    @Override\n    public Object get(DataFetchingEnvironment environment) {\n        return getQuery(environment, environment.getFields().iterator().next()).getResultList();\n    }\n\n    protected TypedQuery getQuery(DataFetchingEnvironment environment, Field field) {\n        CriteriaBuilder cb = entityManager.getCriteriaBuilder();\n        CriteriaQuery<Object> query = cb.createQuery((Class) entityType.getJavaType());\n        Root root = query.from(entityType);\n\n        List<Argument> arguments = new ArrayList<>();\n\n        // Loop through all of the fields being requested\n        field.getSelectionSet().getSelections().forEach(selection -> {\n            if (selection instanceof Field) {\n                Field selectedField = (Field) selection;\n\n                // \"__typename\" is part of the graphql introspection spec and has to be ignored by jpa\n                if(!\"__typename\".equals(selectedField.getName())) {\n\n                    Path fieldPath = root.get(selectedField.getName());\n\n                    // Process the orderBy clause\n                    Optional<Argument> orderByArgument = selectedField.getArguments().stream().filter(it -> \"orderBy\".equals(it.getName())).findFirst();\n                    if (orderByArgument.isPresent()) {\n                        if (\"DESC\".equals(((EnumValue) orderByArgument.get().getValue()).getName()))\n                            query.orderBy(cb.desc(fieldPath));\n                        else\n                            query.orderBy(cb.asc(fieldPath));\n                    }\n\n                    // Process arguments clauses\n                    arguments.addAll(selectedField.getArguments().stream()\n                            .filter(it -> !\"orderBy\".equals(it.getName()))\n                            .map(it -> new Argument(selectedField.getName() + \".\" + it.getName(), it.getValue()))\n                            .collect(Collectors.toList()));\n\n                    // Check if it's an object and the foreign side is One.  Then we can eagerly fetch causing an inner join instead of 2 queries\n                    if (fieldPath.getModel() instanceof SingularAttribute) {\n                        SingularAttribute attribute = (SingularAttribute) fieldPath.getModel();\n                        if (!attribute.isOptional() && (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_ONE || attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_ONE))\n                            root.fetch(selectedField.getName());\n                    }\n                }\n            }\n        });\n\n        arguments.addAll(field.getArguments());\n\n        List<Predicate> predicates = arguments.stream().map(it -> getPredicate(cb, root, environment, it)).collect(Collectors.toList());\n        query.where(predicates.toArray(new Predicate[predicates.size()]));\n\n        return entityManager.createQuery(query.distinct(true));\n    }\n\n    private Predicate getPredicate(CriteriaBuilder cb, Root root, DataFetchingEnvironment environment, Argument argument) {\n        Path path = null;\n        if (!argument.getName().contains(\".\")) {\n            Attribute argumentEntityAttribute = getAttribute(environment, argument);\n\n            // If the argument is a list, let's assume we need to join and do an 'in' clause\n            if (argumentEntityAttribute instanceof PluralAttribute) {\n                Join join = root.join(argument.getName());\n                return join.in(convertValue(environment, argument, argument.getValue()));\n            }\n\n            path = root.get(argument.getName());\n\n            return cb.equal(path, convertValue(environment, argument, argument.getValue()));\n        } else {\n            List<String> parts = Arrays.asList(argument.getName().split(\"\\\\.\"));\n            for (String part : parts) {\n                if (path == null) {\n                    path = root.get(part);\n                } else {\n                    path = path.get(part);\n                }\n            }\n\n            return cb.equal(path, convertValue(environment, argument, argument.getValue()));\n        }\n    }\n\n    protected Object convertValue(DataFetchingEnvironment environment, Argument argument, Value value) {\n        if (value instanceof StringValue) {\n            Object convertedValue =  environment.getArgument(argument.getName());\n            if (convertedValue != null) {\n                // Return real parameter for instance UUID even if the Value is a StringValue\n                return convertedValue;\n            } else {\n                // Return provided StringValue\n                return ((StringValue) value).getValue();\n            }\n        }\n        else if (value instanceof VariableReference)\n            return environment.getArguments().get(((VariableReference) value).getName());\n        else if (value instanceof ArrayValue)\n            return ((ArrayValue) value).getValues().stream().map((it) -> convertValue(environment, argument, it)).collect(Collectors.toList());\n        else if (value instanceof EnumValue) {\n            Class enumType = getJavaType(environment, argument);\n            return Enum.valueOf(enumType, ((EnumValue) value).getName());\n        } else if (value instanceof IntValue) {\n            return ((IntValue) value).getValue();\n        } else if (value instanceof BooleanValue) {\n            return ((BooleanValue) value).isValue();\n        } else if (value instanceof FloatValue) {\n            return ((FloatValue) value).getValue();\n        }\n\n        return value.toString();\n    }\n\n    private Class getJavaType(DataFetchingEnvironment environment, Argument argument) {\n        Attribute argumentEntityAttribute = getAttribute(environment, argument);\n\n        if (argumentEntityAttribute instanceof PluralAttribute)\n            return ((PluralAttribute) argumentEntityAttribute).getElementType().getJavaType();\n\n        return argumentEntityAttribute.getJavaType();\n    }\n\n    private Attribute getAttribute(DataFetchingEnvironment environment, Argument argument) {\n        GraphQLObjectType objectType = getObjectType(environment, argument);\n        EntityType entityType = getEntityType(objectType);\n\n        return entityType.getAttribute(argument.getName());\n    }\n\n    private EntityType getEntityType(GraphQLObjectType objectType) {\n        return entityManager.getMetamodel().getEntities().stream().filter(it -> it.getName().equals(objectType.getName())).findFirst().get();\n    }\n\n    private GraphQLObjectType getObjectType(DataFetchingEnvironment environment, Argument argument) {\n        GraphQLType outputType = environment.getFieldType();\n        if (outputType instanceof GraphQLList)\n            outputType = ((GraphQLList) outputType).getWrappedType();\n\n        if (outputType instanceof GraphQLObjectType)\n            return (GraphQLObjectType) outputType;\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/crygier/graphql/annotation/GraphQLIgnore.java",
    "content": "package org.crygier.graphql.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.ElementType.TYPE;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\n@Target( { TYPE, FIELD })\n@Retention(RUNTIME)\npublic @interface GraphQLIgnore {\n}\n"
  },
  {
    "path": "src/main/java/org/crygier/graphql/annotation/SchemaDocumentation.java",
    "content": "package org.crygier.graphql.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.ElementType.TYPE;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\n@Target( { TYPE, FIELD })\n@Retention(RUNTIME)\npublic @interface SchemaDocumentation {\n\n    String value();\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/EmbeddedQueryExecutorTest.groovy",
    "content": "package org.crygier.graphql\n\nimport javax.persistence.EntityManager\n\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.boot.test.context.SpringBootContextLoader\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.test.context.ContextConfiguration\n\nimport spock.lang.Ignore\nimport spock.lang.Specification\n\n@Configuration\n@ContextConfiguration(loader = SpringBootContextLoader, classes = TestApplication)\nclass EmbeddedQueryExecutorTest extends Specification {\n\n\t@Autowired\n    private GraphQLExecutor executor;\n\t\n\tdef 'Query Embedded Values'() {\n\t\tgiven:\n\t\tdef query = '''\n        {\n            Spaceship (id: \"1000\"){\n                name, created { user {id}}, modified {date}\n            }\n        }\n        '''\n\t\tdef expected = [\n\t\t\t\tSpaceship: [[name: \"X-Wing\", created:[user:[id:\"1000\"]], modified:null]]\n\t\t]\n\n\t\twhen:\n\t\tdef result = executor.execute(query).data\n\n\t\tthen:\n\t\tresult == expected\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/EmbeddedSchemaBuildTest.groovy",
    "content": "package org.crygier.graphql\n\nimport graphql.schema.GraphQLObjectType\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.boot.test.context.SpringBootContextLoader\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.test.context.ContextConfiguration\nimport spock.lang.Specification\n\nimport javax.persistence.EntityManager\nimport javax.persistence.metamodel.EntityType\nimport java.util.stream.Collectors\n\n@Configuration\n@ContextConfiguration(loader = SpringBootContextLoader, classes = TestApplication)\nclass EmbeddedSchemaBuildTest extends Specification {\n\n    @Autowired\n    private EntityManager entityManager;\n\n    private GraphQLSchemaBuilder builder;\n\n    void setup() {\n        builder = new GraphQLSchemaBuilder(entityManager);\n    }\n\n    def 'Correctly read embedded keys'() {\n        when:\n        def embeddingEntity = entityManager.getMetamodel().getEntities().stream().filter { e -> e.name == \"EmbeddingTest\"}.findFirst().get()\n        def graphQlObject = builder.getObjectType(embeddingEntity)\n\n        then:\n        graphQlObject.fieldDefinitions.size() == 1\n    }\n\n    def 'Correctly extract embedded basic query fields'() {\n        when:\n        def embeddingEntity = entityManager.getMetamodel().getEntities().stream().filter { e -> e.name == \"EmbeddingTest\"}.findFirst().get()\n        def graphQlFieldDefinition = builder.getQueryFieldDefinition(embeddingEntity)\n\n        then:\n        graphQlFieldDefinition.arguments.size() == 0\n    }\n\n    def 'Correctly extract a whole moddel with embeddings'() {\n        when:\n        def q = builder.getQueryType()\n\n        then:\n        true\n    }\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/GraphQlController.groovy",
    "content": "package org.crygier.graphql\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport graphql.ExecutionResult\nimport groovy.transform.CompileStatic\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.web.bind.annotation.RequestBody\nimport org.springframework.web.bind.annotation.RequestMapping\nimport org.springframework.web.bind.annotation.RequestMethod\nimport org.springframework.web.bind.annotation.RestController\n\n@RestController\n@CompileStatic\nclass GraphQlController {\n\n    @Autowired\n    private GraphQLExecutor graphQLExecutor;\n\n    @Autowired\n    private ObjectMapper objectMapper;\n\n    @RequestMapping(path = '/graphql', method = RequestMethod.POST)\n    ExecutionResult graphQl(@RequestBody final GraphQLInputQuery query) {\n        Map<String, Object> variables = query.getVariables() ? objectMapper.readValue(query.getVariables(), Map) : null;\n\n        return graphQLExecutor.execute(query.getQuery(), variables);\n    }\n\n    public static final class GraphQLInputQuery {\n        String query;\n        String variables;\n    }\n\n}"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/JavaScalarsTest.groovy",
    "content": "package org.crygier.graphql\n\nimport graphql.schema.Coercing\nimport spock.lang.Specification\n\nimport java.time.Instant\nimport java.time.LocalDate\nimport java.time.LocalDateTime\nimport java.time.Month\nimport java.time.ZoneId\n\nclass JavaScalarsTest extends Specification {\n\n    def 'Long to LocalDateTime'() {\n        given:\n        Coercing coercing = JavaScalars.GraphQLLocalDateTime.getCoercing()\n\n        LocalDateTime localDateTime = LocalDateTime.of(2017, 02, 02, 12, 30, 15)\n        long input = localDateTime.toEpochSecond(ZoneId.systemDefault().getRules().getOffset(localDateTime))\n\n        when:\n        def result = coercing.serialize(input)\n\n        then:\n        result instanceof LocalDateTime\n\n        result.dayOfMonth == 2\n        result.month == Month.FEBRUARY\n        result.year == 2017\n        result.hour == 12\n        result.minute == 30\n        result.second == 15\n    }\n\n    def 'String to LocalDateTime'() {\n        given:\n        Coercing coercing = JavaScalars.GraphQLLocalDateTime.getCoercing()\n        final String input = \"2017-02-02T12:30:15\"\n\n        when:\n        def result = coercing.serialize(input)\n\n        then:\n        result instanceof LocalDateTime\n\n        result.dayOfMonth == 2\n        result.month == Month.FEBRUARY\n        result.year == 2017\n        result.hour == 12\n        result.minute == 30\n        result.second == 15\n    }\n\n    def 'Instant to Long'() {\n        given:\n        Coercing coercing = JavaScalars.GraphQLInstant.getCoercing()\n        final instant = Instant.now()\n\n        when:\n        def result = coercing.serialize(instant)\n\n        then:\n        result instanceof Long\n        result == instant.epochSecond\n    }\n\n    def 'Long to LocalDate'() {\n        given:\n        Coercing coercing = JavaScalars.GraphQLLocalDate.getCoercing()\n        LocalDateTime localDateTime = LocalDateTime.of(2017, 02, 02, 0, 0, 0)\n        long input = localDateTime.toEpochSecond(ZoneId.systemDefault().getRules().getOffset(localDateTime))\n\n        when:\n        def result = coercing.serialize(input)\n\n        then:\n        result instanceof LocalDate\n\n        result.dayOfMonth == 2\n        result.month == Month.FEBRUARY\n        result.year == 2017\n    }\n\n    def 'String to LocalDate'() {\n        given:\n        Coercing coercing = JavaScalars.GraphQLLocalDate.getCoercing()\n        final String input = \"2017-02-02\"\n\n        when:\n        def result = coercing.serialize(input)\n\n        then:\n        result instanceof LocalDate\n\n        result.dayOfMonth == 2\n        result.month == Month.FEBRUARY\n        result.year == 2017\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/MutableQueryExecutorTest.groovy",
    "content": "package org.crygier.graphql\n\nimport graphql.schema.GraphQLFieldDefinition\nimport graphql.schema.GraphQLObjectType\nimport graphql.schema.GraphQLSchema\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.boot.test.context.SpringBootContextLoader\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.test.context.ContextConfiguration\nimport spock.lang.Specification\n\nimport static graphql.Scalars.GraphQLString\n\n@Configuration\n@ContextConfiguration(loader = SpringBootContextLoader, classes = TestApplication)\nclass MutableQueryExecutorTest extends Specification {\n\n    @Autowired\n    private GraphQLExecutor executor;\n\n    private final GraphQLObjectType droidMutation = GraphQLObjectType.newObject()\n            .name(\"CreateDroidMutation\")\n            .field(GraphQLFieldDefinition.newFieldDefinition()\n            .name(\"name\")\n            .type(GraphQLString))\n            .build()\n\n    def 'Can add a schema mutation'() {\n        when:\n        GraphQLSchema.Builder builder = executor.getBuilder().mutation(droidMutation)\n        executor.updateSchema(builder)\n\n        then:\n        executor.getSchema().mutationType == droidMutation\n    }\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/MutableSchemaBuildTest.groovy",
    "content": "package org.crygier.graphql\n\nimport graphql.schema.GraphQLFieldDefinition\nimport graphql.schema.GraphQLObjectType\nimport graphql.schema.GraphQLSchema\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.boot.test.context.SpringBootContextLoader\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.test.context.ContextConfiguration\nimport spock.lang.Specification\n\nimport javax.persistence.EntityManager\n\nimport static graphql.Scalars.GraphQLString\n\n@Configuration\n@ContextConfiguration(loader = SpringBootContextLoader, classes = TestApplication)\nclass MutableSchemaBuildTest extends Specification {\n\n    @Autowired\n    private EntityManager entityManager;\n\n    private GraphQLSchema schema;\n\n    private final GraphQLObjectType droidMutation = GraphQLObjectType.newObject()\n            .name(\"CreateDroidMutation\")\n            .field(GraphQLFieldDefinition.newFieldDefinition()\n                .name(\"name\")\n                .type(GraphQLString))\n            .build()\n\n    void setup() {\n        schema = new GraphQLSchemaBuilder(entityManager).mutation(droidMutation).build()\n    }\n\n    def 'Can add a schema mutation'() {\n        expect:\n        schema.mutationType == droidMutation\n    }\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/StarwarsQueryExecutorTest.groovy",
    "content": "package org.crygier.graphql\n\nimport org.crygier.graphql.model.starwars.Episode\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.boot.test.context.SpringBootContextLoader\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.transaction.annotation.Transactional\nimport spock.lang.Specification\n\nimport javax.persistence.EntityManager\n\n@Configuration\n@ContextConfiguration(loader = SpringBootContextLoader, classes = TestApplication)\nclass StarwarsQueryExecutorTest extends Specification {\n\n    @Autowired\n    private GraphQLExecutor executor;\n\n    def 'Gets just the names of all droids'() {\n        given:\n        def query = '''\n        query HeroNameQuery {\n          Droid {\n            name\n          }\n        }\n        '''\n        def expected = [\n                Droid: [\n                        [ name: 'C-3PO' ],\n                        [ name: 'R2-D2' ]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result['Droid'].sort() == expected['Droid']\n    }\n\n    def 'Query for droid by name'() {\n        given:\n        def query = '''\n        {\n          Droid(name: \"C-3PO\") {\n            name\n            primaryFunction\n          }\n        }\n        '''\n        def expected = [\n                Droid: [\n                        [ name: 'C-3PO', primaryFunction: 'Protocol' ]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected\n    }\n\n    def 'ManyToOne Join by ID'() {\n        given:\n        def query = '''\n        {\n            Human(id: \"1000\") {\n                name\n                homePlanet\n                favoriteDroid {\n                    name\n                }\n            }\n        }\n        '''\n        def expected = [\n                Human: [\n                        [name:'Luke Skywalker', homePlanet:'Tatooine', favoriteDroid:[name:'C-3PO']]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected\n    }\n\t\n\tdef 'Nullable ManyToOne Join'() {\n\t\tgiven:\n\t\tdef query = '''\n        {\n            Human(id: \"1004\") {\n                name\n                homePlanet\n                favoriteDroid {\n                    name\n                }\n            }\n        }\n        '''\n\t\tdef expected = [\n\t\t\t\tHuman: [\n\t\t\t\t\t\t[name:'Wilhuff Tarkin', homePlanet:null, favoriteDroid:null]\n\t\t\t\t]\n\t\t]\n\n\t\twhen:\n\t\tdef result = executor.execute(query).data\n\n\t\tthen:\n\t\tresult == expected\n\t}\n\n    def 'OneToMany Join by ID'() {\n        given:\n        def query = '''\n        {\n            Human(id: \"1000\") {\n                name\n                homePlanet\n                friends {\n                    name\n                }\n            }\n        }\n        '''\n        def expected = [\n                Human: [\n                        [name: 'Luke Skywalker', homePlanet: 'Tatooine', friends: [[name: 'Han Solo'], [name: 'Leia Organa'], [name: 'C-3PO'], [name: 'R2-D2']]]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected\n    }\n\n    def 'Query with parameter'() {\n        given:\n        def query = '''\n        query humanQuery($id: String!) {\n            Human(id: $id) {\n                name\n                homePlanet\n            }\n        }\n        '''\n        def expected = [\n                Human: [\n                        [name: 'Darth Vader', homePlanet: 'Tatooine']\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query, [id: \"1001\"]).data\n\n        then:\n        result == expected\n    }\n\n    def 'Query with alias'() {\n        given:\n        def query = '''\n        {\n            luke: Human(id: \"1000\") {\n                name\n                homePlanet\n            }\n            leia: Human(id: \"1003\") {\n                name\n            }\n        }\n        '''\n        def expected = [\n                luke: [\n                        [name: 'Luke Skywalker', homePlanet: 'Tatooine'],\n                ],\n                leia: [\n                        [name: 'Leia Organa']\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected\n    }\n\n    def 'Allows us to use a fragment to avoid duplicating content'() {\n        given:\n        def query = \"\"\"\n        query UseFragment {\n            luke: Human(id: \"1000\") {\n                ...HumanFragment\n            }\n            leia: Human(id: \"1003\") {\n                ...HumanFragment\n            }\n        }\n        fragment HumanFragment on Human {\n            name\n            homePlanet\n        }\n        \"\"\"\n        def expected = [\n                luke: [\n                        [name: 'Luke Skywalker', homePlanet: 'Tatooine'],\n                ],\n                leia: [\n                        [name: 'Leia Organa', homePlanet: 'Alderaan']\n                ]\n        ]\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected\n    }\n\n    def 'Deep nesting'() {\n        given:\n        def query = '''\n        {\n            Droid(id: \"2001\") {\n                name\n                friends {\n                    name\n                    appearsIn\n                    friends {\n                        name\n                    }\n                }\n            }\n        }\n        '''\n        def expected = [\n                Droid:[\n                        [\n                                name:'R2-D2',\n                                friends:[\n                                        [ name:'Luke Skywalker', appearsIn:['A_NEW_HOPE', 'EMPIRE_STRIKES_BACK', 'RETURN_OF_THE_JEDI', 'THE_FORCE_AWAKENS'], friends:[['name:Han Solo'], [name:'Leia Organa'], [name:'C-3PO'], [name:'R2-D2']]],\n                                        [ name:'Han Solo', appearsIn:['A_NEW_HOPE', 'EMPIRE_STRIKES_BACK', 'RETURN_OF_THE_JEDI', 'THE_FORCE_AWAKENS'], friends:[[name:'Luke Skywalker'], [name:'Leia Organa'], [name:'R2-D2']]],\n                                        [ name:'Leia Organa', appearsIn:['A_NEW_HOPE', 'EMPIRE_STRIKES_BACK', 'RETURN_OF_THE_JEDI', 'THE_FORCE_AWAKENS'], friends:[[name:'Luke Skywalker'], [name:'Han Solo'], [name:'C-3PO'], [name:'R2-D2']]]\n                                ]\n                        ]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result.toString() == expected.toString()\n    }\n\n    def 'Pagination at the root'() {\n        given:\n        def query = '''\n        {\n            HumanConnection(paginationRequest: { page: 1, size: 2 }) {\n                totalPages\n                totalElements\n                content {\n                    name\n                }\n            }\n        }\n        '''\n        def expected = [\n                HumanConnection: [\n                        totalPages: 3,\n                        totalElements: 5,\n                        content: [\n                                [ name: 'Darth Vader' ],\n                                [ name: 'Luke Skywalker' ]\n                        ]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected\n    }\n\n    def 'Pagination without content'() {\n        given:\n        def query = '''\n        {\n            HumanConnection(paginationRequest: { page: 1, size: 2}) {\n                totalPages\n                totalElements\n            }\n        }\n        '''\n\n        def expected = [\n                HumanConnection: [\n                        totalPages: 3,\n                        totalElements: 5\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected\n    }\n\n    def 'Ordering Fields'() {\n        given:\n        def query = '''\n        {\n            Human {\n                name(orderBy: DESC)\n                homePlanet\n            }\n        }\n        '''\n        def expected = [\n                Human: [\n                    [ name: 'Wilhuff Tarkin', homePlanet: null],\n                    [ name: 'Luke Skywalker', homePlanet: \"Tatooine\"],\n                    [ name: 'Leia Organa', homePlanet: \"Alderaan\"],\n                    [ name: 'Han Solo', homePlanet: null],\n                    [ name: 'Darth Vader', homePlanet: \"Tatooine\"]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected\n    }\n\n    def 'Query by Collection of Enums at root level'() {\n        // Semi-proper JPA: select distinct h from Human h join h.appearsIn ai where ai in (:episodes)\n\n        given:\n        def query = '''\n        {\n          Human(appearsIn: [THE_FORCE_AWAKENS]) {\n            name\n            appearsIn\n          }\n        }\n        '''\n        def expected = [\n                Human: [\n                    [ name: 'Leia Organa', appearsIn: [Episode.A_NEW_HOPE, Episode.EMPIRE_STRIKES_BACK, Episode.RETURN_OF_THE_JEDI, Episode.THE_FORCE_AWAKENS] ],\n                    [ name: 'Luke Skywalker', appearsIn: [Episode.A_NEW_HOPE, Episode.EMPIRE_STRIKES_BACK, Episode.RETURN_OF_THE_JEDI, Episode.THE_FORCE_AWAKENS]],\n                    [ name: 'Han Solo', appearsIn: [Episode.A_NEW_HOPE, Episode.EMPIRE_STRIKES_BACK, Episode.RETURN_OF_THE_JEDI, Episode.THE_FORCE_AWAKENS] ]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected;\n    }\n\n    def 'Query by restricting sub-object'() {\n        given:\n        def query = '''\n        {\n          Human {\n            name\n            gender(code: \"Male\") {\n              description\n            }\n          }\n        }\n        '''\n        def expected = [\n                Human: [\n                        [ name: 'Darth Vader', gender: [ description: \"Male\" ] ],                       \n                        [ name: 'Luke Skywalker', gender: [ description: \"Male\" ]],\n                        [ name: 'Han Solo', gender: [ description: \"Male\" ] ],\n\t\t\t\t\t\t[ name: 'Wilhuff Tarkin', gender: [ description: \"Male\" ]]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected;\n    }\n\n    def 'Query for searching by IntType (sequence field)'() {\n        given:\n        def query = '''\n        {\n          CodeList(sequence: 2) {\n            id\n            description\n            active\n            type\n            sequence\n          }\n        }\n        '''\n        def expected = [\n            CodeList: [\n                [ id: 1, description: \"Female\", active: true, type: \"org.crygier.graphql.model.starwars.Gender\", sequence: 2]\n            ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected;\n    }\n\n    def 'Query for searching by BooleanType (active field)'() {\n        given:\n        def query = '''\n        {\n          CodeList(active: true) {\n            id\n            description\n            active\n            type\n            sequence\n          }\n        }\n        '''\n        def expected = [\n                CodeList: [\n                        [ id: 0, description: \"Male\", active: true, type: \"org.crygier.graphql.model.starwars.Gender\", sequence: 1],\n                        [ id: 1, description: \"Female\", active: true, type: \"org.crygier.graphql.model.starwars.Gender\", sequence: 2]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected;\n    }\n\n    @Autowired\n    private EntityManager em;\n\n    @Transactional\n    def 'JPA Sample Tester'() {\n        when:\n        //def query = em.createQuery(\"select h.id, h.name, h.gender.description from Human h where h.gender.code = 'Male'\");\n        def query = em.createQuery(\"select h, h.friends from Human h\");\n        //query.setParameter(1, Episode.THE_FORCE_AWAKENS);\n        //query.setParameter(\"episodes\", EnumSet.of(Episode.THE_FORCE_AWAKENS));\n        def result = query.getResultList();\n        //println JsonOutput.prettyPrint(JsonOutput.toJson(result));\n\n        then:\n        result;\n    }\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/StarwarsSchemaBuildTest.groovy",
    "content": "package org.crygier.graphql\n\nimport graphql.Scalars\nimport graphql.schema.GraphQLSchema\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.boot.test.context.SpringBootContextLoader\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.test.context.ContextConfiguration\nimport spock.lang.Specification\n\nimport javax.persistence.EntityManager\n\n@Configuration\n@ContextConfiguration(loader = SpringBootContextLoader, classes = TestApplication)\nclass StarwarsSchemaBuildTest extends Specification {\n\n    @Autowired\n    private EntityManager entityManager;\n\n    private GraphQLSchemaBuilder builder;\n\n    void setup() {\n        builder = new GraphQLSchemaBuilder(entityManager);\n    }\n\n    def 'Correctly derives the schema from Given Entities'() {\n        when:\n        GraphQLSchema schema = builder.getGraphQLSchema();\n\n        then:   \"Ensure the result is returned\"\n        schema;\n\n        then:   \"Ensure that collections can be queried on\"\n        schema.getQueryType().getFieldDefinition(\"Droid\").getArgument(\"appearsIn\")\n\n        then:   \"Ensure Subobjects may be queried upon\"\n        schema.getQueryType().getFieldDefinition(\"CodeList\").getArguments().size() == 6\n        schema.getQueryType().getFieldDefinition(\"CodeList\").getArgument(\"code\").getType() == Scalars.GraphQLString\n    }\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/TestApplication.groovy",
    "content": "package org.crygier.graphql\n\nimport groovy.transform.CompileStatic\nimport org.springframework.boot.SpringApplication\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration\nimport org.springframework.boot.autoconfigure.domain.EntityScan\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\n\n@Configuration\n@EnableAutoConfiguration\n@EntityScan\n@CompileStatic\nclass TestApplication {\n\n    public static void main(String[] args) {\n        ApplicationContext ac = SpringApplication.run(TestApplication.class, args);\n    }\n\n    @Bean\n    public GraphQLExecutor graphQLExecutor() {\n        return new GraphQLExecutor();\n    }\n\n    @Bean\n    public GraphQlController() {\n        return new GraphQlController();\n    }\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/ThingQueryExecutorTest.groovy",
    "content": "package org.crygier.graphql\n\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.boot.test.context.SpringBootContextLoader\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.test.context.ContextConfiguration\nimport spock.lang.Specification\n\n@Configuration\n@ContextConfiguration(loader = SpringBootContextLoader, classes = TestApplication)\nclass ThingQueryExecutorTest extends Specification {\n\n    @Autowired\n    private GraphQLExecutor executor\n\n    def 'Gets all things'() {\n        given:\n        def query = '''\n        query AllThingsQuery {\n          Thing {\n            id\n            type\n          }\n        }\n        '''\n        def expected = [\n                Thing: [\n                   [ id: UUID.fromString(\"2d1ebc5b-7d27-4197-9cf0-e84451c5bbb1\"), type:'Thing1' ]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected\n    }\n\n    def 'Query for thing by id'() {\n        given:\n        def query = '''\n        query ThingByIdQuery {\n          Thing(id: \"2d1ebc5b-7d27-4197-9cf0-e84451c5bbb1\") {\n            id\n            type\n          }\n        }\n        '''\n        def expected = [\n                Thing: [\n                        [ id: UUID.fromString(\"2d1ebc5b-7d27-4197-9cf0-e84451c5bbb1\"), type:'Thing1' ]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected\n    }\n\n    def 'Query with parameter'() {\n        given:\n        def query = '''\n       query ThingByIdQuery($id: UUID) {\n          Thing(id: $id) {\n            id\n            type\n          }\n        }\n        '''\n        def expected = [\n                Thing: [\n                        [ id: UUID.fromString(\"2d1ebc5b-7d27-4197-9cf0-e84451c5bbb1\"), type:'Thing1' ]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query, [id: \"2d1ebc5b-7d27-4197-9cf0-e84451c5bbb1\"]).data\n\n        then:\n        result == expected\n    }\n\n    def 'Query with alias'() {\n        given:\n        def query = '''\n        {\n         t1:  Thing(id: \"2d1ebc5b-7d27-4197-9cf0-e84451c5bbb1\") {\n            id\n            type\n          }\n        }\n        '''\n        def expected = [\n                t1: [\n                        [ id: UUID.fromString(\"2d1ebc5b-7d27-4197-9cf0-e84451c5bbb1\"), type:'Thing1' ]\n                ]\n        ]\n\n        when:\n        def result = executor.execute(query).data\n\n        then:\n        result == expected\n    }\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/collections/CollectionTest.java",
    "content": "package org.crygier.graphql.model.collections;\n\nimport groovy.transform.CompileStatic;\nimport org.crygier.graphql.annotation.SchemaDocumentation;\n\nimport javax.persistence.ElementCollection;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Entity\n@CompileStatic\npublic class CollectionTest {\n\n    //testing that the Schema Builder does not break\n    //when building collection of non-enum objects\n\n    @Id\n    @SchemaDocumentation(\"Primary Key for the CollectionTest Class\")\n    String id;\n\n    @SchemaDocumentation(\"A List of Strings\")\n    @ElementCollection(targetClass=String.class)\n    List<String> codas = new ArrayList<String>();\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/embeddings/EmbeddingId.java",
    "content": "package org.crygier.graphql.model.embeddings;\n\nimport org.crygier.graphql.model.starwars.Character;\nimport org.crygier.graphql.model.starwars.Episode;\n\nimport javax.persistence.Column;\nimport javax.persistence.Embeddable;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.ManyToOne;\nimport java.io.Serializable;\n\n@Embeddable\nclass EmbeddingId implements Serializable {\n\n    @ManyToOne\n    @JoinColumn(name = \"character_id\")\n    private Character character;\n\n    @Column(name = \"episode\")\n    private Episode episode;\n\n    @Column(name = \"age\")\n    private int age;\n\n    public EmbeddingId(Character character, Episode episode, int age) {\n        this.character = character;\n        this.episode = episode;\n        this.age = age;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        EmbeddingId that = (EmbeddingId) o;\n\n        if (age != that.age) return false;\n        if (character != null ? !character.equals(that.character) : that.character != null) return false;\n        return episode == that.episode;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = character != null ? character.hashCode() : 0;\n        result = 31 * result + (episode != null ? episode.hashCode() : 0);\n        result = 31 * result + age;\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/embeddings/EmbeddingTest.java",
    "content": "package org.crygier.graphql.model.embeddings;\n\nimport groovy.transform.CompileStatic;\nimport org.crygier.graphql.annotation.GraphQLIgnore;\nimport org.hibernate.annotations.Target;\n\nimport javax.persistence.EmbeddedId;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\n\n@Entity\n@CompileStatic\npublic class EmbeddingTest {\n\n    @EmbeddedId\n    private EmbeddingId embeddingId;\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/starwars/Character.groovy",
    "content": "package org.crygier.graphql.model.starwars\n\nimport groovy.transform.CompileStatic\nimport org.crygier.graphql.annotation.SchemaDocumentation\n\nimport javax.persistence.*\n\n@Entity\n@SchemaDocumentation(\"Abstract representation of an entity in the Star Wars Universe\")\n@CompileStatic\nabstract class Character {\n\n    @Id\n    @SchemaDocumentation(\"Primary Key for the Character Class\")\n    String id;\n\n    @SchemaDocumentation(\"Name of the character\")\n    String name;\n\n    @SchemaDocumentation(\"Who are the known friends to this character\")\n    @ManyToMany\n    @JoinTable(name=\"character_friends\",\n            joinColumns=@JoinColumn(name=\"source_id\", referencedColumnName=\"id\"),\n            inverseJoinColumns=@JoinColumn(name=\"friend_id\", referencedColumnName=\"id\"))\n    Collection<Character> friends;\n\n    @SchemaDocumentation(\"What Star Wars episodes does this character appear in\")\n    @ElementCollection(targetClass = Episode)\n    @Enumerated(EnumType.ORDINAL)\n    Collection<Episode> appearsIn;\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/starwars/CodeList.groovy",
    "content": "package org.crygier.graphql.model.starwars\n\nimport groovy.transform.CompileStatic\nimport org.crygier.graphql.annotation.SchemaDocumentation\n\nimport javax.persistence.Entity\nimport javax.persistence.Id\nimport javax.persistence.JoinColumn\nimport javax.persistence.ManyToOne\n\n@Entity\n@SchemaDocumentation(\"Database driven enumeration\")\n@CompileStatic\nclass CodeList {\n\n    @Id\n    @SchemaDocumentation(\"Primary Key for the Code List Class\")\n    Long id;\n\n    String type;\n    String code;\n    Integer sequence;\n    boolean active;\n    String description;\n\n    @ManyToOne\n    @JoinColumn(name = \"parent_id\")\n    CodeList parent;\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/starwars/Droid.groovy",
    "content": "package org.crygier.graphql.model.starwars\n\nimport groovy.transform.CompileStatic\nimport org.crygier.graphql.annotation.GraphQLIgnore\nimport org.crygier.graphql.annotation.SchemaDocumentation\n\nimport javax.persistence.Entity\n\n@Entity\n@SchemaDocumentation(\"Represents an electromechanical robot in the Star Wars Universe\")\n@CompileStatic\nclass Droid extends Character {\n\n    @SchemaDocumentation(\"Documents the primary purpose this droid serves\")\n    String primaryFunction;\n\n    @GraphQLIgnore\n    byte[] data;\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/starwars/Episode.java",
    "content": "package org.crygier.graphql.model.starwars;\n\npublic enum Episode {\n\n    PHANTOM_MENACE,\n    ATTACK_OF_THE_CLONES,\n    REVENGE_OF_THE_SITH,\n    A_NEW_HOPE,\n    EMPIRE_STRIKES_BACK,\n    RETURN_OF_THE_JEDI,\n    THE_FORCE_AWAKENS\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/starwars/Human.groovy",
    "content": "package org.crygier.graphql.model.starwars\n\nimport groovy.transform.CompileStatic\n\nimport javax.persistence.Entity\nimport javax.persistence.FetchType\nimport javax.persistence.JoinColumn\nimport javax.persistence.ManyToOne\n\n@Entity(name = \"Human\")\n@CompileStatic\npublic class Human extends Character {\n\n    String homePlanet;\n\n    @ManyToOne(fetch = FetchType.LAZY)\n    @JoinColumn(name = \"favorite_droid_id\")\n    Droid favoriteDroid;\n\n    @ManyToOne\n    @JoinColumn(name = \"gender_code_id\")\n    CodeList gender;\n\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/starwars/Spaceship.groovy",
    "content": "package org.crygier.graphql.model.starwars\n\nimport javax.persistence.*\n\nimport org.crygier.graphql.annotation.SchemaDocumentation\nimport org.crygier.graphql.model.users.DateAndUser\n\nimport groovy.transform.CompileStatic\n\n@Entity\n@SchemaDocumentation(\"Spaceships in the Star Wars Universe\")\n@CompileStatic\npublic class Spaceship {\n\n\t@Id\n\t@SchemaDocumentation(\"Primary Key for the Spaceship Class\")\n\tpublic String id;\n\n\t@SchemaDocumentation(\"Name of the spaceship\")\n\tString name;\n\t\n\t@Embedded\n\t@AttributeOverrides ([\n\t\t@AttributeOverride(name=\"date\",column=@Column(name=\"createddate\"))\t\n\t])\n\t@AssociationOverrides ([\n\t\t@AssociationOverride(name=\"user\",joinColumns=@JoinColumn(name=\"createduser\"))\n\t])\n\tpublic DateAndUser created;\n\t\n\t@Embedded\n\t@AttributeOverrides ([\n\t\t@AttributeOverride(name=\"date\",column=@Column(name=\"modifieddate\"))\n\t])\n\t@AssociationOverrides ([\n\t\t@AssociationOverride(name=\"user\",joinColumns=@JoinColumn(name=\"modifieduser\"))\n\t])\n\tDateAndUser modified;\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/users/DateAndUser.groovy",
    "content": "package org.crygier.graphql.model.users\n\nimport javax.persistence.Embeddable\nimport javax.persistence.ManyToOne\n\n@Embeddable\npublic class DateAndUser {\n\tpublic Date date;\n\t@ManyToOne\n\tpublic User user;\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/users/User.groovy",
    "content": "package org.crygier.graphql.model.users\n\nimport javax.persistence.Entity\nimport javax.persistence.Id\n\nimport org.crygier.graphql.annotation.SchemaDocumentation\n\nimport groovy.transform.CompileStatic\n\n@Entity\n@SchemaDocumentation(\"User who uses the application\")\n@CompileStatic\nclass User {\n\n\t@Id\n\t@SchemaDocumentation(\"Primary Key for the User Class\")\n\tString id;\n\t\n\tString firstName;\n\t\n\tString lastName;\n}\n"
  },
  {
    "path": "src/test/groovy/org/crygier/graphql/model/uuid/Thing.groovy",
    "content": "package org.crygier.graphql.model.uuid\n\nimport groovy.transform.CompileStatic\nimport org.crygier.graphql.annotation.SchemaDocumentation\n\nimport javax.persistence.Entity\nimport javax.persistence.Id\n\n@Entity\n@SchemaDocumentation(\"Database Thing with UUID field\")\n@CompileStatic\nclass Thing {\n\n    @Id\n    @SchemaDocumentation(\"Primary Key for the Thing Class\")\n    UUID id\n\n    String type\n}\n"
  },
  {
    "path": "src/test/resources/application.yaml",
    "content": "spring:\n  jpa:\n    hibernate.ddl-auto: create-drop\n    show-sql: true\n  h2:\n    console.enabled: true"
  },
  {
    "path": "src/test/resources/data.sql",
    "content": "-- Insert Code Lists\ninsert into code_list (id, type, code, description, sequence, active) values\n    (0, 'org.crygier.graphql.model.starwars.Gender', 'Male', 'Male', 1, true),\n    (1, 'org.crygier.graphql.model.starwars.Gender', 'Female', 'Female', 2, true);\n\n-- Insert Droids\ninsert into character (id, name, primary_function, dtype) values\n    ('2000', 'C-3PO', 'Protocol', 'Droid'),\n    ('2001', 'R2-D2', 'Astromech', 'Droid');\n\n-- Insert Humans\ninsert into character (id, name, home_planet, favorite_droid_id, dtype, gender_code_id) values\n    ('1000', 'Luke Skywalker', 'Tatooine', '2000', 'Human', 0),\n    ('1001', 'Darth Vader', 'Tatooine', '2001', 'Human', 0),\n    ('1002', 'Han Solo', NULL, NULL, 'Human', 0),\n    ('1003', 'Leia Organa', 'Alderaan', NULL, 'Human', 1),\n    ('1004', 'Wilhuff Tarkin', NULL, NULL, 'Human', 0);\n\n-- Luke's friends\ninsert into character_friends (source_id, friend_id) values\n    ('1000', '1002'),\n    ('1000', '1003'),\n    ('1000', '2000'),\n    ('1000', '2001');\n\n-- Luke Appears in\ninsert into character_appears_in (character_id, appears_in) values\n    ('1000', 3),\n    ('1000', 4),\n    ('1000', 5),\n    ('1000', 6);\n\n-- Vader's friends\ninsert into character_friends (source_id, friend_id) values\n    ('1001', '1004');\n\n-- Vader Appears in\ninsert into character_appears_in (character_id, appears_in) values\n    ('1001', 3),\n    ('1001', 4),\n    ('1001', 5);\n\n-- Solo's friends\ninsert into character_friends (source_id, friend_id) values\n    ('1002', '1000'),\n    ('1002', '1003'),\n    ('1002', '2001');\n\n-- Solo Appears in\ninsert into character_appears_in (character_id, appears_in) values\n    ('1002', 3),\n    ('1002', 4),\n    ('1002', 5),\n    ('1002', 6);\n\n-- Leia's friends\ninsert into character_friends (source_id, friend_id) values\n    ('1003', '1000'),\n    ('1003', '1002'),\n    ('1003', '2000'),\n    ('1003', '2001');\n\n-- Leia Appears in\ninsert into character_appears_in (character_id, appears_in) values\n    ('1003', 3),\n    ('1003', 4),\n    ('1003', 5),\n    ('1003', 6);\n\n-- Wilhuff's friends\ninsert into character_friends (source_id, friend_id) values\n    ('1004', '1001');\n\n-- Wilhuff Appears in\ninsert into character_appears_in (character_id, appears_in) values\n    ('1004', 3);\n\n-- C3PO's friends\ninsert into character_friends (source_id, friend_id) values\n    ('2000', '1000'),\n    ('2000', '1002'),\n    ('2000', '1003'),\n    ('2000', '2001');\n\n-- C3PO Appears in\ninsert into character_appears_in (character_id, appears_in) values\n    ('2000', 3),\n    ('2000', 4),\n    ('2000', 5),\n    ('2000', 6);\n\n-- R2's friends\ninsert into character_friends (source_id, friend_id) values\n    ('2001', '1000'),\n    ('2001', '1002'),\n    ('2001', '1003');\n\n-- R2 Appears in\ninsert into character_appears_in (character_id, appears_in) values\n    ('2001', 3),\n    ('2001', 4),\n    ('2001', 5),\n    ('2001', 6);\n\n-- Things\ninsert into thing (id, type) values\n    ('2D1EBC5B7D2741979CF0E84451C5BBB1', 'Thing1');\n\n-- User\ninsert into user(id, first_name, last_name) values\n\t('1000','Bob', 'Austin');\n\t\ninsert into spaceship(id, name, createddate,createduser) values\n\t('1000','X-Wing', sysdate, '1000');"
  },
  {
    "path": "src/test/resources/static/index.html",
    "content": "<!--\n *  Copyright (c) 2015, Facebook, Inc.\n *  All rights reserved.\n *\n *  This source code is licensed under the license found in the\n *  LICENSE file in the root directory of this source tree.\n *\n-->\n<!DOCTYPE html>\n<html>\n  <head>\n    <link href=\"//cdn.jsdelivr.net/graphiql/0.4.4/graphiql.css\" rel=\"stylesheet\">\n    <script src=\"//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js\"></script>\n    <script src=\"//cdn.jsdelivr.net/react/0.14.2/react.min.js\"></script>\n    <script src=\"//cdn.jsdelivr.net/react/0.14.2/react-dom.min.js\"></script>\n    <script src=\"//cdn.jsdelivr.net/graphiql/0.4.5/graphiql.min.js\"></script>\n  </head>\n  <body>\n    Loading...\n    <script>\n\n      /**\n       * This GraphiQL example illustrates how to use some of GraphiQL's props\n       * in order to enable reading and updating the URL parameters, making\n       * link sharing of queries a little bit easier.\n       *\n       * This is only one example of this kind of feature, GraphiQL exposes\n       * various React params to enable interesting integrations.\n       */\n\n      // Parse the search string to get url parameters.\n      var search = window.location.search;\n      var parameters = {};\n      search.substr(1).split('&').forEach(function (entry) {\n        var eq = entry.indexOf('=');\n        if (eq >= 0) {\n          parameters[decodeURIComponent(entry.slice(0, eq))] =\n            decodeURIComponent(entry.slice(eq + 1));\n        }\n      });\n\n      // if variables was provided, try to format it.\n      if (parameters.variables) {\n        try {\n          parameters.variables =\n            JSON.stringify(JSON.parse(parameters.variables), null, 2);\n        } catch (e) {\n          // Do nothing, we want to display the invalid JSON as a string, rather\n          // than present an error.\n        }\n      }\n\n      // When the query and variables string is edited, update the URL bar so\n      // that it can be easily shared\n      function onEditQuery(newQuery) {\n        parameters.query = newQuery;\n        updateURL();\n      }\n\n      function onEditVariables(newVariables) {\n        parameters.variables = newVariables;\n        updateURL();\n      }\n\n      function updateURL() {\n        var newSearch = '?' + Object.keys(parameters).map(function (key) {\n          return encodeURIComponent(key) + '=' +\n            encodeURIComponent(parameters[key]);\n        }).join('&');\n        history.replaceState(null, null, newSearch);\n      }\n\n      // Defines a GraphQL fetcher using the fetch API.\n      function graphQLFetcher(graphQLParams) {\n        return fetch(window.location.origin + '/graphql', {\n          method: 'post',\n          headers: {\n            'Accept': 'application/json',\n            'Content-Type': 'application/json',\n          },\n          body: JSON.stringify(graphQLParams),\n          credentials: 'include',\n        }).then(function (response) {\n          return response.json();\n        });\n      }\n\n      // Render <GraphiQL /> into the body.\n      ReactDOM.render(\n        React.createElement(GraphiQL, {\n          fetcher: graphQLFetcher,\n          query: parameters.query,\n          variables: parameters.variables,\n          onEditQuery: onEditQuery,\n          onEditVariables: onEditVariables\n        }),\n        document.body\n      );\n    </script>\n  </body>\n</html>\n"
  }
]