How to properly save updates to domain objects in Groovy/Grails - grails

I'm starting to touch the Groovy/Grails backend of my organization and am tasked with updating the User on our Document domain object. The problem is, after hitting the update endpoint from the frontend with the correct params attached, the backend responds with an unchanged Document object.
Here is the code:
if (requestParams.userEmail) {
def contact = User.findByEmail(requestParams.userEmail)
log.debug('Reading user found by passed email contact={} error={}',contact, contact.errors.allErrors.inspect())
if (!contact) {
response.status = 400
render WebserviceError.badInput as JSON
return
}
document.user = contact
document.user.save(flush: true)
}
document.save(flush: true)
render survey as JSON
The frontend returns a promise and I'm logging the promise response, and it shows an unchanged Document object with the same exact user attached. I don't receive a 400 so it looks like the contact is successfully found.
I tried adding flush:true to the user.save call and the document.save call and that did not help.
Are there any obvious wrongdoings in my code?

Well db operations should be in a service, not in a controller, using #Transactional, preferably the gorm version not the spring version. You shouldn't need to use flush: true. Then fron the service you can return to the controller, andrender as JSON.

You don’t state that you see the debug statement on the server indicating a found user, perhaps it’s never actually getting to this section?
I assume that the code provided is incomplete, as we don’t see that the survey being returned contains the document that’s being updated. And also the braces look unbalanced, as if there’s a control flow issue. (i.e. why are there 2 opening braces but 3 closing braces?)
I’d suggest that you use a debugger on your code to see how control is actually flowing. Most Java IDEs support easy debugging, essentially clicking the debug button rather than the run button. Set a number of breakpoints sprinkled through this code to catch requests and call the API endpoint from your frontend.

is Document the parent? User a child?
User.addTodocument(someUser)
then Document.merge()

Related

How to remove Ephemeral Messages

I'm trying to figure out the mechanism to post an ephemeral message to a user and then remove it and replace it with a message visible to all. Similar behavior to giphy in which the Slash Command shows an interactive ephemeral message and creates a channel message once the user decides which gif to send. I'm also curious about updating the ephemeral message. I assume this can be done by the response_url if we use an interactive ephemeral message.
I initially figured I'd just create a ephemeral message using chat.postEphemeral and then call chat.delete on it, but it seems chat.delete and chat.update can't be called on a message created using chat.postEphemeral.
The Slack message guidelines seems to suggest that a multi-step interactive flow should always be handled in an ephemeral way so that other channel user don't see all intermediate messages before the result but I'm having bad luck figuring out how to get rid of the ephemeral when done. Probably just being bad at reading but any help appreciated.
Edit with more details:
The documentation around using response_url and postEphemeral states
As you replace messages using chat.update or the replace_original
option, you cannot change a message's type from ephemeral to
in_channel. Once a message has been issued, it will retain its
visibility quality for life.
The message guidelines suggest:
If a user has initiated an action that has multiple steps, those steps
should be shown as ephemeral messages visible only to that user until
the entire action is complete to avoid cluttering the channel for
everyone.
Presumably, I should be able to create an interaction in which I first send an in_channel interactive message.
When a user initiates an action, I should be able to send them a series of ephemeral messages using the response_url and passing response_type: 'ephemeral' and replace_original: false?
A new ephemeral interactive message created this way will have its own response_url for making edits, right?
Once I am done with the interactive flow via ephemeral messages, I can modify the original interactive message using its original response_url?
Lastly, how do I get rid of the last ephemeral edit? Or do I just change it to something like "Workflow completed" and hope for the best? I'm asking because Slash commands obviously seem to have a way to essentially replace the ephemeral message for an in_channel message and I'm trying to figure this kind of workflow out.
I searched high and low on how to do this and finally came across the answer.
Your ephemeral message must trigger an action, i.e. button click.
Your response to the action must use the following body
{
'response_type': 'ephemeral',
'text': '',
'replace_original': true,
'delete_original': true
}
'delete_original': true is the key here, which as far as I can tell is not mentioned in any of the API guides, however it is present in the API field guide under Top-level message fields
If you wish to change the response_type of your message instead of deleting it, you must do so by first deleting the ephemeral message and then posting the same message with 'response_type': 'in_channel'.
In my use case I wanted to take an ephemeral message and repost it with the exact same message body as an in-channel message. I have not found a way to retrieve the content of your ephemeral message, so the best method I've found is to pass whatever necessary data spawned your ephemeral message in the button's value so that your action handler can read this data and dynamically recreate the message body.
In my case, this was the user input being used to perform a query. On the off chance that data in the database changes between the time the original ephemeral message is posted and the in-channel version is posted they will be different. You may be able to send a JSON string directly through this value field and avoid making additional database calls and running the risk of messages changing when posted to the channel. The character limit of value is 2000 so JSON passing is extremely limited.
Assuming you use the same code to generate this body when initially creating the ephemeral message and also when recreating it in-channel, you should receive the same body and essentially are able to change an ephemeral message to in-channel message.
Some ephemeral messages can be "soft" deleted/replaced but only when posted as part of a message with interactive features like buttons or menus. When a button is clicked or a menu selection made, you have a chance to instruct Slack to either "delete" the original message, or replace it with a new one. These docs detail using responses and response_url to accomplish that.
A message created with chat.postEphemeral that itself has no interactive features can never be explicitly deleted. Once it's delivered, it's like a ghost and will disappear following a restart or refresh.
Answering your bulleted questions in order:
Correct, you essentially start a new chain of interactivity with net new ephemeral message you post to that user
Each interactive message interaction will have its own response URL. The new ephemeral message won't have a response_url you can use until the end user presses a button, selects a menu item, etc.
response_url will eventually expire ("using the response_url, your app can continue interacting with users up to 5 times within 30 minutes of the action invocation.") If the original message is non-ephemeral, using chat.update is a better strategy for longer timelines. With ephemeral messages, it's more of a "do your best" strategy. They'll eventually get cleaned up for the user after a refresh.
I think you have a good handle on what's best. Personally, I think it's easier to kick off a new "in_channel" message by using chat.postMessage instead of as a chain effect directly from a slash command or interaction.
The Kotlin/Java version for this solution using the Bolt API as shown below
import com.slack.api.bolt.handler.builtin.BlockActionHandler
import com.slack.api.bolt.request.builtin.BlockActionRequest
import com.slack.api.app_backend.interactive_components.response.ActionResponse
import com.slack.api.bolt.response.Response
import com.slack.api.bolt.context.builtin.ActionContext
object Handler : BlockActionHandler {
override fun apply(req: BlockActionRequest,
context: ActionContext): Response {
val response = ActionResponse
.builder()
.deleteOriginal(true)
.replaceOriginal(true)
.responseType("ephemeral")
.blocks(listOf())
.text("")
.build()
context.respond(response)
return context.ack()
}
}
If you are using Python and Flask the following code should work when you respond to a button click in the ephemeral message:
from flask import jsonify
response = jsonify({
'response_type': 'ephemeral',
'text': '',
'replace_original': 'true',
'delete_original':'true'
})
return make_response(response, 200)

