I built a simple plugin that shows in the CRMContainer the url of my CRM given some attributes parameters (if they are passed by), during inbound tasks this works fine, but the problem is that during outbound calls the behaviour is not the one expected, this is the piece of code:
flex.CRMContainer.defaultProps.uriCallback = (task) => {
return task
? `https://mycrm.zzz/${task.attributes.clicar}/${task.attributes.contacth}/`
: 'https://mycrm.zzz/contacts/';
}
}
I would need an additional condition that tells the code, if this is an outbound voice call to always show a default url.
I tried adding an if/else that checks if task.attributes.direction is outbound, but Flex says this is undefined.
Any tip?
Thanks
Max
The problem is that you aren't checking for the existence of the task. Your original code had this:
flex.CRMContainer.defaultProps.uriCallback = (task) => {
return task
? `https://mycrm.zzz/${task.attributes.clicar}/${task.attributes.contacth}/`
: 'https://mycrm.zzz/contacts/';
}
}
Which returns the URL with the task attributes in it only if the task exists, because of the ternary conditional.
So, when you try to use the attributes you need to make sure the task exists. So taking your code from the last comment, it should look like this:
flex.CRMContainer.defaultProps.uriCallback = (task) => {
if (task) {
if (task.attributes.direction === 'outbound'){
return `https://mycrm.zzz/${task.attributes.clicar}/${task.attributes.contacth}/`;
} else {
return `https://mycrm.zzz/contacts/`
}
} else {
return 'https://mycrm.zzz/contacts/';
}
}
Related
Because of the very specific nature of this question, I could not find an answer anywhere. Basically I want to create a messaging conversation with a specific user, but only if a conversation with that user doesn't already exist. I am looping through an array of conversations, and for each conversation I fetch the identity of the other user via a call to my backend. However, if no conversation is found with a particular user, then I want to create a new conversation. This is what I am doing:
for convo in convos {
HTTPManager.getOtherUserFromConversation(conversation: convo, success: { (otherUser) in
if desiredUser == otherUser {
//Found the desired conversation, so bring the user
//to it instead of creating a new one
}
}, failure: {
//Networking failure
})
}
//IF WE DIDN'T FIND IT, CREATE A NEW CONVERSATION HERE
I have thought of making a boolean value called "found" and setting it to true if we find the desired conversation, but I don't know how to wait until the last callback has executed before checking this boolean in order to avoid calling the check too early. Can anyone point me in the right direction?
The classic solution for this is using dispatch-group
https://developer.apple.com/documentation/dispatch/dispatchgroup
There are many code examples for this. The idea is that each network call should be in a separate task and the system lets you know when all tasks are done (this is where you check "found").
How about create callback function that will called when the all the request to check the convos is done, or when the correct convo is found.
func checkConvoIfExist(convos: [Convo]){
var found = false
var countCheck = 0
for convo in convos {
HTTPManager.getOtherUserFromConversation(conversation: convo, success: { (otherUser) in
countCheck += 1
if desiredUser == otherUser {
//Found the desired conversation
found = true
callbackCheckConvo(result: found, convo: convo)
break // to stop the loop
}else{
if (countCheck == convos.count){
callbackCheckConvo(result: found)
}
}
}, failure: {
//Networking failure
countCheck += 1
if (countCheck == convos.count){
callbackCheckConvo(result: found)
}
})
}
}
func callbackCheckConvo(result: Bool, convo: Convo = nil){
if (result){
//found the desired conversation, so bring the user to it instead of creating a new one
}else{
//convo not found, create new one
}
}
We have a Jenkins pipeline script that requests approval from the user after all the preparatory steps are complete, before it actually applies the changes.
We want to add a timeout to this step, so that if there is no input from the user then the build is aborted, and are currently working on using this kind of method:
try {
timeout(time: 30, unit: 'SECONDS') {
userInput = input("Apply changes?")
}
} catch(err) {
def user = err.getCauses()[0].getUser()
if (user.toString == 'SYSTEM') { // if it's system it's a timeout
didTimeout = true
echo "Build timed out at approval step"
} else if (userInput == false) { // if not and input is false it's the user
echo "Build aborted by: [${user}]"
}
}
This code is based on examples found here: https://support.cloudbees.com/hc/en-us/articles/226554067-Pipeline-How-to-add-an-input-step-with-timeout-that-continues-if-timeout-is-reached-using-a-default-value and other places online, but I really dislike catching all errors then working out what's caused the exception using err.getCauses()[0].getUser(). I'd rather explicitly catch(TimeoutException) or something like that.
So my question is, what are the actual exceptions that would be thrown by either the approval step timing out or the userInput being false? I haven't been able to find anything in the docs or Jenkins codebase so far about this.
The exception class they are referring to is org.jenkinsci.plugins.workflow.steps.FlowInterruptedException.
Cannot believe that this is an example provided by CloudBeeds.
Most (or probably all?) other exceptions won't even have the getCauses() method which of course would throw another exception then from within the catch block.
Furthermore as you already mentioned it is not a good idea to just catch all exceptions.
Edit:
By the way: Scrolling further down that post - in the comments - there you'll find an example catching a FlowInterruptedException.
Rather old topic, but it helped me, and I've done some more research on it.
As I figured out, FlowInterruptedException's getCauses()[0] has .getUser() only when class of getCauses()[0] is org.jenkinsci.plugins.workflow.support.steps.input.Rejection. It is so only when timeout occured while input was active. But, if timeout occured not in input, getCause()[0] will contain object of another class: org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution$ExceededTimeout (directly mentioning timeout).
So, I end up with this:
def is_interrupted_by_timeout(org.jenkinsci.plugins.workflow.steps.FlowInterruptedException e, Boolean throw_again=true) {
// if cause is not determined, re-throw exception
try {
def cause = e.getCauses()[0]
def cause_class = cause.getClass()
//echo("cause ${cause} class: ${cause_class}")
if( cause_class == org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution$ExceededTimeout ) {
// strong detection
return true
} else if( cause_class == org.jenkinsci.plugins.workflow.support.steps.input.Rejection ) {
// indirect detection
def user = cause.getUser()
if( user.toString().equals('SYSTEM') ) {
return true
} else {
return false
}
}
} catch(org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException e_access) {
// here, we may deal with situation when restricted methods are not approved:
// show message and Jengins' admin will copy/paste and execute them only once per Jenkins installation.
error('''
To run this job, Jenkins admin needs to approve some Java methods.
There are two possible ways to do this:
1. (better) run this code in Jenkins Console (URL: /script):
import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval;
def scriptApproval = ScriptApproval.get()
scriptApproval.approveSignature('method org.jenkinsci.plugins.workflow.steps.FlowInterruptedException getCauses')
scriptApproval.approveSignature('method org.jenkinsci.plugins.workflow.support.steps.input.Rejection getUser')
scriptApproval.save()
'''.stripIndent())
return null
}
if( throw_again ) {
throw e
} else {
return null
}
}
And now, you may catch it with something like this:
try {
...
} catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException err) {
if( is_interrupted_by_timeout(err) ) {
echo('It is timeout!')
}
}
P.S. I agree, this is bad Jenkins design.
I'm using spray-routing with Akka to define a route like
def items = path("items") {
get {
complete {
actor.ask(GetItems)(requestTimeout).mapTo[Either[NoChange, Items]] map {
result => result match {
case Left(_) => StatusCodes.NotModified
case Right(items) =>
// here I want to set an HTTP Response header based on a
// field within items -- items.revision
items
}
}
}
}
}
The actor.ask returns a Future that gets mapped to a Future[Either[NoChange, Items]]. "complete" is happy to deal with the Future[StatusCodes...] or the Future[Items] but I'm not sure how to set an HTTP Response header within the Future.
If the header weren't being set within the Future then I could just wrap the complete in a directive but how do I set a header within the complete?
I'm using Spray 1.2.0.
Thanks for any pointers in the right direction!
If you are trying to do this inside of complete all branches of the expression inside must result in a type that can be marshalled by complete.
You could try a structure like this to make it work:
complete {
actor.ask(GetItems)(requestTimeout).mapTo[Either[NoChange, Items]] map {
result => result match {
case Left(_) => StatusCodes.NotModified: ToResponseMarshallable
case Right(items) =>
// here I want to set an HTTP Response header based on a
// field within items -- items.revision
val headers = // items...
HttpResponse(..., headers = headers): ToResponseMarshallable
}
}
}
This ensures that the type of the expression you pass to complete is Future[ToResponseMarshallable] which should always be marshallable.
A better way, though, is to use the onSuccess directive that lets you use other directives after a future was completed:
get {
def getResult() = actor.ask(GetItems)(requestTimeout).mapTo[Either[NoChange, Items]]
onSuccess(getResult()) {
case Left(_) => complete(StatusCodes.NotModified)
case Right(items) =>
// do whatever you want, e.g.
val extraHeaders = // items.revisions
respondWithHeaders(extraHeaders) {
complete(...)
}
}
}
Following is a code fragment obtained from Grails website.
<script>
function messageKeyPress(field,event) {
var theCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode;
var message = $('#messageBox').val();
if (theCode == 13){
<g:remoteFunction action="submitMessage" params="\'message=\'+message" update="temp"/>
$('#messageBox').val('');
return false;
} else {
return true;
}
}
function retrieveLatestMessages() {
<g:remoteFunction action="retrieveLatestMessages" update="chatMessages"/>
}
function pollMessages() {
retrieveLatestMessages();
setTimeout('pollMessages()', 5000);
}
pollMessages();
</script>
The above code worked but when i added the Controller it stopped working. I meant that the records gets saved in the DB, but i am not able to retrieve the data and display on screen.
This is what i did
<g:remoteFunction controller="message" action="retrieveLatestMessages" update="chatMessages"/>
The MessageController function is as follows:
#Secured([ 'ROLE_USER'])
def retrieveLatestMessages() {
println "test"
def messages = Message.listOrderByDate(order: 'desc', max:1000)
[messages:messages.reverse()]
println messages
}
The above controller function gets executed (I see the println statements on console), but the data isn't getting populating on the screen.
Can someone help me out here
UPDATE
[{"class":"myPro.Message","id":3,"date":"2014-07-23T17:31:58Z","message":"dfdf","name":"hi"},{"class":"myPro.Message","id":2,"date":"2014-07-23T17:31:56Z","message":"dfdfdf","name":"dd"},{"class":"myPro.Message","id":1,"date":"2014-07-23T17:31:18Z","message":"xxxx","name":"fie"}]
Your method - retrieveLatestMessages() action in your case - must return a model, but it returns the output of println instead.
To make your code work, you must place the model in the last line, or explicitly return it by using return statement:
def retrieveLatestMessages() {
println "test"
def messages = Message.listOrderByDate(order: 'desc', max:1000)
println messages
[messages:messages.reverse()]
}
Try this
import grails.converters.JSON
#Secured([ 'ROLE_USER'])
def retrieveLatestMessages() {
println "test"
def messages = Message.listOrderByDate(order: 'asc', max:1000)
render messages as JSON
}
Enjoy.
I had this sample app working on mine with no issues but here is the thing, this process requires you to poll the page consistently and it is resource intensive:
I ended up writing a domainClass that was bound to a Datasource that was using the HQL db and was outside of my own app, the process requires a DB table to stream chat....
Alternative is to move away from polling and use websockets:
check out this video
https://www.youtube.com/watch?v=8QBdUcFqRkU
Then check out this video
https://www.youtube.com/watch?v=BikL52HYaZg
Finally look at this :
https://github.com/vahidhedayati/grails-websocket-example
This has been updated and includes the 2nd method of using winsocket to make simple chat ....
I need to get Google Drive a folder's fileId. If the folder does not exist, I need to create a folder with that name and return fileId. With fileId, I need to do other works.
The Google Drive API in Dart is ok for me, I can create a folder with Dart. The question is about Future.
The code is as follow:
drive.files.list(q:"title='TEST'").then((result){
if(result.items.length == 0) {
driveclient.File file = new driveclient.File.fromJson({"title":"TEST", "mimeType": "application/vnd.google-apps.folder"});
drive.files.insert(file).then((result2) {
return result2.id;
});
} else {
return result.items[0].id;
}
});
When TEST exists, the id is returned. But if TEST doesn't, the function error because no return.
How to do that?
Thanks in advance.
You need to return the future from the then method you call in line 4:
drive.files.list(q:"title='TEST'").then((result){
if(result.items.length == 0) {
driveclient.File file = new driveclient.File.fromJson({"title":"TEST", "mimeType": "application/vnd.google-apps.folder"});
return drive.files.insert(file).then((result2) {
return result2.id;
});
} else {
return result.items[0].id;
}
});
then() returns a new future there completes with the value returned from the method you give as parameter to the method. then() is also smart enough to resolve all nested future's so you will always handle a value inside a then() method and never a instance of Future.