TimeCockpit.psm1
$tokenUri = "https://api.timecockpit.com/token"; $baseUrl = "https://api.timecockpit.com/odata"; $mgmntBaseUrl = "https://management.timecockpit.com"; $tcDBServer="gjmyvebfqv.database.windows.net"; $tcDBDatabase="tcedhrqnfxua"; <# .SYNOPSIS Connects to TimeCockpit. .DESCRIPTION Credentials can be persisted using https://www.powershellgallery.com/packages/CredentialsManager/ by setting the parameter UseCredentialsManager to $True. o $Credential -eq $Null -and $UseCredentialsManager -eq $Null -> credentials are requested and are not persisted o $Credential -ne $Null -and $UseCredentialsManager -eq $Null -> passed credentials are used and not persisted o $Credential -ne $Null -and $UseCredentialsManager -ne $Null -> passed credentials are used and persisted o $Credential -eq $Null -and $UseCredentialsManager -ne $Null -> Persisted credentials are used .PARAMETER Credential Credential to access the OData interface with. I not provided they get requested. .PARAMETER UseCredentialsManager If $True, credentials are read and written using the CredentialsManager. #> function Connect-TC { [CmdletBinding()] Param( [PSCredential]$Credential, [Boolean]$UseCredentialsManager = $False ) #end param # check module prerequisites if($UseCredentialsManager) { $module = Get-Module -ListAvailable -Name "CredentialsManager"; if (!$module) { throw "Module 'CredentialsManager' needed. Please install executing 'Install-Module -Name CredentialsManager' as Administrator."; } } $webclient = new-object System.Net.WebClient if($UseCredentialsManager -and $Credential -eq $Null) { $Credential = Read-Credential -ListAvailable | Where { $_.Environment -eq "TimeCockpit" } } if(!$Credential) { $Credential = Get-Credential -Message "Please enter Credentials for Timecockpit."; } if($UseCredentialsManager) { Write-Credential "TimeCockpit" -Credential $Credential; } $webclient.Credentials = $Credential; $script:token = $webclient.DownloadString($tokenUri); } <# .SYNOPSIS Connects to TimeCockpit Management API. .DESCRIPTION Credentials can be persisted using https://www.powershellgallery.com/packages/CredentialsManager/ by setting the parameter UseCredentialsManager to $True. o $Credential -eq $Null -and $UseCredentialsManager -eq $Null -> credentials are requested and are not persisted o $Credential -ne $Null -and $UseCredentialsManager -eq $Null -> passed credentials are used and not persisted o $Credential -ne $Null -and $UseCredentialsManager -ne $Null -> passed credentials are used and persisted o $Credential -eq $Null -and $UseCredentialsManager -ne $Null -> Persisted credentials are used .PARAMETER Credential Credential to access the TC Mangement interface with. I not provided they get requested. .PARAMETER UseCredentialsManager If $True, credentials are read and written using the CredentialsManager. #> function Connect-TCMgmnt { [CmdletBinding()] Param( [PSCredential]$Credential, [Boolean]$UseCredentialsManager = $False ) #end param # check module prerequisites if($UseCredentialsManager) { $module = Get-Module -ListAvailable -Name "CredentialsManager"; if (!$module) { throw "Module 'CredentialsManager' needed. Please install executing 'Install-Module -Name CredentialsManager' as Administrator."; } } $webclient = new-object System.Net.WebClient if($UseCredentialsManager -and $Credential -eq $Null) { $Credential = Read-Credential -ListAvailable | Where { $_.Environment -eq "TimeCockpit.Mgmnt" } } if(!$Credential) { $Credential = Get-Credential -Message "Please enter Credentials for Timecockpit Management API."; } if($UseCredentialsManager) { Write-Credential "TimeCockpit.Mgmnt" -Credential $Credential; } $tokenRequest = "grant_type=client_credentials&client_id=$($Credential.UserName)&client_secret=$($Credential.GetNetworkCredential().password)&scope=management.timecockpit.com mgmt.users" $script:mgmntToken = Invoke-RestMethod -Method Post -Uri "https://auth.timecockpit.com/connect/token" -Body $tokenRequest -ContentType 'application/x-www-form-urlencoded'; } <# .SYNOPSIS Connects to TimeCockpit Database. .DESCRIPTION Credentials can be persisted using https://www.powershellgallery.com/packages/CredentialsManager/ by setting the parameter UseCredentialsManager to $True. Queries are executed using https://www.powershellgallery.com/packages/Invoke-SqlCmd2/ o $Credential -eq $Null -and $UseCredentialsManager -eq $Null -> credentials are requested and are not persisted o $Credential -ne $Null -and $UseCredentialsManager -eq $Null -> passed credentials are used and not persisted o $Credential -ne $Null -and $UseCredentialsManager -ne $Null -> passed credentials are used and persisted o $Credential -eq $Null -and $UseCredentialsManager -ne $Null -> Persisted credentials are used .PARAMETER Credential Credential to access the database with. I not provided they get requested. .PARAMETER UseCredentialsManager If $True, credentials are read and written using the CredentialsManager. #> function Connect-TCDB { [CmdletBinding()] Param( [PSCredential]$Credential, [Boolean]$UseCredentialsManager = $False ) #end param # check module prerequisites $sqlCmd2Module = Get-Module -ListAvailable -Name "Invoke-SqlCmd2"; if (!$sqlCmd2Module) { throw "Module 'Invoke-SqlCmd2' needed. Please install executing 'Install-Module -Name Invoke-SqlCmd2' as Administrator."; } if($UseCredentialsManager) { $module = Get-Module -ListAvailable -Name "CredentialsManager"; if (!$module) { throw "Module 'CredentialsManager' needed. Please install executing 'Install-Module -Name CredentialsManager' as Administrator."; } } if($UseCredentialsManager -and $Credential -eq $Null) { $Credential = Read-Credential -ListAvailable | Where { $_.Environment -eq "TimeCockpitDB" } } if(!$Credential) { $Credential = Get-Credential -Message "Please enter Credentials for Timecockpit DB."; } if($UseCredentialsManager) { Write-Credential "TimeCockpitDB" -Credential $Credential; } $script:DBCredentials = $Credential; } function Invoke-TCDBSqlCmd { [CmdletBinding()] Param( [string]$Query ) #end param if(!$script:DBCredentials) { Connect-TCDB -UseCredentialsManager $True; } return (Invoke-SqlCmd2 -ServerInstance $tcDBServer -Database $tcDBDatabase -Credential $script:DBCredentials -Query $Query) } ######################################## ### Mgmnt API ### ######################################## <# .SYNOPSIS Returns all users from Management API. #> function Get-TCMgmntUsers { [CmdletBinding()] Param( ) #end param if(!$script:mgmntToken) { Connect-TCMgmnt -UseCredentialsManager $True; } $uri = "${mgmntBaseUrl}/Users"; return $(Invoke-RestMethod -Uri $uri -Method Get -Headers @{Authorization = "Bearer " + $script:mgmntToken.access_token}); } <# .SYNOPSIS Adds a country to Timecockpit. .DESCRIPTION .PARAMETER IsoCode 2 Digits ISO Code of the country .PARAMETER CountryName Name of the country #> function Add-TCCountry { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][string]$IsoCode, [Parameter(Mandatory=$true)][string]$CountryName ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $bodyAsJson = ConvertTo-JSON( @{ APP_CountryName=${CountryName}; APP_IsoCode=${IsoCode}; }); return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Country" -Method Post -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } <# .SYNOPSIS Returns customers from Timecockpit. .DESCRIPTION All parameters are optional filters. Executing with no parameter returns all customers. .PARAMETER Code Code of Timecockpit-Customer to filter for .PARAMETER Uuid Uuid of Timecockpit-Customer to filter for #> function Get-TCCustomer { [CmdletBinding()] Param( [string]$Code, [Guid]$Uuid ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $uri = "${baseUrl}/APP_Customer()"; $filterParams = @{}; if($code) { $filterParams.Add("APP_Code", $Code); } if($uuid) { $filterParams.Add("APP_CustomerUuid", $Uuid); } if($filterParams.Count -gt 0) { $uri = "${uri}?`$filter=$(CreateFilter($filterParams))"; } return $(Invoke-RestMethod -Uri $uri -Method Get -Headers @{Authorization=("Bearer {0}" -f $script:token)}).value; } <# .SYNOPSIS Returns projects from Timecockpit. .DESCRIPTION All parameters are optional filters. Executing with no parameter returns all projects. .PARAMETER CustomerCode Code of Timecockpit-Customer to filter projects for .PARAMETER CustomerUuid Uuid of Timecockpit-Customer to filter projects for .PARAMETER Code Code of Timecockpit-Project to filter projects for .PARAMETER Uuid Uuid of Timecockpit-Customer to filter projects for .PARAMETER Closed By default only open projects are returned. (Closed=False) Set to null to return closed and open projects. #> function Get-TCProject { [CmdletBinding()] Param( [string]$CustomerCode, [Guid]$CustomerUuid, [string]$Code, [Guid]$Uuid, [Nullable[boolean]]$Closed=$False ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $uri = "${baseUrl}/APP_Project()"; $filterParams = @{}; if($CustomerCode) { $filterParams.Add("APP_Customer/APP_Code", $CustomerCode); } if($CustomerUuid) { $filterParams.Add("APP_Customer/APP_CustomerUuid", $CustomerUuid); } if($Code) { $filterParams.Add("APP_Code", $Code); } if($Uuid) { $filterParams.Add("APP_ProjectUuid", $Uuid); } if($Closed -ne $Null) { $filterParams.Add("APP_Closed", $Closed); } if($filterParams.Count -gt 0) { $uri = "${uri}?`$filter=$(CreateFilter($filterParams))"; } return $(Invoke-RestMethod -Uri $uri -Method Get -Headers @{Authorization=("Bearer {0}" -f $script:token)}).value; } <# .SYNOPSIS Adds a Project .DESCRIPTION .PARAMETER Code Code of the new project .PARAMETER ProjectName ProjectName of the new project .PARAMETER StartDate StartDate of project. Defaults to current date .PARAMETER EndDate EndDate of project. Defaults to current date .PARAMETER Budget Budget of new project .PARAMETER BudgetInHours Budget in hours of new project .PARAMETER HourlyRate HourlyRate to set in the project. Leave empty to not set and inherit from customer .PARAMETER FixedPrice If Project is fixed price or not. Defaults to $False #> function Add-TCProject { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][Guid]$CustomerUuid, [Parameter(Mandatory=$true)][string]$Code, [Parameter(Mandatory=$true)][string]$ProjectName, [DateTime]$StartDate, [DateTime]$EndDate, [float]$Budget, [float]$BudgetInHours, [float]$HourlyRate, [boolean]$FixedPrice ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $culture = New-Object System.Globalization.CultureInfo("en-US"); $body = @{ APP_CustomerUuid=$CustomerUuid; APP_Code=$Code; APP_ProjectName=$ProjectName}; if($StartDate) { $body.Add("APP_StartDate", $StartDate.ToString('yyyy-MM-dd')); } if($EndDate) { $body.Add("APP_EndDate", $EndDate.ToString('yyyy-MM-dd')); } if($Budget) { $body.Add("APP_Budget", $Budget.ToString($culture)); } if($BudgetInHours) { $body.Add("APP_BudgetInHours", $BudgetInHours.ToString($culture)); } if($HourlyRate) { $body.Add("APP_HourlyRate", $HourlyRate.ToString($culture)); } if($FixedPrice) { $body.Add("APP_FixedPrice", $FixedPrice); } $bodyAsJson = ConvertTo-Json($body); return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Project" -Method Post -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } <# .SYNOPSIS Modifies a Project .DESCRIPTION .PARAMETER Uuid Uuid of the project to modify .PARAMETER Code Code to set in the project .PARAMETER Description Description to set in the project .PARAMETER Closed Set the closed status of the project .PARAMETER HourlyRate HourlyRate to set in the project #> function Edit-TCProject { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][Guid]$Uuid, [string]$Code, [string]$Description, [Nullable[boolean]]$Closed, [float]$HourlyRate ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $body = @{}; if($Code) { $body.Add("APP_Code", $Code); } if($Description) { $body.Add("APP_Description", $Description); } if($Closed -ne $Null) { $body.Add("APP_Closed", $Closed); } if($HourlyRate) { $culture = New-Object System.Globalization.CultureInfo("en-US"); $body.Add("APP_HourlyRate", $HourlyRate.ToString($culture)); } $bodyAsJson = ConvertTo-Json($body); return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Project(guid'${Uuid}')" -Method Patch -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } <# .SYNOPSIS Closes a Project .DESCRIPTION If the project is already closed, no error is created, its just kept closed. .PARAMETER Uuid Uuid of the project to close #> function Close-TCProject { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][Guid]$Uuid ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $body = "{ 'APP_Closed' : 1 }"; return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Project(guid'${Uuid}')" -Method Patch -Body $body -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } <# .SYNOPSIS Opens a Project .DESCRIPTION If the project is already open, no error is created, its just kept open. .PARAMETER Uuid Uuid of the project to open #> function Open-TCProject { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][Guid]$Uuid ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $body = "{ 'APP_Closed' : 0 }"; return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Project(guid'${Uuid}')" -Method Patch -Body $body -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } <# .SYNOPSIS Adds a Task to a project. .DESCRIPTION Use Get-TCProject for receiving the Uuid of a project. .PARAMETER ProjectUuid Uuid of the project .PARAMETER Code The code of the task to add .PARAMETER Description The Description to set in the task #> function Add-TCTask { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][Guid]$ProjectUuid, [Parameter(Mandatory=$true)][string]$Code, [string]$Description = "" ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $bodyAsJson = ConvertTo-JSON( @{ APP_ProjectUuid=${ProjectUuid}; APP_Code=${Code}; APP_Description=${Description} }); return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Task" -Method Post -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } <# .SYNOPSIS Returns tasks from a project. .DESCRIPTION All parameters are optional filters. Executing with no parameter returns all tasks. Use Get-TCProject for receiving the Uuid of a project. .PARAMETER ProjectUuid Uuid of project to get tasks from .PARAMETER Code Code of the task .PARAMETER Uuid Uuid of the task #> function Get-TCTask { [CmdletBinding()] Param( [Guid]$ProjectUuid, [string]$Code, [Guid]$Uuid ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $uri = "${baseUrl}/APP_Task()"; $filterParams = @{}; if($ProjectUuid) { $filterParams.Add("APP_Project/APP_ProjectUuid", $ProjectUuid); } if($Code) { $filterParams.Add("APP_Code", $Code); } if($Uuid) { $filterParams.Add("APP_TaskUuid", $Uuid); } if($filterParams.Count -gt 0) { $uri = "${uri}?`$filter=$(CreateFilter($filterParams))"; } return $(Invoke-RestMethod -Uri $uri -Method Get -Headers @{Authorization=("Bearer {0}" -f $script:token)}).value; } <# .SYNOPSIS Modifies a Task .DESCRIPTION .PARAMETER Uuid Uuid of the task to modify .PARAMETER Code Code to set in the task .PARAMETER Description Description to set in the task #> function Edit-TCTask { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][Guid]$Uuid, [string]$Code, [string]$Description, [Nullable[boolean]]$Closed ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $body = @{}; if($Code) { $body.Add("APP_Code", $Code); } if($Description) { $body.Add("APP_Description", $Description); } if($Closed -ne $Null) { $body.Add("APP_Closed", $Closed); } $bodyAsJson = ConvertTo-Json($body); return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Task(guid'${Uuid}')" -Method Patch -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } <# .SYNOPSIS Closes a Task .DESCRIPTION If the task is already closed, no error is created, its just kept closed. .PARAMETER Uuid Uuid of the task to close #> function Close-TCTask { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][Guid]$Uuid ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $body = "{ 'APP_Closed' : 1 }"; return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Task(guid'${Uuid}')" -Method Patch -Body $body -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } <# .SYNOPSIS Opens a Task .DESCRIPTION If the task is already open, no error is created, its just kept open. .PARAMETER Uuid Uuid of the task to open #> function Open-TCTask { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][Guid]$Uuid ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $body = "{ 'APP_Closed' : 0 }"; return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Task(guid'${Uuid}')" -Method Patch -Body $body -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } <# .SYNOPSIS Returns matching timesheet entries from timecockpit. .DESCRIPTION .PARAMETER TaskUuid Uuid of to task to get all timesheet entries for #> function Get-TCTimesheets { [CmdletBinding()] Param( [Guid]$TaskUuid ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $uri = "${baseUrl}/APP_Timesheet()"; $filterParams = @{}; if($TaskUuid) { $filterParams.Add("APP_Task/APP_TaskUuid", $TaskUuid); } if($filterParams.Count -gt 0) { $uri = "${uri}?`$filter=$(CreateFilter($filterParams))"; } return $(Invoke-RestMethod -Uri $uri -Method Get -Headers @{Authorization=("Bearer {0}" -f $script:token)}).value; } <# .SYNOPSIS Returns a timesheet entry from timecockpit. .DESCRIPTION .PARAMETER Uuid Uuid of to timesheet #> function Get-TCTimesheet { [CmdletBinding()] Param( [Guid]$Uuid ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $uri = "${baseUrl}/APP_Timesheet()"; $filterParams = @{}; if($Uuid) { $filterParams.Add("APP_TimesheetUuid", $Uuid); } if($filterParams.Count -gt 0) { $uri = "${uri}?`$filter=$(CreateFilter($filterParams))"; } return $(Invoke-RestMethod -Uri $uri -Method Get -Headers @{Authorization=("Bearer {0}" -f $script:token)}).value; } <# .SYNOPSIS Adds a timesheet entry. .DESCRIPTION .PARAMETER ProjectUuid Optional. Uuid of Project to create entry in .PARAMETER TaskUuid Optional. Uuid of Task to create entry in .PARAMETER UserDetailUuid Uuid of User to create entry for .PARAMETER BeginTime DateTime to start entry .PARAMETER EndTime DateTime to end entry .PARAMETER Description Description text of entry #> function Add-TCTimesheet { [CmdletBinding()] Param( [Guid]$ProjectUuid, [Guid]$TaskUuid, [Parameter(Mandatory=$true)][Guid]$UserDetailUuid, [Parameter(Mandatory=$true)][datetime]$BeginTime, [Parameter(Mandatory=$true)][datetime]$EndTime, [Parameter(Mandatory=$true)][string]$Description ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $body = @{ APP_UserDetailUuid=$UserDetailUuid; APP_BeginTime=$BeginTime.ToString('yyyy-MM-ddTHH:mm'); APP_EndTime=$EndTime.ToString('yyyy-MM-ddTHH:mm'); APP_Description=$Description; } if($ProjectUuid) { $body.Add("APP_ProjectUuid", $ProjectUuid) } if($TaskUuid) { $body.Add("APP_TaskUuid", $TaskUuid) } $bodyAsJson = ConvertTo-JSON( $body ); return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Timesheet" -Method Post -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } <# .SYNOPSIS Modifies a timesheet entry .DESCRIPTION .PARAMETER Uuid Uuid of the timesheet to modify .PARAMETER TaskUuid Optional. Uuid of task for timesheet to modify .PARAMETER Description Description to set in the timesheet #> function Edit-TCTimesheet { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][Guid]$Uuid, [Guid]$TaskUuid, [string]$Description ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } $body = @{}; if($Description) { $body.Add("APP_Description", $Description); } if($TaskUuid) { $body.Add("APP_TaskUuid", $TaskUuid); } $bodyAsJson = ConvertTo-Json($body); return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Timesheet(guid'${Uuid}')" -Method Patch -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } <# .SYNOPSIS Removes a timesheet entry .DESCRIPTION .PARAMETER Uuid Uuid of the timesheet to remove #> function Remove-TCTimesheet { [CmdletBinding()] Param( [Parameter(Mandatory=$true)][Guid]$Uuid ) #end param if(!$script:token) { Connect-TC -UseCredentialsManager $True; } return $(Invoke-RestMethod -Uri "${baseUrl}/APP_Timesheet(guid'${Uuid}')" -Method Delete -ContentType "application/json; charset=utf-8" -Headers @{Authorization=("Bearer {0}" -f $script:token)}); } ## Private Functions function CreateFilter($parameters) { $first = $True; foreach($parameter in $parameters.Keys) { $value = $parameters[$parameter]; if(!$First) { $filter = "${filter} and "; } if($value.GetType() -eq [string]) { $filter = "${filter}${parameter} eq '$([System.Web.HTTPUtility]::UrlEncode($value))'"; } elseif($value.GetType() -eq [Guid]) { $filter = "${filter}${parameter} eq guid'${value}'"; } elseif($value.GetType() -eq [Boolean]) { $filter = "${filter}${parameter} eq $(([string]$value).ToLower())"; } else { $filter = "${filter}${parameter} eq ${value}"; } $First = $False; } return $filter; } |