Showing posts with label Workflow. Show all posts
Showing posts with label Workflow. Show all posts

Sunday, February 7, 2016

Fix some errors when working with REST API

Response Code: "400 BadRequest"
Response Message: "A node of type 'PrimitiveValue' was read from the JSON reader when trying to read the start of an entry. A 'StartObject' node was expected"

or

Response Message: {“odata.error”:{“code”:”-1, Microsoft.SharePoint.Client.InvalidClientQueryException”,”message”:{“lang”:”en-US”,”value”:”The property ‘__metadata’ does not exist on type ‘SP.User’. Make sure to only use property names that are defined by the type.”}}}

or

Response Message: {“error”:{“code”:”-1, Microsoft.SharePoint.Client.InvalidClientQueryException”,”message”:{“lang”:”en-US”,”value”:”An entry without a type name was found, but no expected type was specified. To allow entries without type information, the expected type must also be specified when the model is specified.”}}}

This error is very common and used to get from result of REST API in workflow or JSON. The special thing of REST API in SharePoint is no having the same as formatted of command structure.

See example:

Upload file to library

url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Folder Name')/Files/Add(url='file name', overwrite=true)
method: POST
body: contents of binary file
headers:
    Authorization: "Bearer " + accessToken
    X-RequestDigest: form digest value
    content-type: "application/json;odata=verbose"
    content-length:length of post body

Create a list

url: http://site url/_api/web/lists
method: POST
body: { '__metadata': { 'type': 'SP.List' }, 'AllowContentTypes': true, 'BaseTemplate': 100,
 'ContentTypesEnabled': true, 'Description': 'My list description', 'Title': 'Test' }
Headers: 
    Authorization: "Bearer " + accessToken
    X-RequestDigest: form digest value
    accept: "application/json;odata=verbose"
    content-type: "application/json;odata=verbose"
    content-length:length of post body

The difference of those structure is a number of parameters and position of parameters. Some parameters must be transfer by query string and some of properties is passed from body of message. Assume that to upload the file name with the path is too long (over 256 characters), it will throw the error.

In the body of message, sometime we must define the variable named "parameters" to pass them to REST API and sometime it is not necessary. The most important thing is to understand of formatted snippet of REST API for separately command.

 

Example above also throws the error and I have a final solution is to remove the "parameters" from body of message. If you set the method is PUT, that means you must provide all properties of endpoint and X-HTTP-Method = MERGE is not affected. If you need to set some properties and leave some properties to be default or not set, you must set the method to POST and define X-HHTP-Method = MERGE

See the result



Good luck!



Stop/Start workflow for SharePoint Online using PowerShell Script

With SharePoint Online, PowerShell is as a connected bridge so that it itself brings a big dedication. I always think about PS script whenever get a new request.

Start/Start workflow for list item of large list is one of tasks that I have been worked on and it was so excited. This article is just sharing the script code with a little explanation to avoid the error while it's executing.

Common Script

$username = "YOUR_TENANTACC" 
$password = "YOUR_PASS" 
$url = "YOUR_SITE_URL"

$securePassword = ConvertTo-SecureString $Password -AsPlainText -Force 

# the path here may need to change if you used e.g. C:\Lib.. 
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll" 
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll" 
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.WorkflowServices.dll" 

# connect/authenticate to SharePoint Online and get ClientContext object.. 
$clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($url) 
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $securePassword) 
$clientContext.Credentials = $credentials 

$web = $clientContext.Web
$clientContext.Load($web)
$clientContext.ExecuteQuery()

$lists = $web.Lists
$clientContext.Load($lists)
$clientContext.ExecuteQuery()

$lst = $lists.GetByTitle("YOUR_LIST")
$clientContext.Load($lst)
$clientContext.ExecuteQuery()

#get all workflow subscription in your web/sub-site
$wfServicesManager = New-Object Microsoft.SharePoint.Client.WorkflowServices.WorkflowServicesManager($clientContext, $web)

$wfSubscriptionService = $wfServicesManager.GetWorkflowSubscriptionService()
$wfSubscriptions = $wfSubscriptionService.EnumerateSubscriptions()
$wfInstanceSevice = $wfServicesManager.GetWorkflowInstanceService()

