FIX: SharePoint 2013 Workflow recursion prevention – Part 2
Following FIX: SharePoint 2013 Workflow recursion prevention – Part 1, this post will walk you through the processes of designing your workflow that creates a list item on another list and invokes the workflow associated with that list using the new REST APIs released through SharePoint 2013 May 2014 CU as pointed out in part 1 of this series. If you haven’t read through part 1 of this series I suggest you do to get some background that will help you appreciate this post.
List1 has a workflow Workflow1 associated with it.
Workflow1 can be set to auto-start on item adding/updating or set to manually start or both, it doesn’t matter.
Workflow1 is designed to create a list item on List2.
List2 has a workflow Workflow2 associated with it.
Workflow2 should have manually start workflow option enabled. (Allow this workflow to be manually started)
Workflow2 simply logs the title of the list item in workflow history list.
NOTE: Both Workflow1 and Workflow2 are built on 2013 workflow platform.
Publish both the workflows and create an item on List1. Manually start Workflow1 on the list item and you should see that it completes successfully.
Now browse to List2 and you will see that a new list item was successfully created by Workflow1.
But Workflow2 would never start off. Refer part 1 of this series, check ULS logs and you’ll see Workflow2 didn’t start because of workflow recursion prevention.
The fix is to leverage the new REST APIs exposed to start off Workflow2 from Workflow1. Let’s open Workflow1 in SharePoint Designer’s workflow editor.
We drop a “Build Dictionary” action just above the create item in list action with the following name/value pairs of type string.
Choose the dropdown against “(Output to Variable: dictionary)” and create a variable to type Dictionary named “ReqHeader”.
Choose the dropdown against “(Output to Variable: create)” and create a new variable named “NewItemGuid”. It will be of type GUID.
Drop a “Call HTTP Web Service” action after the create item in list action.
Click “this”, use the … button against the textbox titled “Enter the HTTP web service URL”. This will bring up string builder dialog box.
Click “Add or Change Lookup”, change “Data source” dropdown to select “Workflow Context” and for “Field from source” choose “Current Site URL” and hit OK.
Append /_api/web/lists/GetByTitle('List2')/items?$filter=GUID eq ' to the end of the current site URL token (watch out for the single open quote in the end, that is necessary too).
Click “Add or Change Lookup” again, change “Data source” dropdown to select “Workflow Variables and Parameters” and then choose “Variable: NewItemGuid” for “Field from source”. Hit OK.
The string builder dialog will now have the following string in it.
We are calling the REST method GetByTitle passing it the title of the list we are interested in (for this scenario it’s List2). We are then asking for all the items filtered on the specific GUID which is the GUID of the item we just created in the create item in list action.
Hit OK on string builder dialog. The “Call HTTP Web Service” dialog should now look like this. Ensure you select “HTTP GET” for the HTTP method. And hit OK.
Choose “Properties” option from the context menu of “Call HTTP Web Service” action.
Set “RequestHeaders” to the request header variable you created and create a new variable of type dictionary named “RespContent” for “ResponseContent” as shown below. Hit OK.
Drop a “Get an Item from a Dictionary” action after “Call HTTP Web Service” action and click “item by name or path” hyperlink. In the editor type d/results(0) .
Click the dictionary hyperlink and choose “Variable: RespContent” from the dropdown. Click the item hyperlink and again choose “Variable: RespContent” from the dropdown. End result will be.
There are plenty of articles out there that talk about how to parse the JSON to get to the actual data we need so I am not going to cover that here. But remember we can use Fiddler to determine the structure of JSON result and we’ll use that knowledge to decide on how we will get to the result we need (for e.g., d/results(0)).
What we did above is that we want the value at d/results(0) from the RespContent dictionary variable we get back from the web service call. We are then storing the result again back in RespContent because the result is also of type dictionary.
Drop another “Get an Item from a Dictionary” action. Click on “item by name or path” hyperlink and type Id. Click “dictionary” hyperlink and choose “Variable: RespContent”. Click the item hyperlink in “(Output to item)” and create a new variable named “NewItemId” of type integer as shown below.
We did all these to get the ID (the sequence number) value of the list item we just created. Phew!!! This is because we need to know the ItemID of the list item we just created as we need to pass it to the StartWorkflowOnListItemBySubscriptionId REST method later. But we are just 30% into our design
The other parameter that StartWorkflowOnListItemBySubscriptionId needs is the workflow SubscriptionId.
We drop another “Call HTTP Web Service” action and construct our next REST call. This time we call /_api/web/lists/GetByTitle('List2') . We set the same “ReqHeader” variable to this call. And set the same “RespContent” variable as we did before. The end result you should have in “Call HTTP Web Service” dialog is shown below.
And in designer we should see.
We then add another “Get an Item from a Dictionary” action. Ask for d/Id from “RespContent” variable and assign the output to a new variable named “List2Guid” of type GUID as shown below.
Now the variable “List2Guid” will have the GUID of List2. We need this to get the workflow subscriptionId that we can get by calling an already available REST API called /_api/SP.WorkflowServices.WorkflowSubscriptionService.Current/EnumerateSubscriptionsByList.
We now drop another “Call HTTP Web Service” action and set it up get all workflow subscriptions. Here’s how string builder should look like for this call.
And we set the “HTTP method” option for this “Call HTTP Web Service” action to “HTTP POST” as shown below.
We should now have the workflow design as shown below.
Now we have a tricky situation where we need to get the subscriptionId of the exact workflow that we are interested to start off. This is where Fiddler is very helpful. Again I’ll defer to the plethora of articles available online that tells us how to do a GET, POST REST calls using Fiddler. But I am going to rush on this piece a bit.
Through Fiddler (by submitting a POST request to the same REST method EnumerateSubscriptionsByList, we see the following output.
Workflow2 happens to be the 3rd in the list of workflows associated to List2 so I can get that out using the 2nd index. So we drop two more “Get an Item from a Dictionary” actions. In the first action we ask for d/results(2) from “RespContent” and we store the output again in “RespContent”. In the second action we ask for Id which happens to be the SubscriptionId of Workflow2 on list List2 from “RespContent” again and store the result in a new variable named “SubscriptionId” of type GUID. Our workflow design should now look like below.
Now we are at the final stage where we will start the workflow.
Drop our last “Call HTTP Web Service” action and call the new /_api/SP.WorkflowServices.WorkflowInstanceService.Current/StartWorkflowOnListItemBySubscriptionId REST method by passing in the SubscriptionId and NewItemId variables. Our string builder dialog should look like the below. (Ensure the parameter names of this method is exactly as it is shown below and that the values are enclosed within single quotes).
This will again be a POST request.
After setting the “ReqHeader” correctly using the “Properties” context menu option for the last “Call HTTP Web Service” action, we drop a “Set Workflow Status” action. The completed Workflow1 should look like below.
We Save/Publish our workflow, go to List1 and create a new item. Start off Workflow1 manually on that new item. It should complete successfully. Then we go to List2 and we should see that a new item is created and Workflow2 was trigged off too.
If we click on “Stage 1”, we’ll see that Workflow2 logged the message as we designed.
I hope the information in this post was useful. Please check out the next part SharePoint 2013 Workflow recursion prevention – Part 3 to achieve the above objective from a Visual Studio SharePoint 2013 Workflow.