Benefits of Post-Redirect-Get

When I started with ZF2 the first module I used was ZfcUser. When I debug it's controller's code I found a weird way (at least for me) to manage actions. I found code like
$prg = $this->prg('zfcuser/changepassword');
if ($prg instanceof Response) {
return $prg;
} elseif ($prg === false) {
return array(
'status' => $status,
'changePasswordForm' => $form,
);
}
//VALIDATE FORM AND DATABASE STUFF
(...)
The behaviour is as follows:
The first load $prg is false, so it returns the form.
When you submit the page, $prg is an instance of Response, so it returns $prg.
When $prg is returned, the same function is called again and $prg becomes an array with all the posted data, so it jumps to the validation of form and database stuff.
I thought it was a weird approach so I override all the needed functions replacing this with the simple request->isPost(). I found it easier to handle the first load/data posted.
I didn't give it more importance until now. I'm facing the Post-Redirect-Get approach again when I'm trying to upload files: it seems that is needed to prevent user to re-select the file and re-upload when a validation error rises on a form.
What's the point of the Post-Redirect-Get? When do you recommend the use of it (apart of the commented file upload)?
As the documentation states:
When a user sends a POST request (e.g. after submitting a form), their browser will try to protect them from sending the POST again, breaking the back button, causing browser warnings and pop-ups, and sometimes reposting the form. Instead, when receiving a POST, we should store the data in a session container and redirect the user to a GET request.
So the purpose of this plugin is to improve user experience. You must have came across this problem when you submit a form and try to refresh the page you get a pop-up message like (example from google chrome):
Confirm Form Resubmission: The page that you're looking for used information that you entered. Returning to that page might cause any action you took to be repeated. Do you want to continue?
You can get more details in the docs for Post/Redirect/Get Plugin, or File Post/Redirect/Get Plugin if your form handles files uploads.
NOTE: For the File Post/Redirect/Get Plugin - Example Usage there's a typo on line 16, you should use $this->filePrg() instead of $this->prg(). It should be like the line below.
$prg = $this->filePrg($myForm, '/user/profile-pic', true);

method timeout instead of waiting