$clientContext.Load($wfSubscriptions)
$clientContext.Load($wfInstanceSevice)
$clientContext.ExecuteQuery()

#initalize the parameters for workflow
$dictionary = New-Object 'system.collections.generic.dictionary[string,object]'
$dictionary.Add("YOUR_VARIABLE", "YOUR_VALUE")

#in case, if you need to start workflow on some items, you could execute the query and start on those items    
$qry = New-Object Microsoft.SharePoint.Client.CamlQuery
$qry.ViewXML = "<View><Query><Where>YOUR_QUERY</Where></Query></View>"
                
$items = $lst.GetItems($qry)
$clientContext.Load($items)
$clientContext.ExecuteQuery()

$srb = $wfSubscriptions | Where-Object { $_.Name -eq "YOUR_WORKFLOW_NAME" }

Start Workflow

$cnt = 0

foreach($itm in $items)
{
    $workflowInstances = $wfInstanceSevice.EnumerateInstancesForListItem($lst.ID, $itm.ID)
    $clientContext.Load($workflowInstances)
    $clientContext.ExecuteQuery()

    # bypass the item which the workflow instance was started
    $instance = $workflowInstances | Where-Object { $_.WorkflowSubscriptionId -eq  $srb.Id -and $_.Status -ne "Started" }
    
    if ($instance -ne $null)
    {
        $resultid = $wfInstanceSevice.StartWorkflowOnListItem($srb, $itm.Id, $dictionary)
        $clientContext.ExecuteQuery()
        Write-Host "Started workfkow [" $srb.Name  "] on item ["  $itm.Id  "]: " $resultid.Value -foregroundcolor Green
        Start-Sleep -s 1
        $cnt++
    }
}

Write-Host $cnt " are effected - Done!"

Stop Workflow 

$cnt = 0

foreach($itm in $items)
{
    $workflowInstances = $wfInstanceSevice.EnumerateInstancesForListItem($lst.ID, $itm.ID)
    $clientContext.Load($workflowInstances)
    $clientContext.ExecuteQuery()

    # bypass the item which the workflow instance was started
    $instance = $workflowInstances | Where-Object { $_.WorkflowSubscriptionId -eq  $srb.Id -and $_.Status -eq "Started" }
    
    if ($instance -ne $null)
        {
            $wfInstanceSevice.CancelWorkflow($instance)
            $clientContext.ExecuteQuery()
            Write-Host "Cancel Workfkow [" $instance.Id  "] on item ["  $itm.Id  "] " $instance.Status -foregroundcolor Red
            $cnt++
            Start-Sleep -s 1
        }
}

Write-Host $cnt " are effected - Done!"

Notes
  • Regarding to limitation on SharePoint Online, it only can start 5 workflows instance in a second. So, it should be delayed 1 second for each loop statement.
  • Just start a workflow if the workflow instance did not start and stop workflow if the workflow instance is running.

Tuesday, August 20, 2013

Case Sensitive in SharePoint

I actually didn't know about some areas in SharePoint using declaration with case-sensitive until today I work on List Definition and Custom Activity.

1) When we define a new list, we must define the list of fields and their properties. Especially in the schema for field, take a look the definition schema field by MSDN:

<Field
  ID = "Text"  Id = "Text"
  Type = "Data_Type"
  ...>
</Field>


Where:
ID: is the GUID of the field and put it into {...}
Id: is the text optional

The confusing thing is if you define the field with type is different from Lookup, you might use Id instead of ID. But if you declare the type is Lookup, you must use ID unless the feature couldn't be activated. Althought Id is the optional text but you are still able to use as ID.

2) If you code a custom activity and need it to be availabe on SharePoint Designer while you are designing a workflow, you must define the mapping type in WSS.ACTION file (you can find here C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\1033\Workflow)

The most important thing is all declarations in this file are case-sensitive and you must correct it before to use. It took me some hours to find out the error because I defined ID instead of Id in the field binding

        <FieldBind Field="Error" Text="this message" Id="1" DesignerType="TextBox"/>


That's awesome!