Genesys Engage on-premises

 View Only

Discussion Thread View
Expand all | Collapse all

Session persistence between callflows and subcallflows

  • 1.  Session persistence between callflows and subcallflows

    Posted 08-10-2018 11:04
    Hello There.

     Is there any standard implementation of a session object to be used with composer voice applications ? The idea is to add parameters to a session object using the ECMA script blocks and pass a single session object containing whatever was added in the call flow to the next subcallflow.

    I remeber using a proprietary support library to implement this concept of session in composer a few years ago, perhaps it was a proprietary script. Life was a lot easier while handling variables between subcallflows.

    Thank you all in advance.

    ------------------------------
    Amauri de Oliveira
    Alctel Telecom
    ------------------------------


  • 2.  RE: Session persistence between callflows and subcallflows

    Posted 08-12-2018 08:52
    it seems you are talking about advanced topic , why you don't just rely on attached data ?

    ------------------------------
    Mohammed Adel
    IST Networks - Saudi Arabia
    ------------------------------



  • 3.  RE: Session persistence between callflows and subcallflows

    Posted 08-16-2018 09:30
    Hello Mohammed Adel.

    Attached data would not be very efficient on this scenario.
    Sometimes some details are only useful for application control etc. Offcourse it can be used as a workaround but its not the best way to handle this from various points of view. Perhaps, thanks a lot for your suggestion.

    Cheers



    ------------------------------
    Amauri de Oliveira
    Alctel Telecom
    ------------------------------



  • 4.  RE: Session persistence between callflows and subcallflows

    Posted 08-13-2018 12:22
    ​Amauri,
    You can pass variables to the subcallflow using the Parameters property of the Subdialog block. Create a variable in your SUBcallflow and change it from type 'user' to type 'input'. then when calling the subcallflow, use Parameters to specify the values you will be passing to the input variable in the subcallflow.
    Consider creating object (JSON) variables so that with one pass, you have multiple values to use.


    The other method is to use ApplicationRoot variables, which are available to all callflows in the project. You can define root variables by editing the /src/ComposerRoot.vxml file. This file is specified in the Entry block of your main callflow and will be executed to create those variables you specify.


    If you pass variables to a subcallflow, and you change them, and you want them to perist, MAKE SURE that in the EXIT block of your SUBcallflow, you pass back the variables you changed.

    ------------------------------
    Todd McCall
    Bank of America
    ------------------------------



  • 5.  RE: Session persistence between callflows and subcallflows

    Posted 08-16-2018 09:26
    Hello Todd McCall.

    Thanks for your suggestions.
    The JSON approach should be enough to achieve what I want. Do you have any example that I can use as a template ?

    As I mentioned on the initial post, I've done something like this in the past but the ECMA Script library to handle the JSON was not available in  composer by default. It was a proprietary one that we developed in our team.

    Thanks again for your quick response.

    ------------------------------
    Amauri de Oliveira
    Alctel Telecom
    ------------------------------



  • 6.  RE: Session persistence between callflows and subcallflows
    Best Answer

    Posted 08-17-2018 09:52

    I LOVE objects! They're extremely useful and ORS uses them almost exclusively.
    First lest get some terms straight: JSON - Java Script Object Notation.
    If I want to show you what is in my object, I will use the notation of an object as defined byJavaScript (technically ECMA) . To say "I am using a JSON object" is redundant. "My JavaScript object is represented using JSON." 

    Enough of semantics. :-)

    Here is an example of declaring an object and assigning its contents using JSON:

    var j_vehicles = {"make":"Ford","model":"F150","color";'Maroon"};
     
    My object has three key:value pairs:
    j_vehicles.make == "Ford"
    j_vehicles.model == "F150"
    j_vehicles.color == "Maroon"

    Optional methods for declaring an object variable:
    var j_vehicles = new Object();
    var j_vehicles = {};

    Declaring the object these last two methods requires a specific method for creating keys and values as described later.

    These methods of declaring all work for me in a Composer ECMA block, but we would need a Java developer to chime in here to tell us what the best method is and why. :-)

    Now for manipulation of data:
    To convert the object into a string variable, I would use the JSON.stringify() function like this:

    var s_vehicles = JSON.stringify(j_vehicles);

    This method is REQUIRED to put an object's contents in a LOG file, otherwise you get "object:object" in the log.
    The s_vehicles string variable now has the string '{"make":"Ford","model":"F150","color";'Maroon"}'.

    Conversely, I can make the string into an object using JSON.parse(). Example:
    j_vehicles = JSON.parse('{"make":"Ford","model":"F150","color";'Maroon"}');  (make sure you use a string)

    If you want to get a specific value from an object, like the value of the key 'model', specify the variable name and the key value like this:
    s_model = j_vehicle.model;

    The s_model string variable now has the string 'F150' in it. 

    To change the value of an object key, I can directly reference it in the same way:
    j_vehicles.color = 'Blue';

    Lets add a new key and its value to the existing object variable:
    j_vehicles['year'] = "1997";

    Now put your thinking cap on as I explain my favorite use of searching through objects, the For loop:

    for (var k in j_vehicles){
        if(k = 'color'){
            s_color = j_vehicles[k];
        }//end if
    }//end for

    In this example, I defined the variable "k" and the for loop gives that variable the value of each key in the object with every loop, and stops looping when it runs out of keys.
    Within each loop, variable "k" is the name of the key for comparison's sake, and that key's value can be referenced using square brackets to reference the key name.
    If "k" = 'color', then j_vehicles[k] returns "Maroon" (or "Blue" if you are holding me to a higher standard of programming) :-)

    Another cool trick:
    var a_keys = Object.keys(j_vehicles);

    Variable a_keys is now an array variable with the contents ["make","model","color","year"] and the function a_keys.indexOf("color") returns integer value 2.

    Finally, any key in the object can contain another list of key:value pairs, defining multiple, complex levels of data. Example:
    j_vehicles =
    {
        "summer":{
            "type":"boat","color":"white"
        }
        ,"winter":{
            "type":"snowmobile", "color":"black"
        }
    }

    Fetch values from the object:
    j_vehicles.winter.color == "black"
    j_vehicles["winter"]["color"] == "black"

    Fetch values using variables for key names:
    s_season = "winter";
    s_param = "color";
    j_vehicles[s_season][s_param] = "black"
        
    There's more to learn at https://www.w3schools.com/js/js_objects.asp. W3Schools formed the foundation of my knowledge of JavaScript.

    Composer is building JavaScript that Orchestration Server executes, and ORS 8.1 has the JavaScript ECMA version called SpiderMonkey from 2015, so you have to be careful about using newer JS functions that work in your JavaScript tester. They may not work in ORS. ('undefined' errors abound) You are free to use all of these object examples I am showing you in ORS 8.1.

    Happy coding!



    ------------------------------
    Todd McCall
    Bank of America
    ------------------------------



  • 7.  RE: Session persistence between callflows and subcallflows

    Posted 08-21-2018 14:09
    Hello Todd.

    spot-on mate!
    Outstanding response.

    I will give it a try on my lab and see how it goes.

    Thanks a lot
    Cheers


    ------------------------------
    Amauri de Oliveira
    Alctel Telecom
    ------------------------------



  • 8.  RE: Session persistence between callflows and subcallflows

    Posted 08-22-2018 14:57
    Hello Todd.

    I tried your approach and its working fine for me, perhaps I found a problem while passing tje JSON object to a callflow as a parameter. If I print the JSON object on the workflow prior to pass it to a callflow as a parameter, everything appears to be fine, perhaps if I log the input parameter right after the entry block on the callflow, it appears to me on the mcp logs as "undefined".
    I´m wondering if this has something to do with the way how the Play Application Block works (via gvp communication dn on the sip server). Could you please let me know if you have this working on your projects (passing the json object as a parameter to a callflow)


    Thanks in advance,

    ------------------------------
    Amauri de Oliveira
    Alctel Telecom
    http://amaurioliveira.com
    ------------------------------



  • 9.  RE: Session persistence between callflows and subcallflows

    Posted 08-22-2018 16:55
    [​RE-POST from individual to Group]

    Since you mentioned the Play Application block, I perceive that you are starting in a .workflow file, creating your object and giving the object its data. Then, you use the Play Application block to specify a .callflow file, and you open your call flow file in Composer and expect to have that data to work with.

    That's not going to happen.

    Call flows can pass to sub callflows and workflows can pass to sub workflows, but you can't pass directly between callflows and workflows.
    BUT THERE IS A METHOD that does work. You have to utilize user data or "KVPs".

    But first, an explanation.

    The Orchestration server (ORS) loads and executes workflows that have been converted to SCXML, while a different server altogether, the Media Control Processor server (MCP) loads and executes call flows that have been converted to VXML.

    Since workflows and callflows are loaded and executed on different servers, the only way to share data is through the common server that is talking to them both - SIP server. ORS can read-write to user data that's passed to SIP server, and so can MCP server.

    So! This is the simplest method, and other developers may pipe in here and offer suggestions that will work just fine. We each have our own best practices.

    1) In your WORKFLOW, open the variable editor and create a variable, either project variable or user variable. Set its value to {} - just like that... an open curly bracket and a closed curly bracket. This indicates to the ECMA engine that its an object. Its unnecessary though. you could just as well leave it undefined. But using this {} as a value will convey to other developers that we have an object.

    2) In you WORKFLOW, use an ECMA block to add keys and values to your object, as discussed in the previous post. Then use the genesys function setuData() to send the key/value pars in that object to SIP server to be stored in user data (as well as in local ORS memory for that interaction). You can find this function in Expression builder on the left if you search on 'setuData'. Here is an example:

    _genesys.ixn.setuData(j_vehicles, system.InteractionID)
    There is no return value - it is known as 'void' - so you don't have to assign a variable like you do with other functions.


    3) In your WORKFLOW, use a PlayApplication block to launch your callflow.

    4) in your CALLFLOW, create variables WITH THE EXACT SAME NAMES as any of the KEYS that you used in your object in your workflow. For example, in my workflow, I created j_vehicles, and it had this data:
    j_vehicles.color = "Maroon"
    j_vehicles.model = "F150"
    j_vehicles.make = "Ford"
    When I used the j_vehicles object in the setuData() function to write user data, ORS sent three key/value pairs of data to the SIP server: {"color":"Maroon","model":"F150","make":"Ford"}. So in my CALLFLOW, I have to create variables that match the names of those keys: 'color' and 'model' and 'make'.
    5) In your CALLFLOW, use the Interaction Data block to select variables that, with a variable name matching the KEY name from user data, will be filled with the values from user data.
         a)  Set property 'Operation' to the value "get" (should be the default)
         b) click on the 'Values' property and the VALUES window will pop up where you can click the check box next to the variables you created.

    Now, your data from the ORS workflow is sitting in the MCP server's call flow variables.

    But guess what? SIP server has them too! When the CALLFLOW is finished executing and ORS regains control after the Play Application block, you may want to delete several of those KVPs in User data, depending on what you created. (alternately, write new KVPs or change existing KVP data to pass data back to ORS)
    To delete a KVP in user data, in your WORKFLOW, use an ECMA block to call the deleteuData() function and specify the keys you want to delete. Example:
    _genesys.ixn.deleteuData('color',system.InteractionID);

    If you are daring, you can send a string of JSON (object notation) as a KVP so that you can perform a JSON.parse([variable]) and extract your KVPs as you would from an object. Afterward, you only have one KVP to delete. But I would master this method in DEV first, then use more advanced methods for your production run.

    Keep in mind that ICON gets access to all the KVPs for reporting, and that too many KVPs will slow down your SIP server performance, so use them smartly, do your clean up and keep them wisely.

    Happy coding!

    ------------------------------
    Todd McCall
    Bank of America
    ------------------------------



  • 10.  RE: Session persistence between callflows and subcallflows

    Posted 08-23-2018 09:29
    Thanks Todd.

    Giving the complexity of the application that I'm working at the moment, I'm assuming that a more efficient approach would be develop the entire "IVR / routing using a "routing application/workflows/scxml" rather than exchange data between Worflows and callflows . I would like to take your opinion and the community opinion in regards to this.

    I'm assuming that the "ORS / URS Centric" approach would be more efficient and I can apply the session object described in this thread, also, this approach will avoid excessive load on the T-Servers / network / resource manager / mcp(s)

    Thanks in advace.

    ------------------------------
    Amauri de Oliveira
    Alctel Telecom
    http://amaurioliveira.com

    ------------------------------



  • 11.  RE: Session persistence between callflows and subcallflows

    Posted 08-24-2018 11:04
    ​Unfortunately, Orchestration Server (ORS) is not capable of establishing a direct talk-path to a call. And the Media Control Processor (MCP) is not capable of targeting (agent groups, skills etc). They each have their roles and their efficiencies. It is the MCP server that requests an RTP stream between itself and the caller to establish a talk-path. ORS does not do this.

    In fact, while your ORS workflows have blocks like PlaySound and UserInput, those functions are actually requests of SIP server to slave an MCP server to perform the function. Even the methods for playing treatments (prompts) vary greatly between workflows (ORS) and callflows (MCP).
    For instance, when ORS is going to play a treatment (.wav file) you have to specify the number of the announcement. That number is directly associated to the corresponding folder name on the MCP server in its 'announcement/' folder, and then, MCP server will fetch the 'pcmu.wav' file within that folder. [These folder names, file names and file types can vary across platforms from within the configuration options] The point is that all treatments (prompts) that ORS requests are stored directly on the MCP server or servers for MCP to use in its talk-path.

    In contrast, when the MCP server is executing a call flow, and it needs to play a prompt (.wav file) it fetches the file from the Tomcat server, rather than looking locally. So your menus and prompts that are commanded from within an ORS workflow must be duplicated on all MCP servers under the control of a Resource Manager. Most developers delegate the audio interaction and collecting of digits to a call flows for the MCP server, with the exception of hold music. [please defer to your Genesys account manager for architectural capabilities - I only know what I have seen]

    Check out the method that Genesys provides for passing data between a workflow and a call flow in the Composer 8.1.4 Help Book on page 775 - Passing Parameters. Genesys understands the need for an ORS strategy to call an MCP call flow and the requirement to pass data. They specify that you use User Data with the .setuData() function. Their method is a little more complicated than what I showed you, but only because you embraced using objects. They coach you to use objects without actually telling you what you are doing.

    I know that passing user data seems more complicated as well as being a burden on the framework when multiplied by 500 concurrent interactions, but this is "the way of the developer". You must keep your memory usage in mind when creating strategies, and the use of User Data at the SIP level is just another extension of the developer's role.
    Should I create thirty project variables, or thirty user variables and pass them between subroutines, or should I declare temporary variables within an ECMA script that are destroyed at the completion of that state block? Don't use any memory that you don't absolutely need, right? And should my IF statement call a function, while the next variable assignment calls the exact same function? No. Call the function one time, put it in a variable, then let the IF statement evaluate the variable. You see? Its "the way of the developer" and managing your User Data is just a part of that. (I am envisioning a new movie from DreamWorks like Kung Fu Panda, but with a kid sitting at a computer)

    Don't be discouraged. As soon as your Call flow launches, you have the ability read all the KVPs that ORS created and then delete them! And whether or not you want to use user-data to pass information between ORS and MCP, the framework is doing it anyway, whether you like it or not. :-)

    Happy (smart and efficient) coding!


    ------------------------------
    Todd McCall
    Bank of America
    ------------------------------



  • 12.  RE: Session persistence between callflows and subcallflows

    Posted 08-27-2018 13:30
    Hello Todd.

    I´m convinced that a mix of workflows and callflows is the best out of the best that we can get from it at the current status. I´m trying to keep most of the logic on the routing application and call gvp just when I need to interact with the user. It still use the attach data approach but I don´t have to pass a big JSON string as attached data. 
    I´m happy with the direction that this discussion went, and I don´t think there is much more to discussi about it. Unless someone have something to add.

    Thanks a lot for your contribution.


    ------------------------------
    Amauri de Oliveira
    Alctel Telecom
    http://amaurioliveira.com
    ------------------------------



  • 13.  RE: Session persistence between callflows and subcallflows

    Posted 08-30-2018 11:30
    Doing some more work on this. I noticed that I can´t simply update values inside my arrays by simply calling object.key = value. How are you guys doing this ?

    ------------------------------
    Amauri de Oliveira
    Alctel Telecom
    http://amaurioliveira.com
    ------------------------------



  • 14.  RE: Session persistence between callflows and subcallflows

    Posted 08-31-2018 09:06
    ​​Setting the value of an array is different than setting the value of an object. This is more complicated you find an arrays IN an object.

    Setting the value of an object:
    Lets create an object called j_employee with first name and last name:
            var j_employee = {"firstname":"Todd","lastname":"McCall"};
    I can read the value like this:
            var s_fName = j_employee.firstname;
            //now s_fName == 'Todd'
    Another way:
        var fName = j_employee['firstname'];

    I can SET the value in the same two ways:
        j_employee.firstname = "John";
        j_employee['firstname'] = "John";
       //now my object is stringified to '{"firstname":"John","lastname":"McCall"}'


    Setting the value of an array:
    Lets create an array of fruits:
        var a_fruits = ['apple','orange','banana','peach'];
    I can read the values like this:
        var s_fruit = a_fruits[0];
        // now s_fruit == 'apple'
        var s_fruit = a_fruits[2];
        //now s_fruit == 'banana'

    Making Life Complicated:
    Lets add an array to an object:
        j_employee['directReports'] = ['Bill','George','Barak'];
        //now the object can be is stringified to:  '{"firstname":"John","lastname":"McCall","directReports":["Bill","George","Barak"]}'
    Now I can reference the values in the array using the key name and square brackets to indicate the element number:
        s_johnsDR = j_employee.directReports[1];
        //now s_johnsDR = 'George';  //the first element number in an array is zero


    And finally to DIRECTLY answer your question, to change an element in an array:
        a_fruit[2] = 'mango';
        //now a_fruit == "apple,orange,mango,peach"
    To change a key value of an object:
        j_employee.firstname = 'Todd';
        //now the object can be is stringified to:  '{"firstname":"Todd","lastname":"McCall","directReports":["Bill","George","Barak"]}'
    To change an array in an object:
        j_employee.directReports[0] = 'Amauri';
         //now the object can be is stringified to:  '{"firstname":"Todd","lastname":"McCall","directReports":["Amauri","George","Barak"]}'
    All JavaScript functions for arrays work just fine when the array is a key value in an object.

    And for a little bit more obfuscation, if you use the 'typeof' method on an array, it will return 'object' because in JavaScript, arrays are objects.
    https://www.w3schools.com/js/js_datatypes.asp

    Happy coding, and ITS FRIDAY!!!

    ------------------------------
    Todd McCall
    Bank of America
    ------------------------------



  • 15.  RE: Session persistence between callflows and subcallflows

    Posted 09-01-2018 04:22
    Hi Todd,
    thanks a lot for your reply once again.

    the problem that I'm facing at the moment appears to be in ORS. Basically, if you pass an object to a subworkflow, then assign something's o it, it should work for the first time, perhaps if you try to change your recently created "element" inside your input session object, ors wont allow you to do it. The workaround that I found to this was replicate the input session object in a user variable, do whatever I need to do in my subworkflow changing my local session object and the return my local session object.




    ------------------------------
    Amauri de Oliveira
    Alctel Telecom
    http://amaurioliveira.com
    ------------------------------



  • 16.  RE: Session persistence between callflows and subcallflows

    Posted 09-27-2018 18:19
    At the end I kept my JSON session variable as a project variable. This because I had issues with my session object apparently being replicated across subworkflows. After implementing this small design change, the JSON approach that Todd described above are working fine and consistently. Perhaps Im still having problems to pass paramters to callflows, even using the attach data approach. But its going well till now.

    Amauri de Oliveira
    Alctel Telecom
    http://amaurioliveira.com

    ------------------------------
    Ate the endt I kept my JSON session variable as a project variable. This because I had issues with my session object apparently being replicated across subworkflows. After implementing this small design change, the JSON approaches that Todd described above are working fine and consistently. Perhaps Im still having problems to pass paramters to callflows, even using the attach data approach.

    Amauri de Oliveira
    Alctel Telecom
    http://amaurioliveira.com
    ------------------------------



Need Help finding something?

Check out the Genesys Knowledge Network - your all-in-one access point for Genesys resources