Project Online: CSOM and REST samples showing task indent and insert


Skipping back to Project Online for a brief interlude – I was working on a case where the requirement was to create summary tasks and sub task relationships in a plan using the REST API.  I started with the samples out on GitHub – and used the .NET samples first just to get my head around the syntax – then applied the same thing to the REST samples.  The samples can be found here:

In the example just a single task is created (createprojects.cs and createprojects.ps1) – and for my practice I wanted to create a structure something like this:

Summary Task

Sub Task

Another Task

A task inserted in here

Another Task

The additional parameters needed to achieve this are ParentId – which creates a task indented under its ‘Parent’ – which therefore becomes a summary and AddAfterId – which allows you to set the position of the new task after an existing one.  Obviously you need to get the IDs of the tasks in question – and this is the GUID that represents the task – and not the ID you may see in Project (1,2,3 etc.).  In my case I’m doing this all in one place – so I have all the GUIDs – your scenario may ned you to read the project and get the IDs.

For the .NET example I used an array of GUIDs and just duplicated the TaskCreationInfomation sections in CreateProjects.cs and set the ParentId for the second task – and then set the final task to insert after task 3 with AddAfterId.  So my snippet of code looked like this with the interesting parts highlighted:

 

csom.DraftProject draft = project.CheckOut();
Guid[] taskId = new Guid[5];
taskId[0] = Guid.NewGuid();
taskId[1] = Guid.NewGuid();
taskId[2] = Guid.NewGuid();
taskId[3] = Guid.NewGuid();
taskId[4] = Guid.NewGuid();
csom.Task task0 = draft.Tasks.Add(new csom.TaskCreationInformation()
{
Id = taskId[0],
Name = "Summary " + taskName,
IsManual = false,
Start = project.StartDate.AddDays(1),
Duration = "5d"
});

            csom.Task task1 = draft.Tasks.Add(new csom.TaskCreationInformation()
{
Id = taskId[1],
Name = "sub " + taskName + " 1",
IsManual = false,
Start = project.StartDate.AddDays(1),
Duration = "4d",
ParentId = taskId[0]

            });
csom.Task task2 = draft.Tasks.Add(new csom.TaskCreationInformation()
{
Id = taskId[2],
Name = taskName  + " 2",
IsManual = false,
Start = project.StartDate.AddDays(1),
Duration = "3d"
});
csom.Task task3 = draft.Tasks.Add(new csom.TaskCreationInformation()
{
Id = taskId[3],
Name = taskName + " 3",
IsManual = false,
Start = project.StartDate.AddDays(1),
Duration = "2d"
});
csom.Task task4 = draft.Tasks.Add(new csom.TaskCreationInformation()
{
Id = taskId[4],
Name = "Inserted " + taskName + " 4",
IsManual = false,
Start = project.StartDate.AddDays(1),
Duration = "1d",
AddAfterId = taskId[2]
            });

            draft.Update();

The created plan looked like this:

image

The REST example is pretty much the same – I used the existing CreateProjects.ps1 and duplicated the REST request and POST sections for each of my tasks.  I did find that unless the POST that creates the task with the task ID had completed – and gone through the queue (The aptly named Project Update from PSI job…) – that I’d get a 403 error thrown – so if just running through the example best to use PowerShell ISE and run each section separately.  If coding for real then you’d need to check the queue.

My REST example code:

# Task parameters as JSON payload
$taskid0 = [Guid]::NewGuid()
$body = "{
'parameters': {
'Id': '$taskid0',
'Name': 'Summary_Task_$taskid0',
'Notes': 'Created from PowerShell using REST API',
'Start': '2016-01-04T08:00:00',
'Duration': '5d'
}
}"

# ReST request to create a task
Post-ReSTRequest $SiteUrl "ProjectServer/Projects('$projectid')/Draft/Tasks/Add" $body

# Task parameters as JSON payload
$taskid1 = [Guid]::NewGuid()
$body = "{
'parameters': {
'Id': '$taskid1',
'Name': 'Task_$taskid1',
'Notes': 'Created from PowerShell using REST API',
'Start': '2016-01-04T08:00:00',
'Duration': '5d',
'ParentId': '$taskid0'
}
}"

# ReST request to create a task
Post-ReSTRequest $SiteUrl "ProjectServer/Projects('$projectid')/Draft/Tasks/Add" $body

# Task parameters as JSON payload
$taskid2 = [Guid]::NewGuid()
$body = "{
'parameters': {
'Id': '$taskid2',
'Name': 'Task_$taskid2',
'Notes': 'Created from PowerShell using REST API',
'Start': '2016-01-04T08:00:00',
'Duration': '5d'
}
}"

# ReST request to create a task
Post-ReSTRequest $SiteUrl "ProjectServer/Projects('$projectid')/Draft/Tasks/Add" $body

# Task parameters as JSON payload
$taskid3 = [Guid]::NewGuid()
$body = "{
'parameters': {
'Id': '$taskid3',
'Name': 'Task_$taskid3',
'Notes': 'Created from PowerShell using REST API',
'Start': '2016-01-04T08:00:00',
'Duration': '5d'
}
}"

# ReST request to create a task
Post-ReSTRequest $SiteUrl "ProjectServer/Projects('$projectid')/Draft/Tasks/Add" $body

# Task parameters as JSON payload
$taskid4 = [Guid]::NewGuid()
$body = "{
'parameters': {
'Id': '$taskid4',
'Name': 'Insert_Task_$taskid4',
'Notes': 'Created from PowerShell using REST API',
'Start': '2016-01-04T08:00:00',
'Duration': '5d',
'AddAfterId': '$taskid2'
    }
}"

# ReST request to create a task
Post-ReSTRequest $SiteUrl "ProjectServer/Projects('$projectid')/Draft/Tasks/Add" $body

And the resulting plan:

image

As mentioned – these are just snippets but you should be able to see where you could use these in createprojects.cs from the .NET samples and createproject.ps1 from the REST samples.  Also I did find a couple of typos in the REST sample which should be getting fixed – lines 11 and 12 should reference $SiteUrl and not $siteUrl – and the POST to add tasks (line 41) should be Draft/Tasks and not just Tasks.  You will also obviously need to set your own Site Url – this is set in pwaInstanceUrl in CreateUpdateProjectSample.cs in the .NET samples, and for the REST samples update $SiteUrl in createproject.ps1 and set your user account in ReST.ps1.

Very basic stuff – and forgive my sloppy duplication of code but hopefully it will help someone understand ParentId and AddAfterId.


Comments (1)

  1. Ray says:

    Hi Brian thanks as always for this. Testing this duration works. I notice the rest field names differ vs the odata field names so the sdk helped there. Tried the same way to simply put in a custom field name but no go. Is it supported to update CFs through rest? If so can you post the end point?

Skip to main content