Debugging

There are two different debugging approaches related to kenaflow abilities.

The first one is debugging of workflow scripts.

The second one is debugging of remove events.

Debugging of Workflow Scripts

To debug a workflow script, it is either opened in Visual Studio Code or in the PowerShell ISE.

The following line must be present in the workflows script:

if($wf-eq$null){Import-Module "C:\Program Files\kenaflow\kenaflow.runtime.dll";Invoke-Kenaflow;exit}

A workflow script that is started outside the kenaflow runtime gets no parameters passed. The variable $wf is empty. In this case, the kenaflow runtime environment is initialized with the Invoke-Kenaflow command.

In debug mode you can use some helpful cmdlets such as

The workflow to which the script to be debugged belongs is executed by kenaflow and when the script to be debugged is reached, the debugger stops and the developer can examine the workflow.

If the script is not reached due to the workflow settings, then the debugger does not stop. This can happen because there are e.g. no SharePoint list items to match the script currently being debugged or because the query returns no result.

In case of State Machine workflow each script belongs to a workflow state. If you execute such a "workflow state script" the engine recognizes the assiciated state and only executes this state! All other workflow states are not executed in the debugger at this time!

In case of SharePoint list and state machine workflows you can configure multiple webs and lists that the workflow processes. If you debug such a workflows script the webs and lists are processed one after the other. You can specify the parameters -List and -Web (both or just one of them) to filter the lists and webs that are used for debugging. Both parameters accept a single string that can contain one list title and/or one web url.

Error Handling Script Debugging

To debug an error handling script of a workflow you just need to raise an exception and set a breakpoint - e.g. with Invoke-KFDebugBreak - in the error handling script.

To raise an error within the workflow script use:

throw ":-("

Debugging of Pre-Query Scripts

SharePoint list and state machine workflows can have a pre-query script that is executed before the items to be processed are queried from SharePoint.

You can debug these script just by adding a breakpoint to the pre-query script. You can use Invoke-KFDebugBreak.

Preparing a debug session

You can specify a pre-workflow execution script with Invoke-Kenaflow. It is executed when the environment is prepared but no workflow script was executed.

PowerShell Workflows

Invoke-Kenaflow -PreWorkflowExecution {
    param($wf, $config)
	
	#your script
}

SharePoint Site Workflows

Invoke-Kenaflow -PreWorkflowExecution {
    param($wf, $clientContext, $config)
	
	#your script
}

SharePoint List and State Machine Workflows

Invoke-Kenaflow -PreWorkflowExecution {
    param($wf, $clientContext, $states, $config)
	
	#your script
}

Email Workflow with SharePoint Connection

IMAP

Invoke-Kenaflow -PreWorkflowExecution {
    param($wf, $clientContext, $imapServer, $config)
	
	#your script
}

POP3

Invoke-Kenaflow -PreWorkflowExecution {
    param($wf, $clientContext, $pop3Server, $config)
	
	#your script
}

EXCHANGE

Invoke-Kenaflow -PreWorkflowExecution {
    param($wf, $clientContext, $exchangeWebService, $config)
	
	#your script
}

Email Workflow (without SharePoint Connection)

IMAP

Invoke-Kenaflow -PreWorkflowExecution {
    param($wf, $imapServer, $config)
	
	#your script
}

POP3

Invoke-Kenaflow -PreWorkflowExecution {
    param($wf, $pop3Server, $config)
	
	#your script
}

EXCHANGE

Invoke-Kenaflow -PreWorkflowExecution {
    param($wf, $exchangeWebService, $config)
	
	#your script
}

Remote Event Debugging

Remote Events are described here: Remote Event. Please read this article first and then come back here!

You cannot directly debug into a workflows script thats runs inside the dedicated "remote event runner" process.

You need to simulate the event context!

Therefore we have prepared the following process.

First you need to decide what type of remove event you want to debug:

  • SharePoint Remote Event (as it would be send from SharePoint)
  • Link Event (...that is created with Get-KFLink).
  • Custom Data Event
  • Unstructured Data Event

For each of the exists a "simulation" cmdlet that we describe below.

First you need to create a new PowerShell file in the folder of the workflow that is to debug with event data.

debug-unstructured-data.ps1:

Import-Module "C:\Program Files\kenaflow\kenaflow.runtime.dll";

