Developing a SmartForm Factory Dynamic Data Service

I recently had a reason to build a demo form in Composer to demonstrate the capabilities of Dynamic Data in SmartForm Factory. This article will give you an idea of how Dynamic Data works and what steps you need to take to get it working in your form. This article assumes an intermediate level of Composer and Manager experience.

Introduction to Dynamic Data

Dynamic Data Services was introduced into SmartForm Factory 3.2 as a mechanism to provide back end querying capability to SmartForm Composer generated forms. The Dynamic Data service is a capability that is initiated in a Composer generated form, and supplied by the SmartForm Manager web application. This extends the reach and reuse of data and functionality to users and customers, saving time and money, while improving the end user experience in a secure and efficient way. The Composer widget maintains its focus on end-user ease-of-use, while providing amazing power and flexibility to the form-filling experience. The widget can appear as a Button or a Link and includes a Test facility so you can mock up the return data in SmartForm Composer before publishing to SmartForm Manager. On the Manager side, a simple to create Service Definition includes all the parameters required to expose a Groovy script which defines the activity of the Dynamic Data Service. Groovy is a language with a syntax very familiar to ActionScript and Javascript programmers alike. You can find out more about Groovy here http://groovy.codehaus.org/, but this article assumes no prior knowledge of Groovy syntax or structure.

Convention over Configuration

One of the problems with conventional server-side invocations is that it can be quite complex to configure. Composer uses a design pattern known as “Convention over Configuration” to enable these invocations.

Using this approach, rather than explicitly mapping the fields in the form to the input and output parameters of the service, Composer simply assumes:

  • All the input fields are in one block
  • All the output fields are in another block
  • The names of fields matches the names of service parameters.

Please see the following article for more information about Convention over Configuration.
http://en.wikipedia.org/wiki/Convention_over_configuration 

Handling Data

Dynamic Data can cater for single and multiple record return values, and as the Form Designer, you need to know when to use the right output structures. Dynamic Data requires only the name of the Dynamic Data Service defined in Manager, but in reality needs an Input Block and an Output Block of widgets to be useful as a look up service. Data passed between the Composer generated SmartForm and SmartForm Manager (and indeed the test data) is structured using the JSON format, a structure almost universally available. While a complete description is beyond the scope of this article, JSON uses a simple key, value naming arrangement that can support deep nested structures and arrays.

Building the Composer Form

The demo I’m referring to is a Composer form for a Fleet Management company, the specific case is the reconciliation of Novated Lease contracts. In this instance, when the employee or employer wishes to terminate the contract, this form is used and some details known to the Fleet management company can be provided by looking up a vehicle registration number.

Input Data

Strictly speaking, the example I’m cribbing from takes two input parameters, email address and vehicle registration, both plain text fields with string values. To supply these parameters, I’ll put them inside a Block and configure them with whatever other parameters I need like mandatory rules and visibility. The Block can be called anything you like and needs no other special configuration. Note that any fields in the Input Block will be automatically bound and transmitted to SmartForm Manager as input data. This doesn’t mean that your Dynamic Data Service has to use them, just be aware that they’re there.

Output Data

Since this is just a simple example, I’m going to put just a couple of fields of output data into a standard Block. For this example I’ve got the Registration Number (text), the Vehicle Description (text) and a Registration Date (date). Note that you don’t have to have your output fields in a block of their own, they can be in a block with other fields. We’ll see later how the binding of the output data occurs.

The Lookup Widget

In Composer, locate the Dynamic Data Service Button widget, typically found in the SmartForm Manager section of the widget palette, and drop it on the form where you need it to appear. The following dialog will appear.

Composer's Dynamic Data Dialog

Composer’s Dynamic Data Dialog

Click the Browse buttons next to the Input and Result block entry areas to locate the parent Blocks of the Input and Output fields you defined earlier. Finally change the Button type as desired. You don’t need to change the SFM Dynamic Data Service Lookup Name just yet, we’ll be using the test mode to prove out our scenario first and then migrating it to SmartForm Manager a little later on. Save the dialog.

