Private/Set-AtwsData.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<#
 
    .COPYRIGHT
    Copyright (c) Office Center Hønefoss AS. All rights reserved. Licensed under the MIT license.
    See https://github.com/officecenter/Autotask/blob/master/LICENSE.md for license information.
 
#>


Function Set-AtwsData {
  <#
      .SYNOPSIS
      This function updates one or more Autotask entities with new or modified properties.
      .DESCRIPTION
      This function updates one or more Autotask entities with new or modified properties
      .INPUTS
      Autotask.Entity[]. One or more Autotask entities to update
      .OUTPUTS
      Autotask.Entity[]. The updated entities are re-downloaded from the API.
      .EXAMPLE
      Set-AtwsData -Entity $Entity
      Passes all Autotask entities in $Entity to the Autotask webservices API
      .NOTES
      NAME: Set-AtwsData
      .LINK
      Get-AtwsData
      New-AtwsData
      Remove-AtwsData
  #>

 
  [cmdletbinding()]
  [OutputType([PSObject[]])]
  param
  (
    [Parameter(
        Mandatory = $True,
        ValueFromPipeline = $True
    )]
    [ValidateNotNullOrEmpty()]
    [PSObject[]]
    $Entity,
    
    [ValidateRange(0,100)]
    [Int]
    $ErrorLimit = 10
  )
    
    
  Begin { 
    # Enable modern -Debug behavior
    If ($PSCmdlet.MyInvocation.BoundParameters['Debug'].IsPresent) { $DebugPreference = 'Continue' }
    
    Write-Debug ('{0}: Begin of function' -F $MyInvocation.MyCommand.Name)
    
    If (-not($Script:Atws.Url)) {
      Throw [ApplicationException] 'Not connected to Autotask WebAPI. Re-import module with valid credentials.'
    }
    

    $ErrorCount = 0
  }
  
  Process { 
    Write-Verbose ('{0}: Updating Autotask {1} with id {2}' -F $MyInvocation.MyCommand.Name, $Entity[0].GetType().Name, $($Entity.id -join ', '))

    # update() function can take up to 200 objects at a time
    For ($i = 0; $i -lt $Entity.count; $i += 200) {
      $j = $i + 199
      If ($j -ge $Entity.count) {
        $j = $Entity.count - 1
      } 
      Write-Debug -Message ('{0}: Creating chunk from index {1} to index {2}' -F $MyInvocation.MyCommand.Name, $i, $j)        
        
      [Collections.ArrayList]$WorkingSet = $Entity[$i .. $j]
        
      # First try
      $Result = $atws.update($WorkingSet)
        
      # If we have errors, try to exclude objects errors
      If ($Result.Errors.Count -gt 0) {
        Do { 
          $Errors = @()
          For ($t = 0; $t -lt $Result.Errors.Count; $t += 2) {
            # Count the errors, we have a limit
            $ErrorCount++
              
            # First line is the error message
            $Message = $Result.Errors[$t].Message
              
            If ($Result.Errors.Count -gt $t) { 
              # Next line may include the element index, first element = 1
              If ($Result.Errors[$t + 1].Message -match '\[(\d+)\]') { 
                
                [int]$Index = $Matches[1]
              }
              Else {
                $Index = 1
              }
            }
              
            # Powershell arrays has first element = 0
            $Index--
            
            # Get the element
            $Element = $WorkingSet[$Index]
            
            # Remove Element from Workingset
            $Errors += $Element
              
            
            # Notify caller of skipped element
            Write-Warning ('Element with index {0} of type {1} with Id {2} was skipped because {3}' -F $Entity.IndexOf($Element), $Element.GetType().Name, $Element.id, $Message)
            
          }

          Foreach ($Element in $Errors) {
            $WorkingSet.Remove($Element)
          }
          
          # Try updating a second time
          $Result = $atws.update($WorkingSet)
        } Until ($Result.Errors.Count -eq 0 -or $WorkingSet.Count -eq 0 -or $ErrorCount -ge $ErrorLimit)
      }
    
      # We have tried multiple times! Still errors?
      If ($Result.Errors.Count -eq 0) {
        # The API documentation explicitly states that you can only use the objects returned
        # by the .create() function to get the new objects ID.
        # so to return objects with accurately represents what has been created we have to
        # get them again by id
        # But not all objects support queries, for instance service adjustments
          
        $EntityInfo = Get-AtwsFieldInfo -Entity $Result.EntityResultType -EntityInfo
          
        If ($Result.EntityResults.Count -gt 0 -and $EntityInfo.CanQuery)
        {
          $NewObjectFilter = 'id -eq {0}' -F ($Result.EntityResults.Id -join ' -or id -eq ')
                        
          $EndResult += Get-AtwsData -Entity $Result.EntityResultType -Filter $NewObjectFilter
        }
      }
      Else {
          
        Write-Error ($Result.Errors.Message -join "`n")
        Break
      }
    }
  }
  End {
    If ($EndResult.count -gt 0) { 
      
      Write-Debug ('{0}: End of function, returning {1} updated {2}(s)' -F $MyInvocation.MyCommand.Name, $Result.count, $Result[0].GetType().Name) 
      Return $EndResult 
    }
    Else
    {
      Write-Debug ('{0}: End of function, no objects to return.' -F $MyInvocation.MyCommand.Name, $Result.count) 
    } 
  }
}