Invoke-Kenaflow -EventData {
	Invoke-KFUnstructuredDataEventSimulation -QueryString "key1=test1&key2=test2" -Body "<node>some xml</node>" `
	   -SimulatedUserName "sharepoint\ingo" -SimulatedUserPassword "kr@ftwerk" -Authentication Basic
}

This cmdlet Invoke-KFUnstructuredDataEventSimulation does all the magic. It creates the remote event from the given parameter values, starts the workflow and enables you to debug the workflow script.

The workflow script looks like this - very simple and basic:

script.ps1

param($wf, $states, $web, $item, $eventData, $config)

if($wf-eq$null){Import-Module "C:\Program Files\kenaflow\kenaflow.runtime.dll";Invoke-Kenaflow;exit}

Invoke-KFDebugBreak

if( $eventData -ne $null ) {
	$eventData.Keys | % { 
		New-Object PSObject -Property @{Name=$_; Value=$eventData[$_]}
	} | Sort-Object Name | Format-Table Name, Value | Out-String | Write-KFLog
}

In a normal (time based) workflow run the workflow script does nothing because $eventData is empty. But when you debug the test script debug-unstructured-data.ps1 you have a valid $eventData object and get all the request data in the output window.

You are able to debug into the test script debug-unstructured-data.ps1 and also to debug into the script block that contains cmdlet Invoke-KFUnstructuredDataEventSimulation in the above example.

You can have a look at the object, that Invoke-KFUnstructuredDataEventSimulation creates. Lets have a look at this screenshot:

Not the test script is currently open but the workflow script. See the line marked with (1) ? The debugger has stopped there. The block marked with (2) shows the object that was created by Invoke-KFUnstructuredDataEventSimulation. The corresponding test script is this:

There we catch the object and write it line marked with (1) to the log using Write-KFLogOutput.

Here is an example test script for a SharePoint Remote Event:

Import-Module "C:\Program Files\kenaflow\kenaflow.runtime.dll";

Invoke-Kenaflow -EventData {
	$debugObject = 
		Invoke-KFSharePointEventSimulation `
			-ItemId 1 `
			-EventType ItemUpdated `
			-SimulatedUserName "sharepoint\ingo" `
			-Data @{Title = "kenaflow is cool!"}

	Write-KFLogOutput $debugObject

	return $debugObject
}

The log message in the output window is:

VERBOSE: kf[normal  ]: {"EventSource":1,"EventData":"\r\n<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/en
velope/\">\r\n\t<s:Body>\r\n\t\t<ProcessOneWayEvent xmlns=\"http://schemas.microsoft.com/sharepoint/remoteapp/\">\r\n
\t\t\t<properties xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n\t\t\t\t<AppEventProperties i:nil=\"true\
"/>\r\n\t\t\t\t<ContextToken/>\r\n\t\t\t\t<CorrelationId>00000000-0000-0000-0000-000000000000</CorrelationId>\r\n\t\t
\t\t<CultureLCID>1031</CultureLCID>\r\n\t\t\t\t<EntityInstanceEventProperties i:nil=\"true\"/>\r\n\t\t\t\t<ErrorCode/
>\r\n\t\t\t\t<ErrorMessage/>\r\n\t\t\t\t<EventType>ItemUpdated</EventType>\r\n\t\t\t\t<ItemEventProperties>\r\n\t\t\t
\t\t<AfterProperties xmlns:a=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">\r\n\t\t\t\t\t\t<a:KeyValu
eOfstringanyType>\r\n\t\t\t\t\t\t\t<a:Key>Title</a:Key>\r\n\t\t\t\t\t\t\t<a:Value i:type=\"b:string\" xmlns:b=\"http:
//www.w3.org/2001/XMLSchema\">kenaflow is cool!</a:Value>\r\n\t\t\t\t\t\t</a:KeyValueOfstringanyType>\r\n\t\t\t\t\t</
AfterProperties>\r\n\t\t\t\t\t<AfterUrl i:nil=\"true\"/>\r\n\t\t\t\t\t<BeforeProperties xmlns:a=\"http://schemas.micr
osoft.com/2003/10/Serialization/Arrays\"/>\r\n\t\t\t\t\t<BeforeUrl/>\r\n\t\t\t\t\t<CurrentUserId>6</CurrentUserId>\r\
n\t\t\t\t\t<ExternalNotificationMessage i:nil=\"true\"/>\r\n\t\t\t\t\t<IsBackgroundSave>false</IsBackgroundSave>\r\n\
t\t\t\t\t<ListId>2ad9bcc4-9f8b-474c-b485-56944d68baca</ListId>\r\n\t\t\t\t\t<ListItemId>1</ListItemId>\r\n\t\t\t\t\t<
ListTitle>Set-KF-Perm-Test</ListTitle>\r\n\t\t\t\t\t<UserDisplayName>Ingo</UserDisplayName>\r\n\t\t\t\t\t<UserLoginNa
me>i:0#.w|sharepoint\\ingo</UserLoginName>\r\n\t\t\t\t\t<Versionless>false</Versionless>\r\n\t\t\t\t\t<WebUrl>https:/
/intranet13.sharepoint.farm</WebUrl>\r\n\t\t\t\t</ItemEventProperties>\r\n\t\t\t\t<ListEventProperties i:nil=\"true\"
/>\r\n\t\t\t\t<SecurityEventProperties i:nil=\"true\"/>\r\n\t\t\t\t<UICultureLCID>1031</UICultureLCID>\r\n\t\t\t\t<We
bEventProperties i:nil=\"true\"/>\r\n\t\t\t</properties>\r\n\t\t</ProcessOneWayEvent>\r\n\t</s:Body>\r\n</s:Envelope>
\r\n","OutputFormat":0}
VERBOSE: kf[normal  ]: Excluding users: |app@sharepoint; sharepoint\kenaflow
VERBOSE: kf[normal  ]: connecting to SharePoint...
VERBOSE: kf[normal  ]: current user: i:0#.w|sharepoint\kenaflow (Microsoft.SharePoint.Client.UserIdInfo)
VERBOSE: kf[normal  ]: connected
VERBOSE: kf[normal  ]: Last action timestamp not found
VERBOSE: kf[normal  ]: Set default config variables
VERBOSE: kf[normal  ]: Preparing query... 
VERBOSE: kf[normal  ]: current item: #1 // 56b2704e-78a9-4bb3-94e6-8eb6914ea3d9

You can see a "native" looking SharePoint remote event XML in the log.

The kenaflow loads the workflow script an initializes it for the item specified in the remote event.

Debugging "Unstructured Data" Remove Event Receivers with Output Data

Please read the corresponding section in article Remote Event first!

Workflow with an remote event of type "unstructured data" can return output data. In normal execution this is send back in the http connection as response.

In the simulation - for debugging - you receive this data as result of Invoke-Kenaflow.

Example:

import-module "C:\Program Files\kenaflow\kenaflow.runtime.dll";

$output = Invoke-Kenaflow -Debug -EventData {
    Invoke-KFUnstructuredDataEventSimulation -QueryString "test1&test2" -Body "test" `
		-SimulatedUserName "sharepoint\ingo" -SimulatedUserPassword "kr@ftwerk" -Authentication Basic `
		-OutputFormat Unconverted
}

$output.Test
$output.currentDate
$output.Evt

You get as result:

(1) is the output of $output.Test
(2) is the output of $output.currentDate
(3) is (part of) the output of $output.Evt

Or you request a JSON string with -OutputFormat JsonString as you would receive it through a real web request:

[{"CurrentDate":"2019-05-16T17:02:52.2971184+02:00","Evt":{"IsSecureConnection":false,"Referrer":null,"__wfid":"E7A0A1BA-5293-4B2F-9F8A-37B052733935","Url":"http://localhost/udb/E7A0A1BA-5293-4B2F-9F8A-37B052733935/?test1&test2","QueryString":"test1&test2","__user":"sharepoint\\ingo","UserAgent":null,"HttpMethod":"POST","LocalEndPoint":"[::1]:56018","ContentLength":4,"IsAuthenticated":false,"UserHostAddress":"[::1]:56018","RemoteEndPointIP":"::1","AcceptTypes":null,"RemoteEndPointPort":"56019","RemoteEndPoint":"[::1]:56019","IsLocal":true,"ContentType":null,"KeepAlive":true,"__userAuthType":"Basic","__userIsAuthenticated":true,"ProtocolVersion":"1.1","UserLanguages":null,"RawUrl":"/kenaflow/?test1&test2","UserHostName":"localhost:56018","__body":"test","Headers":{"Connection":"Keep-Alive","Host":"localhost:56018","Expect":"100-continue","Content-Length":"4"},"ContentEncoding":"System.Text.SBCSCodePageEncoding"},"Test":"kenaflow"}]

Or you request a PowerShell CliXML string with -OutputFormat ClixmlString as you would receive it through a real web request:

<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>System.Collections.Generic.List`1[[System.Management.Automation.PSObject, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToke
n=31bf3856ad364e35]]</T>
      <T>System.Object</T>
    </TN>
    <LST>
      <Obj RefId="1">
        <TN RefId="1">
          <T>System.Collections.Hashtable</T>
          <T>System.Object</T>
        </TN>
        <DCT>
          <En>
            <S N="Key">CurrentDate</S>
            <DT N="Value">2019-05-16T17:04:19.0350126+02:00</DT>
          </En>
          <En>
            <S N="Key">Evt</S>
            <Obj N="Value" RefId="2">
              <TNRef RefId="1" />
              <DCT>
                <En>
                  <S N="Key">IsSecureConnection</S>
                  <B N="Value">false</B>
                </En>
                <En>
                  <S N="Key">Referrer</S>
                  <Nil N="Value" />
                </En>
                <En>
                  <S N="Key">__wfid</S>
                  <S N="Value">E7A0A1BA-5293-4B2F-9F8A-37B052733935</S>
                </En>
                <En>
                  <S N="Key">Url</S>
                  <S N="Value">http://localhost/udb/E7A0A1BA-5293-4B2F-9F8A-37B052733935/?test1&amp;test2</S>
                </En>
                <En>
                  <S N="Key">QueryString</S>
                  <S N="Value">test1&amp;test2</S>
                </En>

(This is only a part of the result CliXML string!)

If you want to test such an request from JavaScript you could create a simple HTML page with an jQuery based AJAX request:

<html>
<script src="jquery.js"></script>
<script type="text/javascript">
function send(){
	$.post('http://localhost:80/uda/j/E7A0A1BA-5293-4B2F-9F8A-37B052733935/',JSON.stringify({
					"test":"kenaflow is cool!"})).done(
				function(data, State) { 
					$('#response').html('Response data: ');
					$('#data').html(data);
				});
}
</script>
<body>
<input type="button" onclick="javascript:send();return false" value="send"/>
<div id="response"></div>
<pre id="data"></pre>
</body>
</html>

This is the result in the browser:

Discussion