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">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.
<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>
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">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.
<scripting:script engine="Groovy"><![CDATA[ /*script*/ ]]></scripting:script>
</scripting:component>
/*store the original payload*/Insert survey_instance
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();
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">Form survey_responses json
<response>
<object-to-string-transformer />
</response>
</https:outbound-endpoint>
<scripting:component doc:name="form survey_response json">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.
<scripting:script engine="Groovy"><![CDATA[/*script*/]]></scripting:script>
</scripting:component>
response = new groovy.json.JsonSlurper().parseText(payload);Insert survey_responses
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();
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