private/projectDatabase/project_database_Async.ps1

Set-MyInvokeCommandAlias -Alias GitHub_UpdateProjectV2ItemFieldValueAsync -Command 'Import-Module {projecthelper} ; Invoke-GitHubUpdateItemValues -ProjectId {projectid} -ItemId {itemid} -FieldId {fieldid} -Value "{value}" -Type {type}'

function Sync-ProjectDatabaseAsync{
    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([bool])]
    param(
        [Parameter(Position = 0)][string]$Owner,
        [Parameter(Position = 1)][int]$ProjectNumber
    )

    ($Owner,$ProjectNumber) = Get-OwnerAndProjectNumber -Owner $Owner -ProjectNumber $ProjectNumber
    if([string]::IsNullOrWhiteSpace($owner) -or [string]::IsNullOrWhiteSpace($ProjectNumber)){ "Owner and ProjectNumber are required" | Write-MyError; return $null}

    if(! $(Test-ProjectDatabaseStaged -Owner $Owner -ProjectNumber $ProjectNumber)){
        "Nothing to commit" | Write-MyHost
        return
    }

    $dbkey = GetDatabaseKey -Owner $Owner -ProjectNumber $ProjectNumber

    $db = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber

    # Send update to project
    $result = Sync-ProjectAsync -Database $db
    if ($null -eq $result) {
        return $false
    }

    # Clear the values that are the same
    $different = New-Object System.Collections.Hashtable
    $equal = New-Object System.Collections.Hashtable
    foreach($itemId in $db.Staged.Keys){
        foreach($fieldId in $db.Staged.$itemId.Keys){
            $fieldName = $db.fields.$fieldId.name

            $stagedV = $db.Staged.$itemId.$fieldId.Value
            $actualV = $db.items.$itemId.$fieldName

            if(!($stagedV -eq $actualV)){
                # Create refe to failing
                $different."$($itemId)_$($Fieldid)" = @{
                    Id = $itemId
                    Field = $fieldId
                    Staged = $stagedV
                    Actual = $actualV
                }
            } else {
                # Create refe success
                $equal."$($itemId)_$($Fieldid)" = @{
                    Id = $itemId
                    Field = $fieldId
                }
            }
        }
    }

    $SyncedCount = $equal.Keys.Count
    $NotSyncedCount = $different.Keys.Count

    # removed equal staged values
    foreach($key in $equal.Keys){
        $itemId = $equal.$key.Id
        $fieldId = $equal.$key.Field

        # Remove staged field
        $db.Staged.$itemId.Remove($fieldId)

        # remove staged item if all are removed
        if($db.Staged.$itemId.Keys.Count -eq 0){
            $db.Staged.Remove($itemId)
        }
    }

    #null Staged if empty
    if($db.Staged.Keys.Count -eq 0){
        $db.Staged = $null
    }
    
    Save-Database -Key $dbkey -Database $db

    if($different.Count -ne 0){
        "Not all Staged values are not equal to actual values" | Write-MyError
        $different | convertto-json | Write-MyError
        return $false
    }

    "Synced $SyncedCount values (Failed: $NotSyncedCount)" | Write-MyHost

    return $true

}

