The third use case in the Producteev business scenario is project collaboration and notification. This page describes the relevant tasks and the operations you use in the Producteev connector and the other ESB connectors. It contains the following sections:
Overview
The flow for project collaboration and notification is illustrated in the following diagram. The ESB connectors for PagerDuty, BugHerd, Zoho CRM and Mandrill will be used to connect to each service.
Retrieving tasks and creating issues
Prerequisites
There must be tasks created and assigned in Producteev.
- Retrieve all active projects from the BugHerd API using the listProjects operation.
- Retrieve all active tasks from the Producteev API using the listTasks operation.
- Retrieve the details of the case from the Zoho CRM API using the getRecordsById operation.
- Create a task in the BugHerd API using the createProjectTask operation.
- Add a description for the task as a comment in the BugHerd API using the addTaskComment operation.
- Create a triggered incident in the PagerDuty API by setting Producteev task ID as the Incident Key using the createIncident operation.
- Retrieve the triggered incidents that were created, using the listIncidents operation in the PagerDuty API
- Retrieve user by assignee e-mail in the PagerDuty API using the listUsers operation.
- Assign a user to the incident using the updateIncident operation in the PagerDuty API.
- Add a description for the task as a note in the PagerDuty API using the createNote operation.
- Create a note for the task by adding bug or incident ID in the Producteev API using the createNote operation.
Note
PagerDuty and BugHerd automatically notify the assignee of the Incident/Bug once the Incident/Bug is created and assigned to the user in PagerDuty and BugHerd respectively.
Producteev operations
Zoho CRM operations
BugHerd operations
PagerDuty operations
Samples
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright (c) 2005-2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. WSO2 Inc. licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <!-- This proxy is used to retrieve active tasks from Producteev and create them either as bugs in Bugherd or incidents in PagerDuty. --> <proxy xmlns="http://ws.apache.org/ns/synapse" name="producteev_retrieveTasksAndCreateIssues" transports="https,http" statistics="disable" trace="disable" startOnLoad="true"> <target> <inSequence onError="faultHandlerSeq"> <!-- Bugherd properties --> <property name="bugherd.apiUrl" value="https://www.bugherd.com" /> <property name="bugherd.apiKey" expression="json-eval($.bugherd.apiKey)" /> <!-- Producteev properties --> <property name="producteev.apiUrl" value="https://www.producteev.com" /> <property name="producteev.accessToken" expression="json-eval($.producteev.accessToken)" /> <property name="producteev.networkId" expression="json-eval($.producteev.networkId)" /> <!-- PagerDuty properties --> <property name="pagerduty.apiUrl" expression="json-eval($.pagerduty.apiUrl)" /> <property name="pagerduty.apiToken" expression="json-eval($.pagerduty.apiToken)" /> <property name="pagerduty.serviceKey" expression="json-eval($.pagerduty.serviceKey)" /> <property name="pagerduty.serviceId" expression="json-eval($.pagerduty.serviceId)" /> <property name="pagerduty.requesterId" expression="json-eval($.pagerduty.modifierId)" /> <!-- ZohoCRM properties --> <property name="zohocrm.apiUrl" value="https://crm.zoho.com" /> <property name="zohocrm.accessToken" expression="json-eval($.zohocrm.accessToken)" /> <property name="id.empty" value="{}" /> <!-- Call the sequence to extract all projects and users from Bugherd. --> <sequence key="retrieveTasksAndCreateIssues_verifyPrerequisites" /> <script language="js"> <![CDATA[ //Set networkId as a json array. var networkId = mc.getProperty('producteev.networkId'); var networkIdArray = [networkId]; mc.setPayloadJSON(networkIdArray); ]]> </script> <property name="producteev.networkId" expression="json-eval($)" /> <!-- List all active tasks in Producteev. --> <producteev.init> <accessToken>{$ctx:producteev.accessToken}</accessToken> <apiUrl>{$ctx:producteev.apiUrl}</apiUrl> </producteev.init> <producteev.listTasks> <sort>created_at</sort> <alias>active</alias> <order>asc</order> <networksCriteria>{$ctx:producteev.networkId}</networksCriteria> </producteev.listTasks> <!-- Removing unused headers. --> <sequence key="removeResponseHeaders" /> <property name="producteev.tasks" expression="json-eval($.tasks)" /> <!-- START of Filter: Proceed to process the tasks only if the listTasks call is successful. --> <filter source="boolean(get-property('producteev.tasks'))" regex="false"> <then> <property name="errorResponse" expression="json-eval($)" /> <!-- Failure case: Append an error message to be sent to the user. --> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="producteev_listTasks" /> <with-param name="id" value="{$ctx:id.empty}" /> <with-param name="status" value="Error" /> <with-param name="message" value="{$ctx:errorResponse}" /> </call-template> <loopback /> </then> <else> <property name="producteev.taskCount" expression="count(//jsonObject/tasks)" /> <property name="producteev.taskIndex" expression="0" scope="operation" /> <property name="createdTaskCount" expression="0" scope="operation" /> <!-- START of Filter: Proceed to process the tasks if there are any. --> <filter source="get-property('producteev.taskCount')" regex="0.0"> <then> <!-- Failure case: Append an error message to be sent to the user. --> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="producteev_retrieveTasksAndCreateIssues" /> <with-param name="id" value="{$ctx:id.empty}" /> <with-param name="status" value="Skipped" /> <with-param name="message" value="There are no new tasks in Producteev to process." /> </call-template> <loopback /> </then> <else> <!-- FOR EACH tasks: BEGIN --> <iterate id="submissionsIterator" expression="//tasks" sequential="true" continueParent="false"> <target> <sequence> <property name="producteev.labelTitle" expression="//tasks/labels[1]/title" /> <property name="producteev.taskTitle" expression="json-eval($.tasks.title)" /> <property name="producteev.taskId" expression="json-eval($.tasks.id)" /> <property name="producteev.taskPriority" expression="json-eval($.tasks.priority)" /> <property name="producteev.assigneeName" expression="json-eval($.tasks.responsibles.firstname)" /> <property name="producteev.assigneeEmail" expression="json-eval($.tasks.responsibles.email)" /> <property name="producteev.projectId" expression="json-eval($.tasks.project.id)" /> <property name="producteev.projectTitle" expression="json-eval($.tasks.project.title)" /> <!-- Call the template to extract notes of a task of Producteev. --> <call-template target="retrieveTaskDetailsFromNotes"> <with-param name="producteev.apiUrl" value="{$ctx:producteev.apiUrl}" /> <with-param name="producteev.accessToken" value="{$ctx:producteev.accessToken}" /> <with-param name="producteev.taskId" value="{$ctx:producteev.taskId}" /> </call-template> <!-- START of Filter: Proceed only if ZohoCRM taskIs exists. --> <filter source="boolean(get-property('zohocrm.caseId'))" regex="true"> <then> <!-- START of Filter: Proceed if task is not already created in PagerDuty or Bugherd for the produteev task. --> <filter source="boolean(get-property('issueId'))" regex="false"> <then> <script language="js"> <![CDATA[ //5s timeout is added to keep the iterations sequential. java.lang.Thread.sleep(5000); ]]> </script> <!-- Get the details of the case. --> <zohocrm.init> <scope>crmapi</scope> <accessToken>{$ctx:zohocrm.accessToken}</accessToken> <apiUrl>{$ctx:zohocrm.apiUrl}</apiUrl> </zohocrm.init> <zohocrm.getRecordsById> <id>{$ctx:zohocrm.caseId}</id> <newFormat>1</newFormat> <version>1</version> <moduleType>Cases</moduleType> </zohocrm.getRecordsById> <!-- Removing unused headers. --> <sequence key="removeResponseHeaders" /> <property name="caseResponse" expression="json-eval($.response.result.Cases.row.FL)" /> <script language="js"> <![CDATA[ //Extract client email from response. var caseResponse = eval("(" + mc.getProperty("caseResponse") + ")"); for (var i = 0; i < caseResponse.length ; i++) { if(caseResponse[i].val =="Email"){ mc.setProperty('clientEmail', caseResponse[i].content); } } ]]> </script> <!-- Proceed if the task label either 'Bug' or 'Production-Issue'. --> <switch source="get-property('producteev.labelTitle')"> <case regex="Bug"> <property name="createdTaskCount" expression="get-property('operation', 'createdTaskCount') + 1" scope="operation" /> <!-- Call the sequence to create a bug in Bugherd. --> <sequence key="createBug" /> </case> <case regex="Production-Issue"> <property name="createdTaskCount" expression="get-property('operation', 'createdTaskCount') + 1" scope="operation" /> <!-- Call the sequence to create a incident in PagerDuty. --> <sequence key="createIncident" /> </case> </switch> <!-- START of Filter: Proceed only if either task or incident is created.--> <filter source="boolean(get-property('append.taskId'))" regex="true"> <then> <property name="note" expression="fn:concat('Issue ID:', get-property('append.taskId'))" /> <!-- Remove property for prevent the setting status value for payload. --> <property name="uri.var.status" action="remove" /> <!-- Create a not for the task by adding bug or incident id. --> <producteev.init> <accessToken>{$ctx:producteev.accessToken}</accessToken> <apiUrl>{$ctx:producteev.apiUrl}</apiUrl> </producteev.init> <producteev.createNote> <message>{$ctx:note}</message> <taskId>{$ctx:producteev.taskId}</taskId> </producteev.createNote> <!-- Removing unused headers. --> <sequence key="removeResponseHeaders" /> <property name="response.noteId" expression="json-eval($.note.id)" /> <!-- START of Filter: Set message to be send to user according to the status of adding note.--> <filter source="boolean(get-property('response.noteId'))" regex="false"> <then> <property name="id" expression="fn:concat('producteev_taskId:', get-property('producteev.taskId'))" /> <property name="errorResponse" expression="fn:concat('Could not add a note to the task due to an error.')" /> <!-- Append error message to be sent to the user. --> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="producteev_createNote" /> <with-param name="id" value="{$ctx:id}" /> <with-param name="status" value="Error" /> <with-param name="message" value="{$ctx:errorResponse}" /> </call-template> </then> </filter><!-- END of Filter: Set message to be send to user according to the status of adding note.--> </then> </filter><!-- END of Filter: Proceed only if either task or incident is created. --> </then> </filter><!-- END of Filter: Proceed if task is not already created in pagerdury or Bugherd for the produteev task. --> </then> </filter><!-- END of Filter: Proceed if task is not already created in pagerdury or Bugherd for the produteev task. --> <property name="producteev.taskIndex" expression="get-property('operation', 'producteev.taskIndex') + 1" scope="operation" /> <!-- START of Filter: Move to outSequence when all the iterations are done. --> <filter xpath="get-property('producteev.taskCount') = get-property('operation', 'producteev.taskIndex')"> <then> <filter xpath="get-property('operation', 'createdTaskCount') = 0"> <then> <!-- Append skipped message to be sent to the user. --> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="producteev_retrieveTasksAndCreateIssues" /> <with-param name="id" value="{$ctx:id.empty}" /> <with-param name="status" value="Skipped" /> <with-param name="message" value="There are no new tasks in Producteev to process." /> </call-template> </then> </filter> <loopback /> </then> </filter><!-- END of Filter: Move to outSequence when all the iterations are done. --> </sequence> </target> </iterate><!-- FOR EACH tasks: END. --> </else> </filter><!-- END of Filter: Proceed to process the task if there are any. --> </else> </filter><!-- END of Filter: Proceed to process the tasks only if the listTasks call is successful. --> </inSequence> <outSequence> <payloadFactory media-type="json"> <format>{ "Response":{ "process":"producteev_retrieveTasksAndCreateIssues", "activityResponse":[$1] } }</format> <args> <arg expression="get-property('operation','responseString')" /> </args> </payloadFactory> <property name="messageType" value="application/json" scope="axis2" /> <send /> </outSequence> </target> </proxy>
{ "bugherd":{ "apiKey":"rxgayi60zqsuqp2plzgk1w" }, "producteev":{ "accessToken": "NTBiMzkzOTIxOWIzMmZiNjg1ZTFmYzRjMmMzODVmMzVkZmQ3ODg4YzUwNWQxNTI2YjI3ODYyYjdjZTJiNjZmNg", "networkId":"55cc21aeb2fa090468000000" }, "pagerduty":{ "apiUrl" : "https://barns-software.pagerduty.com/", "apiToken":"fc9AGY9ajq62UsEByXVR", "serviceKey" : "14f2127889f34d50b7ec8d8ca3947449", "serviceId" : "PZOLS9U", "modifierId" : "PRWYJFY" }, "zohocrm":{ "accessToken":"5c369f3acbbbd0123d6312db3f68c273" } }
Note
The following are the parameter descriptions:
The ID of the Producteev network.producteev.networkId:
The API Key of the PagerDuty service.pagerduty.serviceKey:
The ID key of the PagerDuty service.pagerduty.serviceId:
The ID of a team member who exists in the PagerDuty account.pagerduty.modifierId:
All the rest of the parameters are API URLs and authentication credentials to access each API specified in the request. Refer to the individual connector methods for clarification.
Updating tasks and notifying clients
- Retrieve all the incidents that are resolved from the PagerDuty API using the listIncidents operation.
- Check if the incident is already closed in the Producteev API using the getTask operation.
- Retrieve the resolution added in the PagerDuty API using the listNotes operation.
- Retrieve all the projects in the BugHerd API using the listProjects operation.
- Retrieve all the tasks of the project that are resolved using the listProjectTasks operation in the BugHerd API.
- Retrieve the note details from the Producteev task using the listTaskComments in the BugHerd API.
- Add a note to the Producteev task containing the resolution of the Bug in BugHerd or Incident in PagerDuty using the createNote operation in the Producteev API.
- Update the Producteev task as 'Resolved' using the updateTask operation in the Producteev API.
- Retrieve the requester's name from the Zoho CRM case using the getRecordsById operation in the Zoho CRM API.
- Send a message to the requester of the ticket informing that the task is resolved with the given resolution using the sendMessage operation in the Mandrill API.
- Update the appropriate case in the Zoho CRM API with the resolution of the task and the status as 'Resolved' using the updateRecords operation in the Zoho CRM API.
- if the task type is 'Bug' then update the BugHerd task status as 'Closed' using the updateProjectTask operation in the BugHerd API
Note
There should be a comment added in each of the Bug/Incident on resolving the ticket as an offline process.
PagerDuty operations
Produteev operations
BugHerd operations
Zoho CRM operations
Mandrill operations
Prerequisites
- Assignee of the bug/incident should add a comment/note in BugHerd/PagerDuty respectively before executing this case in the format of 'Task Resolution:<The relevant resolution of the task>'. (e.g:- 'Task Resolution:Correct logo added with proper resolution.')
- Once the resolution is added, the bug's/incident's status needs to be changed as 'Done' (in BugHerd) or 'Resolved' (in PagerDuty).
Samples
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright (c) 2005-2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. WSO2 Inc. licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <!--This proxy will retrieve all the resolved bugs in Bugherd and incidents in PagerDuty, notify the requesters of them via Mandrill and finally update the resolution and the status of them in Producteev and ZohoCrm accordingly. --> <proxy xmlns="http://ws.apache.org/ns/synapse" name="producteev_updateTasksAndNotifyClients" transports="https,http" statistics="disable" trace="disable" startOnLoad="true"> <target> <inSequence> <!-- Bugherd Properties. --> <property name="bugherd.apiUrl" value="https://www.bugherd.com" /> <property name="bugherd.apiKey" expression="json-eval($.bugherd.apiKey)" /> <!-- PagerDuty Properties. --> <property name="pagerduty.apiUrl" expression="json-eval($.pagerduty.apiUrl)" /> <property name="pagerduty.apiToken" expression="json-eval($.pagerduty.apiToken)" /> <property name="pagerduty.serviceId" expression="json-eval($.pagerduty.serviceId)" /> <!-- Mandrill Properties. --> <property name="mandrill.apiUrl" value="https://mandrillapp.com" /> <property name="mandrill.apiKey" expression="json-eval($.mandrill.apiKey)" /> <property name="mandrill.fromEmail" expression="json-eval($.mandrill.fromEmail)" /> <property name="mandrill.fromName" expression="json-eval($.mandrill.fromName)" /> <!-- Producteev Properties. --> <property name="producteev.apiUrl" value="https://www.producteev.com" /> <property name="producteev.accessToken" expression="json-eval($.producteev.accessToken)" /> <!-- ZohoCrm Properties. --> <property name="zohocrm.apiUrl" value="https://crm.zoho.com" /> <property name="zohocrm.accessToken" expression="json-eval($.zohocrm.accessToken)" /> <!-- Operation scoped properties. --> <property name="id.empty" value="{}" /> <property name="responseString" value="" scope="operation" /> <property name="producteev.taskType" value="" scope="operation" /> <!--Listing down all the incidents in the PagerDuty account that are resolved. --> <pagerduty.init> <apiUrl>{$ctx:pagerduty.apiUrl}</apiUrl> <apiToken>{$ctx:pagerduty.apiToken}</apiToken> </pagerduty.init> <pagerduty.listIncidents> <dateRange>all</dateRange> <status>resolved</status> <service>{$ctx:pagerduty.serviceId}</service> <sortBy>asc</sortBy> </pagerduty.listIncidents> <!--Removing unused headers. --> <sequence key="removeResponseHeaders" /> <property name="pagerduty.httpStatusCode" expression="$axis2:HTTP_SC" /> <!--START: If there occurs an authentication failure in Pagerduty then call the 'processResolvedBugsAndNotifyRequester' sequence. --> <filter source="get-property('pagerduty.httpStatusCode')" regex="401"> <then> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="pagerduty_updateTasksAndNotifyClients" /> <with-param name="id" value="{$ctx:id.empty}" /> <with-param name="status" value="Error" /> <with-param name="message" value="Failed to authenticate Pagerduty API.Please verify the authentication credentials." /> </call-template> <!--Call the sequence in order to process the resolved Bugs in Bugherd and notify the requesters. --> <sequence key="processResolvedBugsAndNotifyRequester" /> </then> <else> <!--Checking the availability of resolved incidents. --> <property name="pagerDuty.resolvedIncidentsCount" expression="count(//incidents)" scope="operation" /> <property name="pagerDuty.resolvedIncidentsIndex" expression="0" scope="operation" /> <!--START: If the resolved incidents count is zero then handle the failure case. --> <filter source="get-property('operation','pagerDuty.resolvedIncidentsCount')" regex="0.0"> <then> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="pagerduty_updateTasksAndNotifyClients" /> <with-param name="id" value="{$ctx:id.empty}" /> <with-param name="status" value="Skipped" /> <with-param name="message" value="There are no resolved incidents in the provided service to process." /> </call-template> <!--Call the sequence in order to process the resolved Bugs in Bugherd and notify the requesters. --> <sequence key="processResolvedBugsAndNotifyRequester" /> </then> <else> <property name="producteev.activeTaskCount" expression="0" scope="operation" /> <!-- BEGIN: FOR EACH resolved incident --> <iterate continueParent="false" id="resolvedIncidentsIterator" expression="//incidents" sequential="true"> <target> <sequence> <!--Retrieve the ticket reference. --> <property name="pagerDuty.incidentId" expression="//incidents/id/text()" /> <property name="pagerDuty.incidentKey" expression="//incidents/incident_key/text()" /> <property name="pagerDuty.incidentDescription" expression="//trigger_summary_data/description/text()" /> <property name="pagerDuty.incidentClientEmail" expression="//trigger_summary_data/client/text()" /> <!--Checking whether the incident is already closed in producteev. --> <producteev.init> <apiUrl>{$ctx:producteev.apiUrl}</apiUrl> <accessToken>{$ctx:producteev.accessToken}</accessToken> </producteev.init> <producteev.getTask> <taskId>{$ctx:pagerDuty.incidentKey}</taskId> </producteev.getTask> <!--Removing unused headers. --> <sequence key="removeResponseHeaders" /> <property name="producteev.taskStatus" expression="//task/status/text()" /> <!--START: Retrieve only the tasks in Producteev which are not already closed. --> <filter xpath="get-property('producteev.taskStatus')!=0"> <then> <property name="producteev.activeTaskCount" expression="get-property('operation','producteev.activeTaskCount')+1" scope="operation" /> <property name="producteev.taskSubject" expression="//task/title/text()" /> <!--Call the 'retrieveTaskDetailsFromNotes' template in order to retrieve the note details from the Producteev task. --> <call-template target="retrieveTaskDetailsFromNotes"> <with-param name="producteev.apiUrl" value="{$ctx:producteev.apiUrl}" /> <with-param name="producteev.accessToken" value="{$ctx:producteev.accessToken}" /> <with-param name="producteev.taskId" value="{$ctx:pagerDuty.incidentKey}" /> </call-template> <!--Retrieve the resolution added in Pagerduty. --> <pagerduty.init> <apiUrl>{$ctx:pagerduty.apiUrl}</apiUrl> <apiToken>{$ctx:pagerduty.apiToken}</apiToken> </pagerduty.init> <pagerduty.listNotes> <incidentId>{$ctx:pagerDuty.incidentId}</incidentId> </pagerduty.listNotes> <!--Removing unused headers. --> <sequence key="removeResponseHeaders" /> <property name="pagerDuty.responseNotes" expression="//notes" /> <property name="id" expression="fn:concat('pagerduty_incidentId:', get-property('pagerDuty.incidentId'))" /> <!--START : Checking whether there occurred an error while retrieving the notes of the resolved incident in PagerDuty. --> <filter source="boolean(get-property('pagerDuty.responseNotes'))" regex="false"> <then> <property name="pagerDuty.errorResponse" expression="json-eval($)" /> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="pagerduty_listNotes" /> <with-param name="id" value="{$ctx:id}" /> <with-param name="status" value="Error" /> <with-param name="message" value="{$ctx:pagerDuty.errorResponse}" /> </call-template> </then> <else> <property name="pagerDuty.incidentNotesCount" expression="count(//notes)" scope="operation" /> <!--START : Checking whether the resolution is added as a note in the Pagerduty incident. --> <filter source="get-property('operation','pagerDuty.incidentNotesCount')" regex="0.0"> <then> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="pagerduty_listNotes" /> <with-param name="id" value="{$ctx:id}" /> <with-param name="status" value="Skipped" /> <with-param name="message" value="There are no notes added to the incident thus the resolution could not be retrieved." /> </call-template> </then> <else> <!--FOR EACH notes : BEGIN --> <iterate continueParent="true" id="pagerDutyIncidentNotes" expression="//notes" sequential="true"> <target> <sequence> <property name="pagerduty.noteContent" expression="json-eval($.notes.content)" /> <!--Retrieving the resolution and the task description of the incident from notes. --> <script language="js"><![CDATA[ var noteContent = mc.getProperty('pagerduty.noteContent'); if(noteContent.indexOf("Task Resolution:")!=-1){ var noteStringArray = noteContent.split('Task Resolution:'); mc.setProperty('taskResolution',noteStringArray[1]); } if(noteContent.indexOf("Task Description:")!=-1){ var noteStringArray = noteContent.split('Task Description:'); mc.setProperty('taskDescription',noteStringArray[1]); } ]]></script> <property name="producteev.message" expression="get-property('taskResolution')" scope="operation" /> <!-- Set Producteev task description to operation scope --> <filter source="boolean(get-property('taskDescription'))" regex="true"> <then> <property name="producteev.taskDescription" expression="get-property('taskDescription')" /> </then> </filter> </sequence> </target> </iterate> <!-- Call the sequence to perform the following activities ; ACTIVITY 1: Add a note to Producteev task containing the resolution of the Incident in Pagerduty. ACTIVITY 2: Update the Producteev task as 'resolved'. ACTIVITY 3: Send a message to the requester of the ticket via Mandrill informing that the task is resolved with the given resolution. ACTIVITY 4: Update the appropriate Case in ZohoCrm with setting the resolution of the task and the status as resolved. --> <property name="producteev.taskId" expression="get-property('pagerDuty.incidentKey')" /> <property name="producteev.clientEmail" expression="get-property('pagerDuty.incidentClientEmail')" /> <sequence key="updateTaskAndSendMessage" /> </else> </filter><!--END : Checking whether the resolution is added as a note in the Pagerduty incident. --> </else> </filter><!--END : Checking whether there occurred an error while retrieving the notes of the resolved incident in Pagerduty. --> </then> </filter><!--END: Retrieve only the tasks in Producteev which are not already closed. --> <!--Incrementing the index --> <property name="pagerDuty.resolvedIncidentsIndex" expression="get-property('operation','pagerDuty.resolvedIncidentsIndex') + 1" scope="operation" /> <filter xpath="get-property('operation','pagerDuty.resolvedIncidentsCount') = get-property('operation', 'pagerDuty.resolvedIncidentsIndex')"> <then> <filter source="get-property('operation','producteev.activeTaskCount')" regex="0.0"> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="pagerduty_updateTasksAndNotifyClients" /> <with-param name="id" value="{$ctx:id.empty}" /> <with-param name="status" value="Skipped" /> <with-param name="message" value="There are no unresolved incidents to process." /> </call-template> </filter> <!--Call the sequence in order to process the resolved bugs in Bugherd and notify the requesters. --> <sequence key="processResolvedBugsAndNotifyRequester" /> </then> </filter> </sequence> </target> </iterate><!-- END: FOR EACH resolved incident --> </else> </filter><!--END: If the resolved incidents count is zero then handle the failure case. --> </else> </filter> <!--END of Filter: If there occurs an authentication failure in Pagerduty then loopback. --> </inSequence> <outSequence> <!-- Send the constructed response to the user. --> <payloadFactory media-type="json"> <format>{ "Response":{ "process":"producteev_updateTasksAndNotifyClients", "activityResponse": [$1] } } </format> <args> <arg expression="get-property('operation', 'responseString')" /> </args> </payloadFactory> <send /> </outSequence> </target> </proxy>
{ "bugherd":{ "apiKey":"jy8jdaxnjw11lhsxsxkdwq" }, "pagerduty":{ "apiUrl":"https://producteev-sc-demo.pagerduty.com", "apiToken":"tjZ29LYMu7nXep8mhqRN", "serviceId":"P49PBPP" }, "mandrill":{ "apiKey":"sLBf_3dh5OKosfqs7CTkIA", "fromEmail":"producteev.sc.demo@gmail.com", "fromName":"Barns Software Services" }, "producteev":{ "accessToken":"ZTQ1NmJiMzllZGY5NzQzNmJlOTQ5YTJiYzcxZDk3MGFjODAxOWFiMmJhNDk4N2FlNTg1ZWI0NDNjZGNlMzVkOA" }, "zohocrm":{ "accessToken":"5c369f3acbbbd0123d6312db3f68c273" } }
Note
The following are the parameter descriptions:
The ID of the PagerDuty network.pagerduty.serviceId :
The notification e-mail sender's e-mail address.mandrill.fromEmail:
The notification e-mail sender's name.mandrill.fromName:
All the rest of the parameters are API URLs and authentication credentials to access each API specified in the request. Refer to the individual connector methods for clarification.
Notifying assignees about overdue tasks
- Retrieve overdue tasks from the Producteev API using the listTasks operation.
- Send a message regarding the overdue task/incident to the task/incident assignee via the Mandrill API using the sendMessage operation.
Mandrill operations
Producteev operations
Samples
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright (c) 2005-2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. WSO2 Inc. licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <!-- This template send reminder email to task assignee via Mandrill API --> <template xmlns="http://ws.apache.org/ns/synapse" name="sendReminderEmail"> <!-- Mandrill related template parameters --> <parameter name="mandrill.apiUrl" description="API url of Mandrill." /> <parameter name="mandrill.apiKey" description="Authintication Key of Mandrill." /> <parameter name="mandrill.fromName" description="Name of the issue assigner." /> <parameter name="mandrill.fromEmail" description="Email address of the issue assigner." /> <!-- Common template parameters --> <parameter name="common.id" description="ID of the resource."/> <parameter name="issueId" description="ID of the issue." /> <parameter name="issueSubject" description="Subject of the issue." /> <parameter name="issueDescription" description="Description of the issue." /> <parameter name="serviceOrProjectTitle" description="Service name or Project name of the issue related to." /> <parameter name="issueDeadline" description="Deadline of the issue." /> <parameter name="issueAssigneeEmail" description="Assignee Email of the issue." /> <parameter name="issueAssigneeFirstName" description="First name of the issue assignee." /> <parameter name="source" description="Source name of the issue (Possible values are 'Bugerd' and 'Pagerduty')." /> <parameter name="priorityId" description="ID of the Producteev task priority." /> <sequence> <!-- Mandrill related template properties --> <property name="mandrill.apiUrl" expression="$func:mandrill.apiUrl" /> <property name="mandrill.apiKey" expression="$func:mandrill.apiKey" /> <property name="mandrill.fromName" expression="$func:mandrill.fromName" /> <property name="mandrill.fromEmail" expression="$func:mandrill.fromEmail" /> <!-- Common template properties --> <property name="common.id" expression="$func:common.id" /> <property name="issueId" expression="$func:issueId" /> <property name="issueSubject" expression="$func:issueSubject" /> <property name="issueDescription" expression="$func:issueDescription" /> <property name="serviceOrProjectTitle" expression="$func:serviceOrProjectTitle" /> <property name="issueDeadline" expression="$func:issueDeadline" /> <property name="issueAssigneeEmail" expression="$func:issueAssigneeEmail" /> <property name="issueAssigneeFirstName" expression="$func:issueAssigneeFirstName" /> <property name="source" expression="$func:source" /> <property name="priorityId" expression="$func:priorityId" /> <switch source="get-property('source')"> <case regex="Bugherd"> <property name="issueType" value="Task" /> <property name="issueContainer" value="Project" /> </case> <case regex="Pagerduty"> <property name="issueType" value="Incident" /> <property name="issueContainer" value="Service" /> </case> </switch> <switch source="get-property('priorityId')"> <case regex="0"> <property name="priority" value="None" /> </case> <case regex="1"> <property name="priority" value="Very Low" /> </case> <case regex="2"> <property name="priority" value="Low" /> </case> <case regex="3"> <property name="priority" value="Medium" /> </case> <case regex="4"> <property name="priority" value="High" /> </case> <case regex="5"> <property name="priority" value="Very High" /> </case> </switch> <!-- Send a message to the task/incident assignee via Mandrill informing the overdue task/incident. --> <script language="js"> <![CDATA[ var issueSubject = mc.getProperty('issueSubject'); var issueDescription = mc.getProperty('issueDescription'); var serviceOrProjectTitle = mc.getProperty('serviceOrProjectTitle'); var issueDeadline = mc.getProperty('issueDeadline'); var issueAssigneeEmail = mc.getProperty('issueAssigneeEmail'); var issueAssigneeFirstName = mc.getProperty('issueAssigneeFirstName'); var source = mc.getProperty('source'); var issueType = mc.getProperty('issueType'); var issueContainer = mc.getProperty('issueContainer'); var issueId = mc.getProperty('issueId'); var priority=mc.getProperty('priority'); var fromName=mc.getProperty('mandrill.fromName'); var to = '[{"email": "' + issueAssigneeEmail + '"}]'; var mailSubject = 'REMINDER: Overdue '+issueType.toLowerCase()+ ' in '+ source; var mailContent = "<style>table.line, td.line {border: 1px solid #33CCFF;border-collapse: collapse;}</style><h3>Dear "+issueAssigneeFirstName+",</h3><p>There is an overdue "+issueType.toLowerCase()+" in "+source+" that is assign to you needs urgent attention."+"</p><p>Please find the details as below.</p><table class=line><tr style=background-color:#33CCFF><td class=line colspan='2'>"+issueType+" Details</td></tr><tr><td class=line>"+issueContainer+" Name</td><td class=line>"+serviceOrProjectTitle+"</td></tr><tr><td class=line style=width:100px;>"+issueType+" ID</td><td class=line style=width:300px;>"+issueId+"</td></tr><tr><td class=line style=width:100px;>"+issueType+" Subject</td><td class=line style=width:300px;>"+issueSubject+"</td></tr><tr><td class=line>"+issueType+" Description</td><td class=line>"+issueDescription+"</td></tr><tr><td class=line>"+issueType+" Priority</td><td class=line>"+priority+"</td></tr><tr><td class=line>"+issueType+" Status</td><td class=line>Open</td></tr></table><p>Kind Regards,</p><p><b>"+fromName+"</b></p>"; mc.setProperty('mandrill.to', to); mc.setProperty('mandrill.requester.html', mailContent); mc.setProperty('mandrill.subject', mailSubject); ]]> </script> <property name="mandrill.html" expression="fn:concat('<html>', get-property('mandrill.requester.html'), '</html>')" /> <mandrill.init> <apiUrl>{$ctx:mandrill.apiUrl}</apiUrl> <apiKey>{$ctx:mandrill.apiKey}</apiKey> <format>json</format> </mandrill.init> <mandrill.sendMessage> <html>{$ctx:mandrill.html}</html> <subject>{$ctx:mandrill.subject}</subject> <fromEmail>{$ctx:mandrill.fromEmail}</fromEmail> <fromName>{$ctx:mandrill.fromName}</fromName> <to>{$ctx:mandrill.to}</to> </mandrill.sendMessage> <property name="mandril.sentId" expression="json-eval($.[0]._id)" /> <property name="source.idLabel" expression="fn:concat(lower-case(get-property('source')),'_',get-property('issueType'),'Id:')"/> <!-- Returning Success response status when the email has been sent --> <filter source="boolean(get-property('mandril.sentId'))" regex="false"> <then> <property name="id" expression="fn:concat('producteev_taskId:',get-property('common.id'),',',get-property('source.idLabel'),get-property('issueId'))" /> <property name="status" value="Error" /> <property name="message" expression="json-eval($)" /> </then> <else> <property name="id" expression="fn:concat('producteev_taskId:',get-property('common.id'),',',get-property('source.idLabel'),get-property('issueId'))" /> <property name="status" value="Success" /> <property name="message" expression="fn:concat('Assignee (',get-property('issueAssigneeEmail'),') is reminded about the overdue task successfully.')" /> </else> </filter> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="mandrill_notifyAssignee" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> </sequence> </template>
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright (c) 2005-2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. WSO2 Inc. licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <!-- Retrieving overdue tasks from Producteev API and notify to assignees using Mandrill API --> <proxy xmlns="http://ws.apache.org/ns/synapse" name="producteev_notifyAssigneesAboutOverdueTasks" transports="https,http" statistics="disable" trace="disable" startOnLoad="true"> <target> <inSequence onError="faultHandlerSeq"> <!--Producteev Properties --> <property name="producteev.apiUrl" value="https://www.producteev.com" /> <property name="producteev.accessToken" expression="json-eval($.producteev.accessToken)" /> <!--BugHerd Properties --> <property name="bugherd.apiUrl" value="https://www.bugherd.com" /> <property name="bugherd.apiKey" expression="json-eval($.bugherd.apiKey)" /> <!-- PagerDuty Properties --> <property name="pagerduty.apiUrl" expression="json-eval($.pagerduty.apiUrl)" /> <property name="pagerduty.apiToken" expression="json-eval($.pagerduty.apiToken)" /> <!-- Mandrill Properties --> <property name="mandrill.apiUrl" value="https://mandrillapp.com" /> <property name="mandrill.apiKey" expression="json-eval($.mandrill.apiKey)" /> <property name="mandrill.fromName" expression="json-eval($.mandrill.fromName)" /> <property name="mandrill.fromEmail" expression="json-eval($.mandrill.fromEmail)" /> <!-- Calling listTasks method in Producteev API to retrieve overdue tasks --> <producteev.init> <apiUrl>{$ctx:producteev.apiUrl}</apiUrl> <accessToken>{$ctx:producteev.accessToken}</accessToken> </producteev.init> <producteev.listTasks> <sort>created_at</sort> <alias>late</alias> <order>asc</order> </producteev.listTasks> <property name="tasksStatusCode" expression="$axis2:HTTP_SC" /> <!-- Validating the listTasks response --> <filter xpath="get-property('tasksStatusCode')!=200"> <then> <property name="id" value="{}" /> <property name="status" value="Error" /> <property name="message" value="Error while retrieving tasks from Producteev API." /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="producteev_listTasks" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> <loopback /> </then> <else> <property name="producteev.taskCount" expression="count(//tasks)" scope="operation" /> <property name="producteev.taskIndex" expression="0" scope="operation" /> <!-- Process only if overdue tasks are available --> <filter xpath="get-property('operation','producteev.taskCount')=0.0"> <then> <property name="id" value="{}" /> <property name="status" value="Skipped" /> <property name="message" value="There are no any overdue task(s)." /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="producteev_notifyAssigneesAboutOverdueTasks" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> <loopback /> </then> <else> <!--FOR EACH tasks : BEGIN --> <iterate continueParent="true" id="tasks" expression="//tasks" sequential="true"> <target> <sequence> <property name="producteev.taskId" expression="//tasks/id/text()" /> <property name="producteev.taskLabel" expression="//tasks/labels[1]/title/text()" /> <property name="producteev.taskTitle" expression="//tasks/title/text()" /> <property name="producteev.projectTitle" expression="//tasks/project/title/text()" /> <property name="producteev.taskDeadline" expression="//tasks/deadline/text()" /> <property name="producteev.taskAssigneeEmail" expression="//tasks/responsibles/email/text()" /> <property name="producteev.taskAssigneeFirstName" expression="//tasks/responsibles/firstname/text()" /> <property name="producteev.taskCreatorEmail" expression="//tasks/creator/email/text()" /> <property name="producteev.taskCreatorFirstName" expression="//tasks/creator/firstname/text()" /> <property name="producteev.taskPriority" expression="//tasks/priority/text()" /> <!-- Calling retrieveTaskDetailsFromNotes template to retrieve task details from notes --> <call-template target="retrieveTaskDetailsFromNotes"> <with-param name="producteev.apiUrl" value="{$ctx:producteev.apiUrl}" /> <with-param name="producteev.accessToken" value="{$ctx:producteev.accessToken}" /> <with-param name="producteev.taskId" value="{$ctx:producteev.taskId}" /> </call-template> <property name="issueDescription" expression="get-property('producteev.taskDescription')" /> <property name="issueId" expression="get-property('issueId')" /> <switch source="get-property('producteev.taskLabel')"> <case regex="Production-Issue"> <filter source="boolean(get-property('issueId'))" regex="false"> <then> <property name="id" expression="fn:concat('producteev_taskId:',get-property('producteev.taskId'))" /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="mandril_notifyAssignee" /> <with-param name="status" value="Skipped" /> <with-param name="message" value="Pagerduty incident ID not found." /> </call-template> </then> <else> <!-- Calling sendReminderEmail template to send email notification for overdue incidents in PagerDuty API --> <call-template target="sendReminderEmail"> <with-param name="issueSubject" value="{$ctx:producteev.taskTitle}" /> <with-param name="issueDescription" value="{$ctx:issueDescription}" /> <with-param name="serviceOrProjectTitle" value="{$ctx:producteev.projectTitle}" /> <with-param name="issueDeadline" value="{$ctx:producteev.taskDeadline}" /> <with-param name="issueAssigneeEmail" value="{$ctx:producteev.taskAssigneeEmail}" /> <with-param name="issueAssigneeFirstName" value="{$ctx:producteev.taskAssigneeFirstName}" /> <with-param name="mandrill.fromName" value="{$ctx:mandrill.fromName}" /> <with-param name="mandrill.fromEmail" value="{$ctx:mandrill.fromEmail}" /> <with-param name="source" value="Pagerduty" /> <with-param name="issueId" value="{$ctx:issueId}" /> <with-param name="priorityId" value="{$ctx:producteev.taskPriority}" /> <with-param name="common.id" value="{$ctx:producteev.taskId}" /> </call-template> </else> </filter> </case> <case regex="Bug"> <filter source="boolean(get-property('issueId'))" regex="false"> <then> <property name="id" expression="fn:concat('producteev_taskId:',get-property('producteev.taskId'))" /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="mandrill_notifyAssignee" /> <with-param name="status" value="Skipped" /> <with-param name="message" value="Bugherd task ID not found." /> </call-template> </then> <else> <!-- Calling sendReminderEmail template to send email notification for overdue tasks in BugHerd API --> <call-template target="sendReminderEmail"> <with-param name="issueSubject" value="{$ctx:producteev.taskTitle}" /> <with-param name="issueDescription" value="{$ctx:issueDescription}" /> <with-param name="serviceOrProjectTitle" value="{$ctx:producteev.projectTitle}" /> <with-param name="issueDeadline" value="{$ctx:producteev.taskDeadline}" /> <with-param name="issueAssigneeEmail" value="{$ctx:producteev.taskAssigneeEmail}" /> <with-param name="issueAssigneeFirstName" value="{$ctx:producteev.taskAssigneeFirstName}" /> <with-param name="mandrill.fromName" value="{$ctx:mandrill.fromName}" /> <with-param name="mandrill.fromEmail" value="{$ctx:mandrill.fromEmail}" /> <with-param name="source" value="Bugherd" /> <with-param name="issueId" value="{$ctx:issueId}" /> <with-param name="priorityId" value="{$ctx:producteev.taskPriority}" /> <with-param name="common.id" value="{$ctx:producteev.taskId}" /> </call-template> </else> </filter> </case> <default> <property name="id" expression="fn:concat('producteev_taskId:',get-property('producteev.taskId'))" /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="mandril_notifyAssignee" /> <with-param name="status" value="Skipped" /> <with-param name="message" value="Invalid task label value." /> </call-template> </default> </switch> <property name="producteev.taskIndex" expression="get-property('operation','producteev.taskIndex')+1" scope="operation" /> <filter xpath="get-property('operation','producteev.taskCount') = get-property('operation','producteev.taskIndex')"> <then> <loopback /> </then> </filter> </sequence> </target> </iterate> <!--FOR EACH tasks : END --> </else> </filter> </else> </filter> </inSequence> <outSequence> <payloadFactory media-type="json"> <format> { "Response":{ "process":"producteev_notifyAssigneesAboutOverdueTasks", "activityResponse":[$1] } } </format> <args> <arg expression="get-property('operation','responseString')" /> </args> </payloadFactory> <property name="messageType" value="application/json" scope="axis2" /> <send /> </outSequence> </target> </proxy>
{ "producteev":{ "accessToken":"ZTQ1NmJiMzllZGY5NzQzNmJlOTQ5YTJiYzcxZDk3MGFjODAxOWFiMmJhNDk4N2FlNTg1ZWI0NDNjZGNlMzVkOA" }, "bugherd":{ "apiKey":"rxgayi60zqsuqp2plzgk1w" }, "pagerduty":{ "apiUrl":"https://producteev-sc-demo.pagerduty.com", "apiToken":"tjZ29LYMu7nXep8mhqRN" }, "mandrill":{ "apiKey":"sLBf_3dh5OKosfqs7CTkIA", "fromName":"Red John", "fromEmail":"jedjohn@blakassociation.com" } }
Note
The following are the parameter descriptions:
The notification e-mail sender's e-mail address.mandrill.fromEmail:
The notification e-mail sender's name.mandrill.fromName:
All the rest of the parameters are API URLs and authentication credentials to access each API specified in the request. Refer to the individual connector methods for clarification.