0

I am working on a project using grailsViews and jsonApi format. I am using Grails v3.3.9 and jsonViews v1.2.10.

So the views provides template support for generating jsonApi from your domain model, but I can't find the inverse that allows you to build a domain object (and tree) from data that's posted/patched.

I managed to get this clumsy version to work but it's a quick hack.

In essence I had to override the createResource mthod in RestfulController. What this does is grab the post body and parses it. It dips into the json and finds the attributes and tries to use the bindData for this simple attributes map.

Relationships are harder. I have to iterate over these and see if there's any children with data for each tag.

I then have to addTo<Tag> (for each entry if I can find an instance in the domain model to add to the collection. There are lots of problems with this code - but it does actually get around to persisting a new OrgRoleInstance.

//works - needs lots of improvement !! overwites, lookups etc
@Override
protected <T extends OrgRoleInstance> T createResource() {
    def instance = super.resource.newInstance()
    RequestFacade requestFacade = getObjectToBind()
    BufferedReader bodyReader = requestFacade.request.getReader()
    long bodyLength = requestFacade.request.getContentLengthLong()

    String jsonBody = bodyReader.text
    //String strippedBackJson = jsonBody.replaceAll("\\s+","")
    JsonSlurper slurper = new JsonSlurper()
    def body = slurper.parseText (jsonBody)
    def data = body.data
    String bodyDataType = body.data.type
    //json views  api seems to show the class starting in lower case - change to uppercase
    String dataType = convertFirstCharToUppercase (bodyDataType)
    Map attributes = body.data.attributes
    Map relationships = body.data.relationships

    //wrap in a try block
    //def jsonClassRef = Class.forName(toToBindString) - https://stackoverflow.com/questions/13215403/finding-a-class-reflectively-by-its-simple-name-alone
    //assume users know what they are doing ! - just sanity check simple name
    String resClassSimpleName = resource.getSimpleName()
    assert dataType == resClassSimpleName



    //needs a custom bindData
    bindData instance, attributes //getObjectToBind()
    //process any relationships and bind these
    relationships.each {tag, value ->
        def dataArray = value.data
        for (item in dataArray) {
            def jsonType = item['type']
            //convert first char to uppercase
            String type = convertFirstCharToUppercase (jsonType)
            def id = Long.parseLong (item['id'])

            //with type and id try and find in existing domain model
            def refEntity
            Class<?> domainClass = domainClassLookupByName (type)
            if (domainClass) {
                refEntity = domainClass.get (id)
            }
            //help cant overwrite foreign key - clone the mag?
            if (refEntity) {
                def prop = convertFirstCharToUppercase(tag)
                instance."addTo$prop" (refEntity)
                //if (refEntity.validate())
                    //refEntity.save (failOnError:true)
            }
            instance

        }

    }

    //bindData instance, relationships //getObjectToBind()
    instance
}

The post data looks like this (i pretty printed the output from the get action on another record end edited that.

Note think it's a defect - but the jsonapi rendering for type: does lowercase char for a type rather than capital case as you'd expect - I've had to compensate for that).

I was hoping that the grails team might have been doing something on this. If not I'll request a feature enhancement.

halfer
  • 19,824
  • 17
  • 99
  • 186
WILLIAM WOODMAN
  • 1,185
  • 5
  • 19
  • 36

1 Answers1

0

Well there are command objects, and technically speaking you can use Domain objects as command objects. I don't recommend using domain objects as command objects outside of a toy app though, because of the inherent security risks, which is why I think the is kind of no...

However I would suggest you use command objects, because they give you, basic verification, and document your controllers API, in a way. One note, I generally try to keep command objects light, not adding services, or do calls, for the same reasons you don't do those in domains, memory consumption, and db calls belong in services in transitions. Also I generally pass the params from a command object to a 'nicely' layed out API, unless the command objects represents, a Jon document, where that would be ovekill.

There is also a command plugin that to give command objects, more convention over configuration, like the other Grails artifacts. I wrote it, so you can take that with a grain of salt :)

virtualdogbert
  • 462
  • 3
  • 12
  • Ok i get the Command Object but the proble remains the same conceptually - the jsonviews/rest model as described works with the domain classes as the rendering needs to understand the relationsips across the model. However as implemented the json views can generate jsonApi formatted output (responding to a GET). However i can find no support for processing jsonApi body content as a POST into domain, or command objects. – WILLIAM WOODMAN Jan 25 '19 at 18:14
  • I think I'm missing something in your question then, what do you mean by, jsonApi body content? Can you give an example? If you just mean json data coming in the post body, that will automatically get bound to a command object in a controllers signature: http://docs.grails.org/latest/guide/theWebLayer.html#commandObjects – virtualdogbert Jan 25 '19 at 21:04