function Sync-ProjectAsync{
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Position = 0)][object]$Database
    )

    $db = $Database
    $calls = @()

    foreach($itemId in $db.Staged.Keys){
        foreach($fieldId in $db.Staged.$itemId.Keys){

            $projectId = $db.ProjectId
            $value = $db.Staged.$itemId.$fieldId.Value
            $type = ConvertTo-UpdateType $db.Staged.$itemId.$fieldId.Field.dataType

            $params = @{
                projecthelper = $MODULE_PATH
                projectid = $projectId
                itemid = $itemId
                fieldid = $fieldId
                value = $value
                type = $type
            }

            "Calling to save [$projectId/$itemId/$fieldId ($type) = $value ]" | Write-MyHost

            $job = Start-MyJob -Command GitHub_UpdateProjectV2ItemFieldValueAsync -Parameters $params

            $call = [PSCustomObject]@{
                job = $job
                projectId = $projectId
                itemId = $itemId
                value = $value
                fieldId = $fieldId
                type = $type
                fieldName = $db.fields.$fieldId.name
            }

            $calls += $call

        }
    }

    # "Waiting for all calls to finish ..." | Write-MyHost
    # $results = $calls.job | Wait-Job
    
    $isDone = $false
    $all = $calls.job.Count
    $waitingJobs = $calls.job

    "Waiting for all calls to finish [$($waitingJobs.Count)] " | Write-MyHost -noNewline

    while(!$isdone){

        $waitings = $waitingJobs | Wait-Job -Any

        "." | Write-MyHost -NoNewline

        $completed = ($calls.job | Where-Object{$_.State -eq "Completed"}).Count
        $failed = ($calls.job | Where-Object{$_.State -eq "Failed"}).Count

        # $running = ($calls.job | Where-Object{$_.State -eq "Running"}).Count
        # "Running [$running] Completed [$completed] Failed [$failed] TOTAL [$all]" | Write-MyHost

        # Remove completed jobs from the waiting list
        $waitingJobs = $waitingJobs | Where-Object { $_.Id -ne $waitings.Id }
        
        $isDone = ($completed + $failed) -eq $all
    }
    "" | Write-MyHost
    "Completed [$completed] Failed [$failed] TOTAL [$all]" | Write-MyHost


    # Process all the calls
    foreach($call in $calls){

        $result = Receive-Job -Job $call.job

        $projectId = $call.projectId
        $itemId = $call.itemId
        $fieldId = $call.fieldId
        $fieldName = $call.fieldName
        $value = $call.value

        if ($null -eq $result.data.updateProjectV2ItemFieldValue.projectV2Item) {
            # TODO: Maybe worth checking response values to confirm change was made correctly even without error
            "Updating Project Item call Failed [$itemId/$fieldName/$value]" | Write-MyError
            continue
        }

        "Saving to database [$projectId/$itemId/$fieldName ($type) = $value ]" | Write-MyHost

        if ($PSCmdlet.ShouldProcess($itemId, "Set-ProjectV2Item")) {
            # update database with change
            $db.items.$itemId.$fieldName = $value
        }
    }

    return $db
}

function Sync-Project{
    [CmdletBinding()]
    param(
        [Parameter(Position = 0)][object]$Database
    )

    $db = $Database

    foreach($idemId in $db.Staged.Keys){
        foreach($fieldId in $db.Staged.$idemId.Keys){

            $project_id = $db.ProjectId
            $item_id = $idemId
            $field_id = $fieldId
            $value = $db.Staged.$idemId.$fieldId.Value
            $type = ConvertTo-UpdateType $db.Staged.$idemId.$fieldId.Field.dataType

            $params = @{
                projectid = $project_id
                itemid = $item_id
                fieldid = $field_id
                value = $value
                type = $type
            }

            "Saving [$project_id/$item_id/$field_id ($type) = $value ]" | Write-MyHost

            $result = Invoke-MyCommand -Command GitHub_UpdateProjectV2ItemFieldValue -Parameters $params

            if ($null -eq $result) {
                "Updating Project Item Field [$item_id/$field_id/$value]" | Write-MyError
                return $null
            }

            if ($PSCmdlet.ShouldProcess($item.url, "Set-ProjectV2Item")) {
                # update database with change
                $fieldName = $db.fields.$fieldId.name
                $db.items.$item_id.$fieldName = $value

                # $item = Convert-ItemFromResponse $projectV2Item
                # Set-ProjectV2Item2Database $db $projectV2Item -Item $item
                # $projectV2Item = $result.data.updateProjectV2ItemFieldValue.projectV2Item
            }
        }
    }

    return $db
}