It would appear that the savechanges method on breeze waits indefinitely when calling to or waiting for the server. Is there a way of getting it to time out? I am calling save change with allowConcurrentSaves: false. This now causes users who somehow do not get a response from the server to simply hang in limbo indefinitely say for example with a dropped internet connection.
I do not want to re-call the method with allowConcurrentSaves to false fearing that I might duplicate the data.
Any ideas?
Thanks
Update 16 May 2014
You can set HTTP-level timeout and cancellation with the AJAX Adapter's requestInterceptor as of v.1.4.12. See the documentation, "Controlling AJAX calls".
I'd still be reluctant to use this feature on save as you have no chance of knowing what whether the server persisted the data or not. Of course if your client hangs or crashes you don't know anyway. It's up to you.
Original Answer
Actually, there is a ready-made solution from Q.js. It's called timeout and it's mentioned in the API reference with a simplified example of its implementation and use in the readme.md.
I know you asked about Save but your question is pertinent for promises in general. Here is a query example adapted from the queryTests.js in our DocCode Sample
var timeoutMs = 10000; // 10 second timeout
var em = newEm(); // creates a new EntityManager
var query = new EntityQuery().from("Customers").using(em);
Q.timeout(query.execute, timeoutMs)
.then(queryFinishedBeforeTimeout)
.fail(queryFailedOrTimedout);
function queryFailedOrTimedout(error) {
var expect = /timed out/i;
var emsg = error.message;
if (expect.test(emsg)) {
log("Query timed out w/ message '{0}' " + expectTimeoutMsg)
.format(emsg));
// do something
} else {
handleFail(error);
}
}
Note: I just added this test so you'd have to get if from github or wait for a Breeze release after 1.2.5.
Oops ... maybe not
I gave what I think is a great answer for query. It may not be the right answer for save.
The problem with save is that you do not know on the client if the save succeeded until the server responds. Things could go wrong anywhere along the way. The server might not have heard the request to save. The server may have failed during save. The server may have saved the data but the response never made it back to the client.
Changing the value of allowConcurrentSaves won't get you out of this bind. Neither will having a save timeout.
In fact, adding a timeout to the save is probably deceiving. It is even possible for the save response to arrive after your custom timeout ... in which case Breeze will have tried to update your EntityManager ... and you won't know if Breeze succeeded or failed!
What if we added a Breeze save timeout. What should it do? What if breeze said the save had timedout ... and Breeze ignored a belated response from the server? Then imagine that the save succeeded on the server - it just took "too long" for it to respond to the client. Now you've got a client whose state is unexpectedly out of sync with the server. This is not good.
So I think you want a different solution to this very real problem. It's a user experience problem really. You can indicate to the user that you think the save is still in progress and then set your own timer. If the save isn't done when your timer expires, you can query the server to see if the data have been saved or if there is a connection ... or something along these lines. I can't think of a better way right now honestly.
Note that I'm assuming you need to know that the server succeeded. If you avoid store-generated IDs and always assume saves succeed unless the server tells you otherwise ... well that's a completely different paradigm and programming model that we could talk about someday (see meteorjs).
The net of all of this: I'm pretty darned sure that a save timeout is NOT what you want.
Still useful on a query though :)
Great question, and I wish I had a good answer. But it is definitely worth looking into. Could you please add this as a feature request to the Breeze User Voice. We take these requests very seriously in determining our priorities for Breeze development.

Rails validation fails but object memory remains unchanged?

First time user long time reader. I have thoroughly looked for an explanation for the problem I'm having via the mighty search engine Google, but alas I have failed to produce any significant insight.
I need to be able to ensure that a model form is not reloaded with invalid data. Since the model stored in memory on the server is edited directly with the parameters of the web form first, and THEN checked for validity, without additional code invalid model data will ALWAYS be sent back to the form. This is less than desirable to me. My question is this: how do I ensure this doesn't happen?
What I'm thinking is I need some mechanism for saving the state of the object before it's modified with the parameters sent from the web form, and then after a failed validation restore the object to it's previous, correct and unmodified state of being.
Help!
Thanks,
Les
The object isn't actually modified in the db if validation fails, even though the object is in an invalid state in the form ... the thinking behind this is that the user wants to see the errors they made so they can correct them.
If you don't want that to be the case, then just read back the object with a WhateverObject.find(x) and assign it to the variable that the form is referencing and it will 'restore' the object to its previous unmodified state.
To add to what concept47 said you can also get the value for a particular field using
object.field_was
Have a look at ActiveRecord::Dirty for details (http://ar.rubyonrails.org/classes/ActiveRecord/Dirty.html)
Using that you could retrieve the original values for just those fields that had validation errors.

How do I associate all logs with their request in grails?

In our grails application we're logging a lot, but need a mechanism to associate all of those messages with the request/response being processed. It has proven easy enough to generate a request UUID, but now I'd like that id appended to each log message generated within a request context without passing that id within each log message. Has anybody implemented such a system so that you can associate all of your log statements together?
A rather obscure feature of log4j, called MDC seems to be exactly what you need.
Something like http://gustlik.wordpress.com/2008/07/05/user-context-tracking-in-log4j/
It will work fine in Grails as well if you use a custom AppFilter to set the request-unique value.
you could try utilizing the RequestContextHolder
http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/context/request/RequestContextHolder.html
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
log.debug attr.getRequest().getSession()
Once you get the session object, you can get whatever identifier you have stashed away?

Resources