plugins/Hubspot/Public/peoplestage/invoke-upload.ps1
function Invoke-Upload{ [CmdletBinding()] param ( [Parameter(Mandatory=$false)][Hashtable] $InputHashtable ) begin { #----------------------------------------------- # START TIMER #----------------------------------------------- $processStart = [datetime]::now #$inserts = 0 #----------------------------------------------- # LOG #----------------------------------------------- $moduleName = "UPLOAD" # Start the log Write-Log -message $Script:logDivider Write-Log -message $moduleName -Severity INFO # Log the params, if existing Write-Log -message "INPUT:" if ( $InputHashtable ) { $InputHashtable.Keys | ForEach-Object { $param = $_ Write-Log -message " $( $param ) = '$( $InputHashtable[$param] )'" -writeToHostToo $false } } #----------------------------------------------- # DEBUG MODE #----------------------------------------------- Write-Log "Debug Mode: $( $Script:debugMode )" #----------------------------------------------- # PARSE MESSAGE #----------------------------------------------- #$script:debug = $InputHashtable $uploadOnly = $false # TODO add an option to turn off tagging for upload only If ( "" -eq $InputHashtable.MessageName ) { #Write-Log "A" $uploadOnly = $true $mailing = [Mailing]::new(999, "UploadOnly") } else { #Write-Log "B" Write-Log "Parsing message: '$( $InputHashtable.MessageName )' with '$( $Script:settings.nameConcatChar )' as separator" $mailing = [Mailing]::new($InputHashtable.MessageName) Write-Log "Got chosen message entry with id '$( $mailing.mailingId )' and name '$( $mailing.mailingName )'" #$mailing = [Mailing]::new($InputHashtable.MessageName) #Write-Log "Got chosen message entry with id '$( $mailing.mailingId )' and name '$( $mailing.mailingName )'" } #----------------------------------------------- # DEFAULT VALUES #----------------------------------------------- $uploadSize = $Script:settings.upload.uploadSize #2 # TODO put this later into settings and increase it #----------------------------------------------- # CHECK INPUT FILE #----------------------------------------------- # Checks input file automatically $file = Get-Item -Path $InputHashtable.Path Write-Log -Message "Got a file at $( $file.FullName )" # Add note in log file, that the file is a converted file if ( $file.FullName -match "\.converted$") { Write-Log -message "Be aware, that the exports are generated in Codepage 1252 and not UTF8. Please change this in the Channel Editor." -severity ( [LogSeverity]::WARNING ) } # Count the rows # [ ] if this needs to much performance, this is not needed If ( $Script:settings.upload.countRowsInputFile -eq $true ) { $rowsCount = Measure-Rows -Path $file.FullName -SkipFirstRow Write-Log -Message "Got a file with $( $rowsCount ) rows" } else { Write-Log -Message "RowCount of input file not activated" } #throw [System.IO.InvalidDataException] $msg #Write-Log -Message "Debug Mode: $( $Script:debugMode )" } process { try { #----------------------------------------------- # CREATE GROUP IF NEEDED #----------------------------------------------- $listId = $InputHashtable.ListName #----------------------------------------------- # LOAD HEADER AND FIRST ROWS #----------------------------------------------- # Read first 100 rows $deliveryFileHead = Get-Content -Path $file.FullName -ReadCount 100 -TotalCount 201 -Encoding utf8 $deliveryFileCsv = ConvertFrom-Csv $deliveryFileHead -Delimiter "`t" $headers = [Array]@( $deliveryFileCsv[0].psobject.properties.name ) #----------------------------------------------- # EXAMPLE FOR USING DIFFERENT MODES #----------------------------------------------- <# Switch ( $InputHashtable.mode ) { "taggingOnly" { #$tags = ,$params.MessageName -split "," $tags = [Array]@(,$mailing.mailingName) # TODO only allow one tag for the moment, but can easily be extended to multiple ones } Default { # Combination of a source, a random letter, 7 more random characters and a timestamp $tag = [System.Text.StringBuilder]::new() [void]$tag.Append( $Script:settings.upload.tagSource ) [void]$tag.Append( "." ) [void]$tag.Append(( Get-RandomString -length 1 -ExcludeSpecialChars -ExcludeUpperCase -ExcludeNumbers )) [void]$tag.Append(( Get-RandomString -length 7 -ExcludeSpecialChars -ExcludeUpperCase )) [void]$tag.Append( "_" ) [void]$tag.Append( $processStart.toString("yyyyMMddHHmmss") ) If ( ($uploadOnly -eq $true -and $Script:settings.upload.useTagForUploadOnly -eq $true) -or $uploadOnly -eq $false ) { $tags = [Array]@(, $tag.ToString() ) } else { $tags = [Array]@() } } } Write-Log -Message "Using the tag: $( $tags -join ", " )" #> #----------------------------------------------- # GO THROUGH FILE IN PARTS #----------------------------------------------- # Start stream reader $reader = [System.IO.StreamReader]::new($file.FullName, [System.Text.Encoding]::UTF8) [void]$reader.ReadLine() # Skip first line. $emailIndex = $headers.IndexOf($InputHashtable.EmailFieldName) $i = 0 # row counter $v = 0 # valid counter $j = 0 # uploaded entries counter $k = 0 # upload batches counter $checkObject = [System.Collections.ArrayList]@() $uploadObject = [System.Collections.ArrayList]@() while ($reader.Peek() -ge 0) { #----------------------------------------------- # CREATE THE OBJECT/ROW TO UPLOAD #----------------------------------------------- # raw empty receivers template: https://rest.cleverreach.com/explorer/v3/#!/groups-v3/upsertplus_post $uploadEntry = [PSCustomObject]@{ #"registered" #"activated" #"deactivated" "email" = "" #$dataCsv[$i].email #"global_attributes" = [PSCustomObject]@{} #"attributes" = [PSCustomObject]@{} #"tags" = [Array]@() } # values of current row $values = $reader.ReadLine().split("`t") # put in email address $uploadEntry.email = ($values[$emailIndex]).ToLower() # Add entry to the check object [void]$checkObject.Add( $uploadEntry ) #----------------------------------------------- # VALIDATE AND CHECK ROWS EVERY N ROWS OR AT END #----------------------------------------------- # Do an validation every n records when threshold is reached or if it is the last row $i += 1 if ( ( $i % $uploadSize ) -eq 0 -or $reader.EndOfStream -eq $true ) { Write-Log "CHECK at row $( $i )" # Load IDs to receivers Write-Log "Validate email addresses" Write-Log " $( $checkObject.count ) rows" $filter = [Array]( [Ordered]@{ "propertyName"="email" "operator"="IN" "values"= [Array]@( $checkObject.email.ToLower() ) } ) $validatedAddresses = @(, ( get-crmdata -Object contacts -Filter $filter )) #-properties email, firstname, lastname $v += $validatedAddresses.count # Transform the result $checkedObject = [Array]@( $validatedAddresses.hs_object_id ) Write-Log " $( $checkedObject.count ) left rows" # Add checked objects to uploadobject [void]$uploadObject.AddRange( $checkedObject ) # Clear the current object completely $checkObject.Clear() } #----------------------------------------------- # UPLOAD ROWS EVERY N ROWS OR AT END #----------------------------------------------- # Do an upload when threshold is reached if ( $uploadObject.Count -ge $uploadSize -or $reader.EndOfStream -eq $true ) { # Commit, when size is reached $uploadFinished = $false Write-Log "UPLOAD at row $( $i )" Write-Log " $( $uploadObject.count ) objects ready for upload" Do { Write-Log " $( ( $uploadObject[0..$uploadSize] ).count ) objects/rows will be uploaded" $uploadBody = $uploadObject[0..( $uploadSize - 1 )] If ( $uploadBody.count -gt 0 ) { # Output the request body for debug purposes #Write-Log -Message "Debug Mode: $( $Script:debugMode )" If ( $Script:debugMode -eq $true ) { $tempFile = ".\$( $i )_$( [guid]::NewGuid().tostring() )_request.txt" Set-Content -Value ( ConvertTo-Json $uploadBody -Depth 99 ) -Encoding UTF8 -Path $tempFile } # As a response we get the id of added/removed members back Switch ( $mailing.mailingId ) { "add" { $upload = @( Add-ListMember -ListId 355 -AddMemberships $uploadObject ) $j += $upload.recordsIdsAdded.count } "del" { $upload = @( Remove-ListMember -ListId 355 -RemoveMemberships $uploadObject ) $j += $upload.recordIdsRemoved.count } default { throw "This upload mode is not supported yet" # should not happen! } } # Count the batches $k += 1 # Output the response body for debug purposes If ( $Script:debugMode -eq $true ) { $script:debug += $upload $tempFile = ".\$( $i )_$( [guid]::NewGuid().tostring() )_response.txt" Set-Content -Value ( ConvertTo-Json $upload -Depth 99 ) -Encoding UTF8 -Path $tempFile } $uploadObject.RemoveRange(0,$uploadBody.Count) } else { Write-Log "No more data to upload" } # Do an extra round for remaining records AND if it is the last row If ( $uploadObject.count -gt 0 -and $reader.EndOfStream -eq $true) { $uploadFinished = $true } else { $uploadFinished = $true # Otherwise always do only one upload } } Until ( $uploadFinished -eq $true ) } } Write-Log "Stats for upload" Write-Log " checked $( $i ) rows" Write-Log " $( $v ) valid rows" Write-Log " $( $j ) uploaded records" Write-Log " $( $k ) uploaded batches" } catch { $msg = "Error during uploading data in line $( $_.InvocationInfo.ScriptLineNumber ). Abort!" Write-Log -Message $msg -Severity ERROR -WriteToHostToo $false Write-Log -Message $_.Exception -Severity ERROR throw $_.Exception } finally { # Close the file reader, if open # If the variable is not already declared, that shouldn't be a problem try { $reader.Close() } catch { } #----------------------------------------------- # STOP TIMER #----------------------------------------------- $processEnd = [datetime]::now $processDuration = New-TimeSpan -Start $processStart -End $processEnd Write-Log -Message "Needed $( [int]$processDuration.TotalSeconds ) seconds in total" #----------------------------------------------- # SEND STATUS TO ORBIT #----------------------------------------------- Switch ( $mailing.mailingId ) { "add" { Write-Log "Added $( $j ) records in $( $k ) batches." -severity INFO } "del" { Write-Log "Removed $( $j ) records in $( $k ) batches." -severity INFO } } } #----------------------------------------------- # RETURN VALUES TO PEOPLESTAGE #----------------------------------------------- # count the number of successful upload rows $recipients = $j #$dataCsv.Count # TODO work out what to be saved # put in the source id as the listname $transactionId = Get-ProcessId # return object $return = [Hashtable]@{ # Mandatory return values "Recipients"=$recipients "TransactionId"=$transactionId # General return value to identify this custom channel in the broadcasts detail tables "CustomProvider"= $Script:settings.providername "ProcessId" = Get-ProcessId #$Script:processId # More values for broadcast #"Tag" = ( $tags -join ", " ) #"GroupId" = $groupId #"PreheaderIsSet" = $preheaderIsSet # Some more information for the broadcasts script #"EmailFieldName"= $params.EmailFieldName #"Path"= $params.Path #"UrnFieldName"= $params.UrnFieldName #"TargetGroupId" = $targetGroup.targetGroupId # More information about the different status of the import #"RecipientsIgnored" = $status.report.total_ignored #"RecipientsQueued" = $recipients #"RecipientsSent" = $status.report.total_added + $status.report.total_updated } # log the return object Write-Log -message "RETURN:" $return.Keys | ForEach-Object { $param = $_ Write-Log -message " $( $param ) = '$( $return[$param] )'" -writeToHostToo $false } # return the results $return } end { } } |