2013-08-18

Filling ServiceNow survey from external web page with MuleESB

The ServiceNow platform provides an easy and powerful tools for defining, requesting users to answer, and answering the surveys.

Some time ago a client of ours had an issue. They wanted to provide their end users a way to answer a survey anonymously, but due to Single Sign On setup (and network) the ServiceNow platform would always know who the survey taker was.

In this particular case the client decided to setup an external web server hosting a web page which would POST the survey data to a servlet which in turn would invoke ServiceNow's SOAP web services.

Since I currently have a thing going on with MuleESB, I decided to see how it could be used to solve the dilemma. Nothing fancy, just the bare bones implementation. See below.


Initially I tried, for practice's sake, to do all of this without scripting components and by invoking SOAP web services, but it quickly turned out to be a bad idea. In the end I ended up using ServiceNow's JSON web service interface and forming the messages with Groovy components.

The Mule application has two flows.
  • The first flow serves a static page with the survey questions
  • The second flow populates needed tables in ServiceNow when a user submits the page of the first flow

Static Survey Questions

The "ServeTheForm" flow is as short as it gets. Just a HTTP inbound endpoint and a static resource handler.


<flow name="ServeTheForm" doc:name="ServeTheForm">
        <http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8082" doc:name="HTTP"/>
        <http:static-resource-handler resourceBase="${app.home}/docroot/" defaultFile="index.html"/>
    </flow>
The form contains just two question, one with numerical answer and one with free text answer. The form's action refers to the second flow which will populate the ServiceNow system.

POSTing the data to ServiceNow



In short the "PostData2SNC" creates a survey_instance record and two survey_response records linked to the survey_instance record.

HTTP
The HTTP inbound endpoint takes in the POST call
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="SurveyFiller" doc:name="HTTP"/>
Body to Parameter Map
The body to parameter map transformer transforms the payload into a parameter map, which is a whole lot nicer to handle in the Groovy component.
<http:body-to-parameter-map-transformer doc:name="Body to Parameter Map"/>
Form survey_instance json

<scripting:component doc:name="form survey_instance json">
            <scripting:script engine="Groovy"><![CDATA[ /*script*/ ]]></scripting:script>
        </scripting:component>
The script itself stores the payload to a variable so we have it in store in later phases, and creates a JSON string for inserting a survey_instance record.
/*store the original payload*/
flowVars['originalPayload'] = originalPayload;
/*form json for survey_instance*/
formatter = new java.text.SimpleDateFormat('yyyy-MM-dd kk:mm:ss');
survey_instance = [sysparm_action:'insert', survey:'c73b74780a0a0b0f00bd66a7805db324', taken_on:formatter.format(new java.util.Date())];
builder = new groovy.json.JsonBuilder(survey_instance);
builder.toString();
Insert survey_instance
This HTTPS outbound endpoint POSTs the payload JSON to a ServiceNow instance and transforms the response to a string.
<https:outbound-endpoint exchange-pattern="request-response" host="<HOST_HERE>" port="443" path="survey_instance.do?JSON" method="POST" mimeType="application/json" user="<USERNAME>" password="<PASSWORD>" contentType="application/json" doc:name="insert survey_instance">
        <response>
            <object-to-string-transformer />
        </response>
 </https:outbound-endpoint>
Form survey_responses json
<scripting:component doc:name="form survey_response json">
            <scripting:script engine="Groovy"><![CDATA[/*script*/]]></scripting:script>
        </scripting:component>
Script itself creates an object from the payload string, which contains the newly created survey_instance's sys_id. The sys_id is needed for linking the new survey_responses to the correct survey_instance. And then creates the JSON for inserting the two survey_responses.
response = new groovy.json.JsonSlurper().parseText(payload);
instance_id = response.records[0].sys_id;
log.info("instance_id:"+instance_id);

response_arr = [];
response_arr.push([instance: instance_id, question: 'c87146820a0a0b0f005d8098d76aca8b', response: flowVars['originalPayload'].number, answer_integer:flowVars['originalPayload'].number]);
response_arr.push([instance: instance_id, question: '0975dd38c0a80a6b00dd413133ba2aac', response: flowVars['originalPayload'].free_text, answer: flowVars['originalPayload'].free_text]);
message = [records: response_arr];
builder = new groovy.json.JsonBuilder(message);
builder.toString();
Insert survey_responses
The last component is responsible for POSTing the JSON payload to ServiceNow for creating the survey_responses.
<https:outbound-endpoint exchange-pattern="request-response" host="<HOST_HERE>" port="443" path="survey_response.do?JSON&sysparm_action=insert" method="POST" user="<USERNAME>" password="<PASSWORD>" contentType="application/json" doc:name="insert survey_responses"/>

Must do in real life implementations

  • Host the survey page someplace else
  • (update the task_survey record to link to the survey_instance if you need to see the whole chain from task to survey_responses
  • You would definitely need to implement an exception strategy into the application, which would return an error page.
  • Check the input
  • Serve a static thank you page when everything goes well. This example will return the JSON payload coming back from the ServiceNow platform. In essence the full survey_response records just created.
  • Put the 'S' as in Secure to the inbound HTTP components.

No comments:

Post a Comment