Testing the Lookup

Our next step will be to provide some test data so we can be assured of the JSON data format required to prove our scenario. Bring up the property editor for the Dynamic Data Service widget. In the Data section, check the Test Mode flag and enter the JSON formatted return value into the Test Data text area. In my case, I’m returning data for two text fields and a date field. The format to use when formulating JSON notation is as follows:

{“KeyA”:”ValueA”,”KeyB”:”ValueB”}

which equates to:

KeyA = ValueA

KeyB = ValueB

This appears in Composer as:

{“FieldNameA”:”Field Value”,”FieldNameB”:”Field Value”}

There are a few gotchas I found when formulating my test data:

 Remember to use Field Names as the key, not labels. Don’t be fooled into thinking the hierarchy uses field names, you’ll need to look up the field names in the property editor for the field.

 Values need to be formatted as appropriate for the field.

The Composer date field format required in JSON notation was a little tricky to figure out, so I put a date field on the form and a text field with a rule to copy the date field value. This exposed the date field format as: YYYY-MM-DD Here is an example of the JSON test data that I formulated for my specific test case. RegistrationNumber, VehicleDescription and RegistrationRenewalD are the three output fields of type text, text and date respectively.

{“RegistrationNumber”:”ABC123″,”VehicleDescription”:”Its Blue”,”RegistrationRenewalD”:”2014-01-01″}

Composer's Dynamic Data Service Data Dialog

Composer’s Dynamic Data Service Data Dialog

When the Dynamic Data Service component receives the results data from either test or SmartForm Manager, it binds the associated value to the appropriate field by means of it’s name. It’s this name binding that makes it possible to specify an output block that has other fields that aren’t part of the result set. After saving the properties and previewing the form, you should see your test data appear in output fields. If your data does not appear, or you get errors you’ll need to resolve these before migrating the test data to SmartForm Manager.

Building the SmartForm Manager Dynamic Data Service.

SmartForm Manager exposes a single endpoint for Dynamic Data Services, but determines which service to use by a single hidden field included in the input. Manager also secures the use of the Dynamic Data Service by embedding a hash key into the form when published to thwart possible misuse of the service data by unauthorised clients. Dynamic Data Service Definitions use a combination of Service Definition configuration values and parameters to configure the service. I won’t go into explaining them all here, we’ll simply call up an existing Dynamic Service Definition, copy it and modify it for our own purpose.

Copying an existing Dynamic Service Definition

Log in to your SmartForm Manager instance and select the System > Service Definitions menu item. Select Dynamic Data from the list of Service Types and click the search button. This gives you a list of the existing Dynamic Data Services already developed and you should go through some to get a feel for how they work. For the purpose of this example we’re going to plough ahead and copy an existing service and then modify it to suit our needs.

Manager's Service Definition Page

Manager’s Service Definition Page

On the Service Definitions page, click the Copy button. On the Copy Service Definition page, select Dynamic Data as the Service Type, “Groovy Dynamic Data” as the Existing Service Name and enter a new name for the service. For this demonstration, I’m using Vehicle Registration. Note that spaces are perfectly acceptable in a Service Definition name.

Modifying the Dynamic Service Definition

Now that the service exists, technically we could call it from a published SmartForm but the output data in the script isn’t tailored to our needs, so we’ll fix that up first. Edit the Service Definition for the new Dynamic Data service and click the Serchive Parameters tab. Click on the groovyScript parameter name, this will invoke the text area editor which has a handy code syntax highlighter to help you spot errors.

Manager's Service Definition Groovy Script Editor

Manager’s Service Definition Groovy Script Editor

While this is a simple kickstarter to get you going to creating your own Dynamic Data Services, I will include a few pointers to help you write some Groovy script. In fact this example is so simple it’s probably considered the least functionality you could get away with for a demo situation. The script presented here does a very simple test for two different vehicle registration values and returns a different result for each. (formatted for readability).

def emailAddress = request.getParameter('EnterEmployeeEmailAd');
def registration = request.getParameter('EnterRegistrationNum'); 
def response; 

if(registration == "ABC123")
{ 
    response='{"RegistrationNumber":"ABC123","VehicleDescription":"Its     
    Blue","RegistrationRenewalD":"2014-01-01"}' 
} 
else if(registration == "DEF456")
{ 
    response='{"RegistrationNumber":"DEF456","VehicleDescription":"Its 
    Green","RegistrationRenewalD":"2012-08-02"}' 
} 
return response;

The first two lines extract the input block parameter values, note the use of Composer field names. The next if block simply tests the registration value, while emailAddress is ignored. Note how the JSON values are formulated as text strings and returned using the …

return response;

at the end of the code block. That’s it – couldn’t be much simpler and still be useful.   Those of you looking to kick it up a notch should have a look at some of the Manager Dynamic Data service implementations below, the Groovy website and other Internet resources.

A slightly more complex invocation proxies a web service to determine US city. This example makes a good start for re-purposing internal organisational web services. It looks like this:

import wslite.soap.*
import net.sf.json.*
import net.sf.json.xml.*

def cityName = request.getParameter('cityName')
def client = new SOAPClient('http://www.webserviceX.NET/uszip.asmx')
def response = client.send(SOAPAction:'http://www.webserviceX.NET/GetInfoByCity') {
 body {
 GetInfoByCity('xmlns':'http://www.webserviceX.NET') {
 USCity(cityName)
 }
 }
}

def jsonResponse = new XMLSerializer().read(response.text)
return jsonResponse

and yet another sample that invokes a more complex web service is here:

import wslite.soap.*
import net.sf.json.*
import net.sf.json.xml.*

def abn = request.getParameter('abn')

def client = new SOAPClient('http://abr.business.gov.au/abrxmlsearch/AbrXmlSearch.asmx')
def response = client.send(
    """<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:abr="http://abr.business.gov.au/ABRXMLSearch/">
   <soapenv:Header/>
   <soapenv:Body>
      <abr:ABRSearchByABN>
         <!--Optional:-->
         <abr:searchString>""" + abn + """</abr:searchString>
         <!--Optional:-->
         <abr:includeHistoricalDetails>N</abr:includeHistoricalDetails>
         <!--Optional:-->
         <abr:authenticationGuid>suppressed</abr:authenticationGuid>
      </abr:ABRSearchByABN>
   </soapenv:Body>
</soapenv:Envelope>"""
)

String organisationName =response.ABRSearchByABNResponse.ABRPayloadSearchResults.response.businessEntity.mainName.organisationName;
def jsonResponse = new JSONSerializer().toJSON(["CompanyName": organisationName]);
return jsonResponse;

A full description of these Groovy Script is beyond the scope of this article, but this is generally something a programmer would build for you, so you probably don’t need to understand the details.

Bringing it all back to Composer.

Ok, now that you’ve created your Dynamic Data Service and given it a name, drop back into Composer, open the Dynamic Data Service widget and paste the name into the SFM Dynamic Data Service Name. Make sure you turn off the Test Mode flag and you’re ready to publish your form to Manager. Click the publish button, select HTML only if you’re only interested in the Dynamic Data Service capability and follow the usual routine for publishing your form into your favorite portal and organisation. Open the Form properties page, click the Render Form button and try out your new Dynamic Data Service. Winning!

A Final Word – Testing Testing Testing

OK, so there’s a good chance that when you got it all hooked up it didn’t work. For debugging I highly recommend FireFox and Firebug or Chrome and its Dev Tools for analysing page behavior. In the screenshot you’ll notice there’s a section at the bottom of the page where you can see the POST request to the Manager servlet that services the Dynamic Data Services requests. The response from that POST includes the data as formatted in my Test Mode data. Yours should too.

SmartForm Debugging

SmartForm Debugging

Bonus Points – Repeating blocks of results

Thankfully, the Composer developers catered for multiple results sets by allowing data to be returned in a JSON array. This is easily accomplished by wrapping the JSON response data in an array spec and separating records with commas, like so:

[{"record1":"value1"},{"record2","value2"}]

On the Composer side, the output block must be Repeatable Content to get the Repeatable-Block field definition. Again, field binding is done by name with each record in the JSON array creating a new repeat section. Do be careful if switching between the two types (Repeating and Standard Blocks) since the Output Block specification differs depending on the type. In my example the Output Block specification for a single result is:

../VehicleDetails

If I were to change the block to Repeatable Content in response to some change in development, I must ensure my Output Block specification is updated and appears as follows:

../VehicleDetails[*]

Conclusion

Dynamic Data Services is a very important and strategic feature for SmartForm Factory providing a mechanism to further enrich the user experience by including interactivity between Composer and Manager. The range of possible applications for this capability are untold with the flexibility and power in the current design.

References

1. Groovy - http://groovy.codehaus.org/

2. JSON - http://www.json.org/

6 thoughts on “Developing a SmartForm Factory Dynamic Data Service

  1. Great article Jason, thank you. Just a few words to those reading…
    - We have recently added a Dynamic Data Assistant that automates several of the steps in the above process. However, this article provides an excellent insight into what is actually going on under the covers.
    - At the moment, the only way to invoke a Dynamic Data service is using a button. Over time, we will be building several widgets that use the Dynamic Data service implicitly. One of these is a drop-down list that automatically populates itself based on a Dynamic Data invocation.

  2. Hi,
    I was trying to use the dynamic data service button feature as you had explained in the article here. But when i try to look up the data it gives an error saying that ‘Missing url for service ‘. Could you please say what can be the issue that is giving this error.
    Thanks!

  3. Hi Sadaf,

    Thanks for your question. This message is presented when using the Dynamic Data Service in the Composer design time environment. Normally, forms are generated by the server and contain the base URL for any server related acitvities, like calling Dynamic Data Services. Forms previewed in Composer don’t contain this URL by design, but they do include a test data area where you can supply an expected response to be displayed.

    Regards,

  4. Hi,
    Firstly, thank you for this article. It helped me a lot to learn about the dynamic data service. But when i am implementing this i am unable to generate any prefill. I have followed all the steps as mentioned. I am unable to test the script written as well. I have tried to replicate what you have explained with another webservice.

    import wslite.soap.*
    import net.sf.json.*
    import net.sf.json.xml.*

    def Zip = request.getParameter(‘Zipcode’)

    def client = new SOAPClient(‘http://www.webserviceX.NET/uszip.asmx’)
    def response = client.send(SOAPAction:’http://www.webserviceX.NET/GetInfoByCity’) {
    body {
    GetInfoByCity(‘xmlns’:'http://www.webserviceX.NET’) {
    USZip(Zipcode)
    }
    }
    }
    def jsonResponse = new XMLSerializer().read(response.text)
    return jsonResponse

    can you please see what can be the error in this case.

  5. Hi Jason,

    I am New to AVOKA therefore some of my questions might not make sense to you :) .

    I am working in composer where I was trying to invoke a web service using the groovy script , I used the web service provided by AVOKA, in the groovy sample.The web service is trying to get the details based on the city which is passed to the web service.When I am executing that through the “groovy script console” by hard coding cityname , its throwing Internal Server Error. I thought it should have worked fine.I am not able to figure out the reason.

    I have a requirement where I need to provide a search functionality to the end user in the composer form where the user can search for the name of their manager and get the details line mail,contact number etc. Is there any other way also where I can achieve the above functionality.

    Also,to work on groovy script what level of expertise or knowledge is required.It seems a new language similar to JAVA.

    Thanks in Advance, Manish

  6. Hi Jason,

    Suppose the webservice we called returned multiple values for the same city name, how can i bind the values? ..
    Thanks ,
    Sandhya

Leave a Reply

Your email address will not be published. Required fields are marked *


5 + four =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>