Private/Set-AtwsData.ps1

<#
 
    .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) 
    } 
  }
}