Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Graphql on a @GrailsCompileStatic domain class cause nullable constraint to be ignored #55

Open
magnomp opened this issue Nov 5, 2020 · 1 comment
Assignees

Comments

@magnomp
Copy link

magnomp commented Nov 5, 2020

If you have a domain class that is marked with @GrailsCompileSatic and this class has a property with a constraint nullable: true, and if this class is enabled for GraphQL with a custom operation/query, then the nullable: true constraint behaves like nullable: false.

Steps to reproduce:

  1. Create a new project (Tested with Grails 4.0.4 / 4.0.5 and their default gorm version)
    Add compile "org.grails.plugins:gorm-graphql-plugin:2.0.0" dependency

  2. Create a domain class, say MyDomain marked with @GrailsCompileStatic and a nullable property, like:

@GrailsCompileStatic
class MyDomain {
    String name

    static constraints = {
        name nullable: true
    }
}
  1. Add a custom GraphQL mapping which defines a simple query:
@GrailsCompileStatic
class MyDomain {
    String name

    static constraints = {
        name nullable: true
    }

    static graphql = GraphQLMapping.build {
        query("myQuery", Integer) {
            dataFetcher(new DataFetcher() {
                @Override
                Object get(DataFetchingEnvironment environment) throws Exception {
                    return new Integer(0)
                }
            })
        }
    }
}

If you just put static graphql=true or static graphql = GraphQLMapping.build {} there's no issue.

  1. Now in BootStrap, you just create and save a new instance of that class leaving the nullable field empty, like:
class BootStrap {

    def init = { servletContext ->
        new MyDomain().save(failOnError: true)
    }
    def destroy = {
    }
}
  1. Now you run with gradlew bootRun. It is expected the instance to be persisted with no errors, because it has only one field, which is null, and that field allows null. Instead, you see a validation exception:
grails.validation.ValidationException: Validation Error(s) occurred during save():
- Field error in object 'test.MyDomain' on field 'name': rejected value [null]; codes [test.MyDomain.name.nullable.error.test.MyDomain.name,test.MyDomain.name.null
able.error.name,test.MyDomain.name.nullable.error.java.lang.String,test.MyDomain.name.nullable.error,myDomain.name.nullable.error.test.MyDomain.name,myDomain.name.
nullable.error.name,myDomain.name.nullable.error.java.lang.String,myDomain.name.nullable.error,test.MyDomain.name.nullable.test.MyDomain.name,test.MyDomain.name.nu
llable.name,test.MyDomain.name.nullable.java.lang.String,test.MyDomain.name.nullable,myDomain.name.nullable.test.MyDomain.name,myDomain.name.nullable.name,myDomain
.name.nullable.java.lang.String,myDomain.name.nullable,nullable.test.MyDomain.name,nullable.name,nullable.java.lang.String,nullable]; arguments [name,class test.My
Domain]; default message [O campo [{0}] da classe [{1}] nÒo pode ser vazio]

        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:80)
        at org.codehaus.groovy.reflection.CachedConstructor.doConstructorInvoke(CachedConstructor.java:74)
        at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1732)
        at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1556)
        at org.codehaus.groovy.runtime.InvokerHelper.invokeConstructorOf(InvokerHelper.java:1042)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.newInstance(DefaultGroovyMethods.java:17159)
        at org.grails.orm.hibernate.AbstractHibernateGormInstanceApi.save(AbstractHibernateGormInstanceApi.groovy:134)
        at org.grails.datastore.gorm.GormEntity$Trait$Helper.save(GormEntity.groovy:153)
        at test.MyDomain.save(MyDomain.groovy)
        at test.MyDomain.save(MyDomain.groovy)
        at org.grails.datastore.gorm.GormEntity$save.call(Unknown Source)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:127)
        at testconstraintnotnull.BootStrap$_closure1.doCall(BootStrap.groovy:10)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:101)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323)
        at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:263)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1041)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1099)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1041)
        at groovy.lang.Closure.call(Closure.java:405)
        at groovy.lang.Closure.call(Closure.java:399)
        at grails.util.Environment.evaluateEnvironmentSpecificBlock(Environment.java:594)
        at grails.util.Environment.executeForEnvironment(Environment.java:587)
        at grails.util.Environment.executeForCurrentEnvironment(Environment.java:563)
        at org.grails.web.servlet.boostrap.DefaultGrailsBootstrapClass.callInit(DefaultGrailsBootstrapClass.java:74)
        at org.grails.web.servlet.context.GrailsConfigUtils.executeGrailsBootstraps(GrailsConfigUtils.java:83)
        at org.grails.plugins.web.servlet.context.BootStrapClassRunner.onStartup(BootStrapClassRunner.groovy:56)
        at grails.boot.config.GrailsApplicationPostProcessor.onApplicationEvent(GrailsApplicationPostProcessor.groovy:269)
        at grails.boot.config.GrailsApplicationPostProcessor.onApplicationEvent(GrailsApplicationPostProcessor.groovy)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
        at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:403)
        at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:360)
        at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:897)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:162)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:744)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:391)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:312)
        at grails.boot.GrailsApp.run(GrailsApp.groovy:99)
        at grails.boot.GrailsApp.run(GrailsApp.groovy:485)
        at grails.boot.GrailsApp.run(GrailsApp.groovy:472)
        at testconstraintnotnull.Application.main(Application.groovy:11)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)

Now if you either remove @GrailsCompileStatic or remove the custom query or remove graphql at all, the app just runs

I uploaded a semple here

@puneetbehl puneetbehl self-assigned this Nov 5, 2020
@jamesdh
Copy link
Contributor

jamesdh commented Nov 10, 2020

This appears to be the same issue as #48

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants