Public/Get-PegasusSyncOperations.ps1

function Get-PegasusSyncOperations {
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [Parameter(Mandatory = $false, Position = 0)]
        [Switch] $Delta
    )

    Process {
        $ExistingPersons = Get-PegasusConnectorPersons -ConnectorId $Script:SyncSessionConnectorId
        $ExistingPersonsMap = ($ExistingPersons | Where-Object { $_.($Script:SyncSessionPersonJoinAttribute) } | Group-Object -Property $Script:SyncSessionPersonJoinAttribute -AsHashTable) ?? @{}
        Write-Verbose "Found $(($ExistingPersons | Measure-object).Count) persons in the connector"

        $ExistingPositions = Get-PegasusConnectorPositions -ConnectorId $Script:SyncSessionConnectorId
        $ExistingPositionsMap = ($ExistingPositions | Group-Object -Property "sourceid" -AsHashTable) ?? @{}
        Write-Verbose "Found $(($ExistingPositions | Measure-object).Count) positions in the connector"

        $ExistingGenericData = Get-PegasusConnectorGenericData -ConnectorId $Script:SyncSessionConnectorId
        $ExistingGenericDataMap = ($ExistingGenericData | Group-Object -Property "sourceid" -AsHashTable) ?? @{}
        Write-Verbose "Found $(($ExistingGenericData | Measure-object).Count) generic data in the connector"

        $ExistingOrgUnits = Get-PegasusConnectorOrgUnits -ConnectorId $Script:SyncSessionConnectorId
        $ExistingOrgUnitsMap = ($ExistingOrgUnits | Group-Object -Property "sourceid" -AsHashTable) ?? @{}
        Write-Verbose "Found $(($ExistingOrgUnits | Measure-object).Count) org units in the connector"

        #region Sync persons
        if ($Script:SyncSessionObjects["person"]) {
            Write-Verbose "Syncing persons"
            $inc = 0
            $totalcount = $Script:SyncSessionObjects["person"].Count

            $Script:SyncSessionObjects["person"].GetEnumerator() |
            ForEach-Object {
                $inc += 1
                $Person = $_ # $Person = $Script:SyncSessionObjects["person"].GetEnumerator() | Foreach-Object { $_ } | get-random -count 1
                Write-Debug "Processing person $($Person.Key) ($inc / $totalcount)"
                $ExistingPerson = $ExistingPersonsMap[$Person.Key]

                $Body = @{
                    ssn          = $Person.Value.ssn
                    anchor       = $Person.Value.anchor
                    displayname  = $Person.Value.displayname
                    firstname    = $Person.Value.firstname
                    lastname     = $Person.Value.lastname
                    mobile       = $Person.Value.mobile
                    customfields = $Person.Value.customfields
                }

                if (-not $ExistingPerson) {
                    Write-Verbose "Person $($Person.Key) will be updated ($inc / $totalcount) - $($Body.firstname) $($Body.lastname)"

                    $ExistingPersonsMap[$Person.Key] = New-PegasusSyncOperation -Method Post -Endpoint "/connectors/$($Script:SyncSessionConnectorId)/persons" -Body $Body -Type "Create person"
                    $ExistingPersonsMap[$Person.Key]
                }
                else {
                    $difference = New-Object System.Collections.ArrayList

                    # Determine differences between existing and new person
                    $Body.keys | where-object { $_ -notin "customfields" } | where-object { $Body[$_] -ne $ExistingPerson.$_ } | ForEach-Object { $difference.Add($_) } | Out-Null

                    $Left = $Body.customfields ?? @{}
                    $Right = $ExistingPerson.customfields ?? @{}
                    @($Left.Keys; $Right.Keys) | Sort-Object -Unique | ForEach-Object {
                        $key = $_
                        if ($Left.$key -cne $Right.$key) {
                            $difference.Add("customfields/$key") | Out-Null
                        }
                    }

                    if ($difference.Count -gt 0) {
                        Write-Verbose "Person $($Person.Key) will be updated ($inc / $totalcount) - $($Body.firstname) $($Body.lastname) with diff in these attributes: $($difference -join ", ")"

                        New-PegasusSyncOperation -Method PATCH -Endpoint "/connectors/$($Script:SyncSessionConnectorId)/persons/$($ExistingPerson.id)" -Body $Body -Type "Update person" -Old $ExistingPerson -Difference $difference
                    }
                    else {
                        Write-Debug "Person $($Person.Key) ($inc / $totalcount) - $($Body.firstname) $($Body.lastname) is up to date"
                    }
                }
            }
        }
        else {
            Write-Verbose "No persons to sync"
        }
        #endregion

        #region Sync org units
        if ($Script:SyncSessionObjects["orgunit"]) {
            Write-Verbose "Syncing org units"
            $inc = 0
            $totalcount = $Script:SyncSessionObjects["orgunit"].Count

            $Script:SyncSessionObjects["orgunit"].GetEnumerator() |
            ForEach-Object {
                $inc += 1
                $OrgUnit = $_ # $OrgUnit = $Script:SyncSessionObjects["orgunits"].GetEnumerator() | Foreach-Object { $_ } | get-random -count 1
                Write-Debug "Processing org unit $($OrgUnit.Key) ($inc / $totalcount)"
                $ExistingOrgUnit = $ExistingOrgUnitsMap[$OrgUnit.Key]

                $Body = @{
                    sourceid     = $OrgUnit.Value.sourceid
                    parent       = $null
                    managers     = New-Object System.Collections.ArrayList
                    longname     = $OrgUnit.Value.longname
                    shortname    = $OrgUnit.Value.shortname
                    displayname  = $OrgUnit.Value.displayname
                    customfields = $OrgUnit.Value.customfields
                }

                if ($OrgUnit.Value.parent) {
                    $Body.parent = $ExistingOrgUnitsMap[$OrgUnit.Value.parent].id
                    if (!$Body.parent) {
                        Write-Warning "Parent org unit $($OrgUnit.Value.parent) does not exist"
                    }
                    else {
                        Write-Debug "Parent org unit $($OrgUnit.Value.parent) maps to $($Body.parent)"
                    }
                }

                if ($OrgUnit.Value.managers) {
                    $OrgUnit.Value.managers | ForEach-Object {
                        $manager = $ExistingPersonsMap[$_]
                        if ($manager) {
                            $Body.managers.Add($manager.id) | Out-Null
                        }
                        else {
                            Write-Warning "Manager person $($_) does not exist"
                        }
                    }
                }

                $Body["deputies"] = New-Object System.Collections.ArrayList
                if ($OrgUnit.Value.deputies) {
                    $OrgUnit.Value.deputies | ForEach-Object {
                        $deputy = $ExistingPersonsMap[$_]
                        if ($deputy) {
                            $Body.deputies.Add($deputy.id) | Out-Null
                        }
                        else {
                            Write-Warning "Deputy person $($_) does not exist"
                        }
                    }
                }
                else {
                    if ($ExistingOrgUnit.deputies) {
                        $ExistingOrgUnit.deputies | Foreach-Object {
                            $Body["deputies"].Add($_) | Out-Null
                        }
                    }
                }

                $Body["roleassignments"] = $ExistingOrgUnit.roleassignments

                if (-not $ExistingOrgUnit) {
                    Write-Verbose "Orgunit $($OrgUnit.Key) will be created ($inc / $totalcount) - $($Body.displayname)"

                    $ExistingOrgUnitsMap[$OrgUnit.Key] = New-PegasusSyncOperation -Method Post -Endpoint "/connectors/$($Script:SyncSessionConnectorId)/orgunits" -Body $Body -Type "Create orgunit"
                    $ExistingOrgUnitsMap[$OrgUnit.Key]
                }
                else {
                    $difference = New-Object System.Collections.ArrayList

                    # Determine differences between existing and new position
                    $Body.keys | where-object { $_ -notin "customfields", "managers", "deputies" } | where-object { $Body[$_] -cne $ExistingOrgUnit.$_ } | ForEach-Object { $difference.Add($_) } | Out-Null
                    
                    # Check managers field
                    if ((Compare-Object @($Body.managers ?? @()) @($ExistingOrgUnit.managers ?? @()))) {
                        $difference.Add("managers") | Out-Null
                    }

                    # Check deputies field
                    if ((Compare-Object @($Body.deputies ?? @()) @($ExistingOrgUnit.deputies ?? @()))) {
                        $difference.Add("deputies") | Out-Null
                    }
                    
                    
                    $Left = $Body.customfields ?? @{}
                    $Right = $ExistingOrgUnit.customfields ?? @{}
                    @($Left.Keys; $Right.Keys) | Sort-Object -Unique | ForEach-Object {
                        $key = $_
                        if ($Left.$key -cne $Right.$key) {
                            $difference.Add("customfields/$key") | Out-Null
                        }
                    }

                    if ($difference.Count -gt 0) {
                        Write-Verbose "Orgunit $($OrgUnit.Key) will be updated ($inc / $totalcount) with diff in these attributes: $($difference -join ", ")"

                        Write-Debug "Detailed orgunit change: $($Body | ConvertTo-Json -Depth 10 -Compress) VS $($ExistingOrgUnit | ConvertTo-Json -Depth 10 -Compress)"

                        New-PegasusSyncOperation -Method PATCH -Endpoint "/connectors/$($Script:SyncSessionConnectorId)/orgunits/$($ExistingOrgUnit.id)" -Body $Body -Type "Update orgunit" -Old $ExistingOrgUnit -Difference $difference
                    }
                    else {
                        Write-Debug "OrgUnit $($OrgUnit.Key) ($inc / $totalcount) is up to date"
                    }
                }
            }
            if (!$Delta.IsPresent) {
                $ExistingOrgUnitsMap.GetEnumerator() |
                Where-Object { $Script:SyncSessionObjects["orgunit"].ContainsKey($_.Key) -eq $false } |
                ForEach-Object {
                    Write-Verbose "Orgunit $($_.Key) will be deleted"
                    New-PegasusSyncOperation -Method DELETE -Endpoint "/connectors/$($Script:SyncSessionConnectorId)/orgunits/$($_.Value.id)" -Type "Delete orgunit" -Old $_.Value
                }
            }
        }
        else {
            Write-Verbose "No org units to sync"
        }
        #endregion

        #region Sync positions
        if ($Script:SyncSessionObjects["position"]) {
            Write-Verbose "Syncing positions"
            $inc = 0
            $totalcount = $Script:SyncSessionObjects["position"].Count

            $Script:SyncSessionObjects["position"].GetEnumerator() |
            ForEach-Object {
                $inc += 1
                $Position = $_ # $Position = $Script:SyncSessionObjects["position"].GetEnumerator() | Foreach-Object { $_ } | get-random -count 1
                Write-Debug "Processing position $($Position.Key) ($inc / $totalcount)"
                $ExistingPosition = $ExistingPositionsMap[$Position.Key]

                $Body = @{
                    sourceid     = $Position.Value.sourceid
                    person       = $null
                    startdate    = $Position.Value.startdate
                    enddate      = $Position.Value.enddate
                    primary      = $Position.Value.primary ?? $false
                    manager      = $null
                    orgunit      = $null
                    jobtitle     = $Position.Value.jobtitle
                    jobcode      = $Position.Value.jobcode
                    employeeid   = $Position.Value.employeeid
                    employerid   = $Position.Value.employerid
                    customfields = $Position.Value.customfields
                }

                if ($Position.Value.person) {
                    $Body.person = $ExistingPersonsMap[$Position.Value.person].id
                    if (!$Body.person) {
                        Write-Warning "Person $($Position.Value.person) does not exist yet, not planning to sync position $($Position.Key) for now"
                        return
                    }
                    else {
                        Write-Debug "Person $($Position.Value.person) maps to $($Body.person)"
                    }
                }

                if ($Position.Value.orgunit) {
                    $Body.orgunit = $ExistingOrgUnitsMap[$Position.Value.orgunit].id
                    if (!$Body.orgunit) {
                        Write-Warning "Orgunit $($Position.Value.orgunit) does not exist yet, not planning to sync position $($Position.Key) for now"
                        return
                    }
                    else {
                        Write-Debug "Orgunit $($Position.Value.orgunit) maps to $($Body.orgunit)"
                    }
                }
                if ($Position.Value.manager) {
                    $Body.manager = $ExistingPersonsMap[$Position.Value.manager].id
                    if (!$Body.manager) {
                        Write-Warning "Manager person $($Position.Value.manager) does not exist"
                    }
                    else {
                        Write-Debug "Manager person $($Position.Value.manager) maps to $($Body.manager)"
                    }
                }

                if (-not $ExistingPosition) {
                    Write-Verbose "Position $($Position.Key) will be created ($inc / $totalcount) - $($Body.firstname) $($Body.lastname)"

                    $ExistingPositionsMap[$Position.Key] = New-PegasusSyncOperation -Method Post -Endpoint "/connectors/$($Script:SyncSessionConnectorId)/positions" -Body $Body -Type "Create position"
                    $ExistingPositionsMap[$Position.Key]
                }
                else {
                    $difference = New-Object System.Collections.ArrayList

                    # Determine differences between existing and new position
                    $Body.keys | where-object { $_ -notin "customfields" } | where-object { $Body[$_] -ne $ExistingPosition.$_ } | ForEach-Object { $difference.Add($_) } | Out-Null

                    $Left = $Body.customfields ?? @{}
                    $Right = $ExistingPosition.customfields ?? @{}
                    @($Left.Keys; $Right.Keys) | Sort-Object -Unique | ForEach-Object {
                        $key = $_
                        if ($Left.$key -cne $Right.$key) {
                            $difference.Add("customfields/$key") | Out-Null
                        }
                    }

                    if ($difference.Count -gt 0) {
                        Write-Verbose "Position $($Position.Key) will be updated (resolved to position id $($ExistingPosition.id)) ($inc / $totalcount) with diff in these attributes: $($difference -join ", ")"

                        Write-Debug "Detailed position change: $($Body | ConvertTo-Json -Depth 10 -Compress) VS $($ExistingPosition | ConvertTo-Json -Depth 10 -Compress)"

                        New-PegasusSyncOperation -Method PATCH -Endpoint "/connectors/$($Script:SyncSessionConnectorId)/positions/$($ExistingPosition.id)" -Body $Body -Type "Update position" -Old $ExistingPosition -Difference $difference
                    }
                    else {
                        Write-Debug "Position $($Position.Key) (resolved to position id $($ExistingPosition.id)) ($inc / $totalcount) is up to date"
                    }
                }
            }
            if (!$Delta.IsPresent) {
                $ExistingPositionsMap.GetEnumerator() |
                Where-Object { $Script:SyncSessionObjects["position"].ContainsKey($_.Key) -eq $false } |
                ForEach-Object {
                    Write-Verbose "Position $($_.Key) will be deleted"
                    
                    New-PegasusSyncOperation -Method DELETE -Endpoint "/connectors/$($Script:SyncSessionConnectorId)/positions/$($_.Value.id)" -Type "Delete position" -Old $_.Value
                }
            }
        }
        else {
            Write-Verbose "No positions to sync"
        }
        #endregion
    }
}