Trials or tribulation? Inside SharePoint 2013 workflows–Part 12
Hi all, and welcome to part 12 of my articles about SharePoint 2013 Workflows and whether they are ready for prime time. Along the way we have learnt all about CAML, REST, JSON, calling web services, Fiddler, Dictionary objects and a heap of scenarios that can derail aspiring workflow developers. All this just to assign a task to a user!
Anyways, since it has been such a long journey, I felt it worthwhile to remind you of the goal here. We have a fictitious company called Megacorp trying to develop a solution to controlled documents management. The site structure is as follows:
The business process we have been working through looks like this:
The big issue that has caused me to have to write 12 articles all boils down to the information architecture decision to use a managed metadata column to store the Organisation hierarchy.
Right now, we are in the middle of implementing an approach of calling a web service to perform step 3 in the above diagram. In part 9 and part 10 of this series, I explained the theory of embedding a CAML query into a REST query and in part 11, we built out most of the workflow. Currently the workflow has 4 stages and we have completed the first three of them.
- 1) Get the organisation name of the current item
- 2) Obtain an X-RequestDigest via a web service call
- 3) Constructed the URL to search the Process Owner list and called the web service
The next stage will parse the results of the web service call to get the AssignedToID and then call another web service to get the actual userid of the user. Then we can finally have what we need to assign an approval task. So letās get into itā¦
Obtaining the UserID
In the previous post, I showed how we constructed a URL similar to this one:
http://megacorp/iso9001/_api/web/Lists/GetByTitle(‘Process%20Owners’)/GetItems(query=@v1)?@v1={“ViewXml”:”<View><Query><ViewFields><FieldRef%20Name=’Organisation’/><FieldRef%20Name=’AssignedTo’/></ViewFields><Where><Eq><FieldRef%20Name=’Organisation’/><Value%20Type=’TaxonomyFieldType’>Megacorp%20Burgers</Value></Eq></Where></Query></View>”}
This URL uses the CAML in REST method of querying the Process Owners list and returns any items where Organisation equals āMegacorp Burgersā. The JSON data returned shows the AssignedToID entry with a value of 8. Via the work we did in the last post. we already have this data available to us in a dictionary variable called ProcessOwnerJSON.
The rightmost JSON output below illustrates taking that AssignedToID value and calling another web service to return the username , i.e : http://megacorp/iso9001/_api/Web/GetUserById(8).
Confused at this point? Then I suggest you go back and re-read parts 8 and 10 in particular for a recap.
So our immediate task is to extract the AssignedToId from the dictionary variable called ProcessOwnerJSON. Now that you are a JSON guru, you should be able to figure out that the query will be d/results(0)/AssignedToId.
Step 1:
Add a Get an Item from a Dictionary action as the first action in the Obtain Userid workflow stage. Click the item by name or path hyperlink and click the ellipses to bring up the string builder screen. Type in d/results(0)/AssignedToId.
Step 2:
Click on the dictionary hyperlink and choose the ProcessOwnerJSON variable from the list.
Step 3:
Click the item hyperlink and use the AssignedToID variable
That is basically it for now with this workflow stage as the rest of it remains unchanged from when we constructed it in part 8. At this point, the Obtain Userid stage should look like this:
If you look closely, you can see that it calls the GetUserById method and the JSON response is added to the dictionary variable called UserDetail. Then if the HTTP response code is OK (code 200), it will pull out the LoginName from the UserDetail variable and log it to the workflow history before assigning a task.
Phew! Are we there yet? Letās see if it all works!
Testing the workflow
So now that we have the essential bits of the workflow done, letās run a test. This time I will use one of the documents owned by Megacorp Iron Man Suits ā the Jarvis backup and recovery procedure. The process owner for Megacorp Iron Man suits is Chris Tomich (Chris reviewed this series and insisted he be in charge of Iron Man suits!).
If we run the workflow against the Jarvis backup and recovery procedure, we should expect a task to be created and assigned to Chris Tomich. Looking at the workflow information below, it worked! HOLY CRAP IT WORKED!!!
So finally, after eleven and a half posts, we have a working workflow! We have gotten around the issues of using managed metadata columns to filter lists, and we have learnt a heck of a lot about REST/oData, JSON, CAML and various other stuff along the way. So having climbed this managed metadata induced mountain, is there anything left to talk about?
Of course there is! But letās summarise the workflow in text format rather than death by screenshot
Stage: Get Organisation Name
Find | in the Current Item: Organisation_0 (Output to Variable:Index)
then Copy Variable:Index characters from start of Current Item: Organisation_0 (Output to Variable: Organisation)
then Replace " " with "%20" in Variable: Organisation (Output to Variable: Organisation)
then Log Variable: Organisation to the workflow history list
If Variable: Organisation is not empty
Go to Get X-RequestDigest
else
Go to End of Workflow
Stage: Get-X-RequestDigest
Build {...} Dictionary (Output to Variable: RequestHeader)
then Call [%Workflow Context: Current Site URL%]_api/contextinfo HTTP Web Service with request
(ResponseContent to Variable: ContextInfo
|ResponseHeaders to responseheaders
|ResponseStatusCode to Variable:ResponseCode )
If Variable: responseCode equals OK
Get d/GetContextWebInformation/FormDigestValue from Variable: ContextInfo (Output to Variable: X-RequestDigest )
If Variable: X-RequestDigest is empty
Go to End of Workflow
else
Go to Prepare and execute process owners web service call
Stage: Prepare and execute process owners web service call
Build {...} Dictionary (Output to Variable: RequestHeader)
then Set Variable:URLStart to _api/web/Lists/GetByTitle('Process%20Owners')/GetItems(query=@v1)?@v1={"ViewXml":"<View><Query><ViewFields><FieldRef%20Name='Organisation'/><FieldRef%20Name='AssignedTo'/></ViewFields><Where><Eq><FieldRef%20Name='Organisation'/><Value%20Type='TaxonomyFieldType'>
then Set Variable:URLEnd to </Value></Eq></Where></Query></View>"}
then Call [%Workflow Context: Current Site URL%][Variable: URLStart][Variable: Organisation][Variable: URLEnd] HTTP Web Service with request
(ResponseContent to Variable: ProcessOwnerJSON
|ResponseHeaders to responseheaders
|ResponseStatusCode to Variable:ResponseCode )
then Log Variable: responseCode to the workflow history list
If Variable: responseCode equals OK
Go to Obtain Userid
else
Go to End of Workflow
Stage: Obtain Userid
Get d/results(0)/AssignedToId from Variable: ProcessOwnerJSON (Output to Variable: AssignedToID)
then Call [%Workflow Context: Current Site URL%]_api/Web/GetUserByID([Variable: AssignedToID]) HTTP Web Service with request
(ResponseContent to Variable: userDetail
|ResponseHeaders to responseheaders
|ResponseStatusCode to Variable:ResponseCode )
If Variable: responseCode equals OK
Get d/LoginName from Variable: UserDetail (Output to Variable: AssignedToName)
then Log The User to assign a task to is [%Variable: AssignedToName]
then assign a task to Variable: AssignedToName (Task outcome to Variable:Outcome | Task ID to Variable: TaskID )
Go to End of Workflow
Tidying upā¦
Just because we have our workflow working, does not mean it is optimally set up. In the above workflow, there are a whole heap of areas where I have not done any error checking. Additionally, the logging I have done is poor and not overly helpful for someone to troubleshoot later. So I will finish this post by making the workflow a bit more robust. I will not go through this step by step ā instead I will paste the screenshots and summarise what I have done. Feel free to use these ideas and add your own good practices in the commentsā¦
First up, I added a new stage at the start of the workflow for anything relation to initialisation activities. Right now, all it does is check out the current item (recall in part 3 we covered issues related to check in/out), and then set a Boolean workflow variable called EndWorkflow to No. You will see how I use this soon enough. I also added a new stage at the end of the workflow to tidy things up. I called it Clean up Workflow and itās only operation is to check the current item back in.
In the Get Organisation Name stage, I changed it so that any error condition logs to the history list, and then set the EndWorkflow variable to Yes. Then in the Transition to stage section, I use the EndWorkflow variable to decide whether to move to the next stage or end the workflow by calling the Clean up workflow stage that I created earlier. My logic here is that there can be any number of error conditions that we might check for, and its easier to use a single variable to signify when to abort the workflow.
In the Get X-RequestDigest stage, I have added additional error checking. I check that the HTTP response code from the contextinfo web service call is indeed 200 (OK), and then if it is, I also check that we successfully extracted the X-RequestDigest from the response. Once again I use the EndWorkflow variable to flag which stage to move to in the transition section.
In the Prepare and execute process owners web service call stage, I also added more error checking ā specifically with the AssignedToID variable. This variable is an integer and its default value is set to zero (0). If the value is still 0, it means that there was no process owner entry for the Organisation specified. If this happens, we need to handle for this…
Finally, we come to the Obtain Userid stage. Here we are checking both the HTTP code from the GetUserInfo web service call, as well as the userID that comes back via the AssignedToName variable. We assign the task to the user and then set the workflow status to āCompleted workflowā. (Remember that we checked out the current item in the Workflow Initialisation stage, so we can now update the workflow status without all that check out crap that we hit in part 3).
Conclusionā¦
So there we have it. Twelve posts in and we have met the requirements for Megacorp. While there is still a heap of work to do in terms of customising the behaviour of the task itself, I am going to leave that to you!
Additionally, there are a lot of additional things we can do to make these workflows much more robust and easier to manage. To that end, I strongly urge you to check out Fabian Williams blog and his brilliant set of articles on this topic that take it much (much) further than I do here. He has written a ton of stuff and it was his work in particular inspired me to write this series. He also provided me with counsel and feedback on this series and I canāt thank him enough.
Now that we have gotten to where I wanted to, Iāll write one more article to conclude the series ā reflecting on what we have covered, and its implications for organisations wanting to leverage out of the boxĀ SharePoint workflow, as well as implications for all of you citizen developers out there.
Until then, thanks for readingā¦
Paul Culmsee
Leave a Reply