- replace variable with you username, sometimes your email address
- replace variable with a password secure string you can generate via command
read-host "Please enter the password" -assecurestring | convertfrom-securestring
- Change all the other details you need in $settings
- replace "" with the file you wish to up- and download
Last active
January 21, 2020 15:50
-
-
Save gitfvb/fca93fac61cb7bd6065d92d458749fff to your computer and use it in GitHub Desktop.
Example of how to handle the Apteco Orbit API / FastStats API v2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
NOTES | |
* swagger: http://www.tealgreenholidays.co.uk/OrbitAPI/swagger/ui/index.html#/About/About_GetDataViews | |
#> | |
################################################ | |
# | |
# SETTINGS | |
# | |
################################################ | |
<# | |
create password secure string via | |
read-host "Please enter the password" -assecurestring | convertfrom-securestring | |
#> | |
$settings = @{ | |
baseUrl = "http://www.tealgreenholidays.co.uk/OrbitAPI/" | |
dataViewName = "CloudDemo" # if set to "", then the script asks for selecting a dataview | |
user = "<email>" | |
password = "<passwordSecureString>" # secure string that cannot be encrypted backwards except for my machine | |
loginType = "SALTED" # SIMPLE|SALTED | |
uploadType = "MULTIPART" # ONEPART|MULTIPART | |
} | |
################################################ | |
# | |
# FUNCTIONS | |
# | |
################################################ | |
# Get-Endpoint -Key "CreateLoginParameters" | |
Function Get-Endpoint{ | |
param( | |
[String]$Key | |
) | |
$endpoints | where { $_.name -eq $Key } | |
} | |
# Resolve the endpoint by adding baseUrl and replace some parameters | |
Function Resolve-Url { | |
param( | |
[Parameter(Mandatory=$true)][PSCustomObject] $endpoint, | |
[Parameter(Mandatory=$false)][Hashtable] $additional, | |
[Parameter(Mandatory=$false)][Hashtable] $query | |
) | |
# build the endpoint | |
$uri = "$( $Script:settings.baseUrl )$( $endpoint.urlTemplate )" | |
# replace the dataview | |
$uri = $uri -replace "{dataViewName}", $Script:settings.dataViewName | |
# replace other parameters in path | |
if ($additional) { | |
$additional.Keys | ForEach { | |
$uri = $uri -replace "{$( $_ )}", $additional[$_] | |
} | |
} | |
# add parts to the query | |
if ($query) { | |
$uri += "?" | |
$query.Keys | ForEach { | |
$uri += "$( $_ )=$( [System.Web.HttpUtility]::UrlEncode($query[$_]) )" | |
} | |
} | |
$uri | |
} | |
Function Get-StringHash | |
{ | |
param( | |
[Parameter(Mandatory=$true)][string]$inputString, | |
[Parameter(Mandatory=$true)][string]$hashName, | |
[Parameter(Mandatory=$false)][string]$salt, | |
[Parameter(Mandatory=$false)][boolean]$uppercase=$false | |
) | |
$string = $inputString + $salt | |
$StringBuilder = New-Object System.Text.StringBuilder | |
[System.Security.Cryptography.HashAlgorithm]::Create($hashName).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($string))|%{ | |
[Void]$StringBuilder.Append($_.ToString("x2")) | |
} | |
$res = $StringBuilder.ToString() | |
if ( $uppercase ) { | |
$res.ToUpper() | |
} else { | |
$res | |
} | |
} | |
Function Crypt-Password { | |
param( | |
[String]$password | |
) | |
$cryptedPassword = @() | |
$password.ToCharArray() | %{[int][char]$_} | ForEach { | |
If ($_ % 2 -eq 0) { | |
$cryptedPassword += [char]( $_ + 1 ) | |
} else { | |
$cryptedPassword += [char]( $_ - 1 ) | |
} | |
} | |
$cryptedPassword -join "" | |
} | |
# https://stackoverflow.com/questions/4533570/in-powershell-how-do-i-split-a-large-binary-file | |
Function Split-File{ | |
param( | |
$inFile, | |
$outPrefix, | |
[Int32] $bufSize | |
) | |
$file = Get-Item -Path $inFile | |
$stream = [System.IO.File]::OpenRead($inFile) | |
$chunkNum = 1 | |
$barr = New-Object byte[] $bufSize | |
$chunks = @() | |
while( $bytesRead = $stream.Read($barr,0,$bufsize)){ | |
$outFile = "$( $file.DirectoryName )\$( $file.Name ).$( $outPrefix )$( $chunkNum )" | |
$ostream = [System.IO.File]::OpenWrite($outFile) | |
$ostream.Write($barr,0,$bytesRead); | |
$ostream.close(); | |
#echo "wrote $outFile" | |
$chunks += $outFile | |
$chunkNum++ | |
} | |
return ,$chunks # the comma enforces the function to return an array with one element rather than a string, if it is only one element | |
} | |
Function Prepare-MultipartUpload { | |
param( | |
[Parameter(Mandatory=$true)][String]$path, | |
[Parameter(Mandatory=$false)]$part = $false | |
) | |
# standard settings | |
$uploadEncoding = "ISO-8859-1" | |
$crlf = "`r`n"; | |
# if multipart, remove the part prefix | |
$fileItem = Get-Item -Path $path | |
if ($part) { | |
$fileName = $fileItem.Name.Substring(0, $fileItem.Name.lastIndexOf('.')) | |
} else { | |
$fileName = $fileItem.Name | |
} | |
# get file, load and encode it | |
$fileBytes = [System.IO.File]::ReadAllBytes($fileItem.FullName); | |
$fileEncoded = [System.Text.Encoding]::GetEncoding($uploadEncoding).GetString($fileBytes); | |
# create guid for multipart upload | |
$boundary = [System.Guid]::NewGuid().ToString(); | |
# create body | |
$body = ( | |
"--$( $boundary )", | |
"Content-Disposition: form-data; name=`"file`"; filename=`"$( $fileName )`"", | |
"Content-Type: application/octet-stream$( $crlf )", | |
$fileEncoded, | |
"--$( $boundary )--$( $crlf )" | |
) -join $crlf | |
# put it together | |
@{ | |
"body"=$body | |
"contentType"="multipart/form-data; boundary=""$( $boundary )""" | |
} | |
} | |
################################################ | |
# | |
# LOAD ALL ENDPOINTS FIRST | |
# | |
################################################ | |
$pageSize = 100 | |
$offset = 0 | |
$endpoints = @() | |
Do { | |
$uri = "$( $settings.baseUrl )About/Endpoints?count=$( $pageSize )&offset=$( $offset )" | |
$res = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json" -Verbose | |
$endpoints += $res.list | |
$offset += $pageSize | |
} Until ( $endpoints.count -eq $res.totalCount ) | |
#$endpoints | out-gridview | |
################################################ | |
# | |
# LOAD SOME METADATA AND CHOOSE DATAVIEW | |
# | |
################################################ | |
# Get the current version of APIv2 and output to console | |
$endpoint = Get-Endpoint -key "GetVersionDetails" | |
$uri = Resolve-Url -endpoint $endpoint | |
$version = Invoke-RestMethod -Uri $uri -Method $endpoint.method -ContentType "application/json" | |
$version.version | |
# Set dataview, if not filled in settings | |
if ( $settings.dataViewName -eq "" ) { | |
$endpoint = Get-Endpoint -key "GetDataViews" # GetDataViews|GetDataViewsForDomain|GetDataViewsForSystemName | |
$uri = Resolve-Url -endpoint $endpoint | |
$dataviews = Invoke-RestMethod -Uri $uri -Method $endpoint.method -ContentType "application/json" | |
$settings.Item("dataViewName") = ( $dataviews.list | Out-GridView -PassThru ).Name | |
} | |
################################################ | |
# | |
# LOGIN VIA API | |
# | |
################################################ | |
#----------------------------------------------- | |
# LOAD CREDENTIALS | |
#----------------------------------------------- | |
$credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist $settings.user,($settings.password | ConvertTo-SecureString) | |
$user = $credentials.GetNetworkCredential().Username | |
$pw = $credentials.GetNetworkCredential().password | |
#----------------------------------------------- | |
# PREPARE LOGIN | |
#----------------------------------------------- | |
$headers = @{ | |
"accept"="application/json" | |
} | |
switch ( $settings.loginType ) { | |
#----------------------------------------------- | |
# SIMPLE LOGIN PREPARATION | |
#----------------------------------------------- | |
"SIMPLE" { | |
$endpoint = Get-Endpoint -key "CreateSessionSimple" | |
$body = @{ | |
"UserLogin"=$user | |
"Password"=$pw | |
} | |
} | |
#----------------------------------------------- | |
# SALTED LOGIN PREPARATION | |
#----------------------------------------------- | |
"SALTED" { | |
# GET LOGIN DETAILS FIRST | |
$endpoint = Get-Endpoint -key "CreateLoginParameters" | |
$body = @{ | |
"userName"=$user | |
} | |
$uri = Resolve-Url -endpoint $endpoint | |
$loginDetails = Invoke-RestMethod -Uri $uri -Method $endpoint.method -ContentType "application/x-www-form-urlencoded" -Headers $headers -Body $body -Verbose | |
# GET ALL INFORMATION TOGETHER | |
$endpoint = Get-Endpoint -key "CreateSessionSalted" | |
<# | |
1. "Encrypt" password + optionally add salt | |
2. Hash that string | |
3. Add LoginSalt and hash again | |
#> | |
$pwStepOne = Crypt-Password -password $pw | |
if ($loginDetails.saltPassword -eq $true -and $loginDetails.userSalt -ne "") { | |
# TODO [ ] test password salting (and if userSalt from API is the correct value for that) | |
# TODO [ ] put salt in settings | |
$pwStepOne += $loginDetails.userSalt | |
} | |
$pwStepTwo = Get-StringHash -inputString $pwStepOne -hashName $loginDetails.hashAlgorithm -uppercase $false | |
$pwStepThree = Get-StringHash -inputString $pwStepTwo -hashName $loginDetails.hashAlgorithm -salt $loginDetails.loginSalt -uppercase $false | |
$body = @{ | |
"Username"=$user | |
"LoginSalt"=$loginDetails.loginSalt | |
"PasswordHash"=$pwStepThree | |
} | |
} | |
} | |
#----------------------------------------------- | |
# LOGIN + GET SESSION | |
#----------------------------------------------- | |
$uri = Resolve-Url -endpoint $endpoint | |
$login = Invoke-RestMethod -Uri $uri -Method $endpoint.method -ContentType "application/x-www-form-urlencoded" -Headers $headers -Body $body -Verbose | |
$login | |
$auth = "Bearer $( $login.accessToken )" | |
$session = $login.sessionId | |
$headers += @{ "Authorization"=$auth } | |
################################################ | |
# | |
# DO SOMETHING | |
# | |
################################################ | |
#----------------------------------------------- | |
# GET SESSION DETAILS | |
#----------------------------------------------- | |
$endpoint = Get-Endpoint -key "GetSessionDetails" | |
$uri = Resolve-Url -endpoint $endpoint -additional @{"sessionId"=$session} | |
$sessionDetails = Invoke-RestMethod -Uri $uri -Method $endpoint.method -Headers $headers -Verbose | |
$sessionDetails | |
################################################ | |
# | |
# UPLOAD TEMPORARY FILE | |
# | |
################################################ | |
<# | |
Some links for multipart upload | |
hint from https://stackoverflow.com/questions/36268925/powershell-invoke-restmethod-multipart-form-data | |
other: https://stackoverflow.com/questions/51739874/multipart-form-script-works-perfectly-in-powershell-but-not-in-powershell-core | |
with .NET classes https://get-powershellblog.blogspot.com/2017/09/multipartform-data-support-for-invoke.html | |
#> | |
# Choose file | |
$fileItem = Get-Item -Path "<uploadfile>" | |
$fileId = [System.Guid]::NewGuid().ToString() | |
switch ( $settings.uploadType ) { | |
#----------------------------------------------- | |
# UPLOAD IN ONE PART | |
#----------------------------------------------- | |
"ONEPART" { | |
# Prepare multipart | |
$multipart = Prepare-MultipartUpload -path $fileItem.FullName | |
# Prepare API call | |
$endpoint = Get-Endpoint -key "UpsertTemporaryFile" | |
$uri = Resolve-Url -endpoint $endpoint -additional @{"id"=$fileId} | |
# Execute API call | |
$tempUpload = Invoke-RestMethod -Uri $uri -Method $endpoint.method -ContentType $multipart.contentType -Headers $headers -Body $multipart.body -Verbose | |
} | |
#----------------------------------------------- | |
# UPLOAD IN MULTIPLE PARTS | |
#----------------------------------------------- | |
"MULTIPART" { | |
# TODO [ ] put no of parts or buffersize in settings | |
$noParts = 2 | |
$partPrefix = "part" | |
$secondsToWait = 30 | |
# create chunks | |
$buffer = $fileItem.Length / $noParts | |
$chunks = Split-File -inFile $fileItem.FullName -outPrefix $partPrefix -bufSize $buffer | |
# Prepare API call | |
$endpoint = Get-Endpoint -key "UpsertTemporaryFilePart" | |
# Upload chunks | |
$final = $false | |
$tempUpload = @() | |
for ( $i = 0; $i -lt $chunks.Length ; $i++ ) { | |
# Choose part | |
$partFile = $chunks[$i] | |
if ( $i+1 -eq $chunks.Length ) { $final = $true } | |
# Prepare part | |
$multipart = Prepare-MultipartUpload -path $partFile -part $true | |
$uri = Resolve-Url -endpoint $endpoint -additional @{"id"=$fileId; "partNumber"=$i} -query @{"finalPart"="$( $final )".ToLower()} | |
# Execute API call | |
$tempUpload += Invoke-RestMethod -Uri $uri -Method $endpoint.method -ContentType $multipart.contentType -Headers $headers -Body $multipart.body -Verbose | |
} | |
# Wait a moment for the file to be processed | |
Start-Sleep -Seconds $secondsToWait | |
# Remove part files | |
$chunks | ForEach { Remove-Item $_ } | |
} | |
} | |
# Result of upload | |
$tempUpload | |
#----------------------------------------------- | |
# DOWNLOAD IN ONE PART | |
#----------------------------------------------- | |
# Where to save the file | |
$dlLocation = "$( $fileItem.DirectoryName )\$( $fileId )$( $fileItem.Extension )" | |
# Download file directly | |
$endpoint = Get-Endpoint -key "GetTemporaryFile" | |
$uri = Resolve-Url -endpoint $endpoint -additional @{"id"=$fileId} | |
Invoke-RestMethod -Uri $uri -Method $endpoint.method -Headers $headers -Verbose -ContentType "application/octet-stream" -OutFile $dlLocation | |
################################################ | |
# | |
# PEOPLESTAGE | |
# | |
################################################ | |
<# | |
# show all available endpoints | |
$endpoints | Out-GridView | |
# Get the current system | |
$endpoint = Get-Endpoint -key "GetPeopleStageSystems" | |
$uri = Resolve-Url -endpoint $endpoint -additional @{"dataViewName"=$settings.dataViewName} -query @{"count"=1000000} | |
$peoplestageSystems = Invoke-RestMethod -Uri $uri -Method $endpoint.method -Headers $headers -Verbose -ContentType "application/json" | |
$systemName = $peoplestageSystems.list[0].systemName | |
$diagramId = $peoplestageSystems.list[0].diagramId | |
$programmeId = $peoplestageSystems.list[0].programmeId | |
# Get all campaigns by diagram ID | |
$endpoint = Get-Endpoint -key "GetElementStatusForDescendants" | |
$uri = Resolve-Url -endpoint $endpoint -additional @{"dataViewName"=$settings.dataViewName;"systemName"=$systemName;"elementId"=$diagramId} -query @{"offset"=0;"count"=1000000;"orderBy"="-LastRan";"filter"="Type eq 'Campaign'"} | |
$peoplestageCampaigns = Invoke-RestMethod -Uri $uri -Method $endpoint.method -Headers $headers -Verbose -ContentType "application/json" | |
$peoplestageCampaigns.list | Out-GridView | |
# Get all audiences for campaigns | |
$campaignElements = @() | |
$peoplestageCampaigns.list | select -first 5 | ForEach { | |
$campaign = $_ | |
$endpoint = Get-Endpoint -key "GetElementChildren" | |
$uri = Resolve-Url -endpoint $endpoint -additional @{"dataViewName"=$settings.dataViewName;"systemName"=$systemName;"elementId"=$campaign.id} -query @{"offset"=0;"count"=1000000} | |
$elements = Invoke-RestMethod -Uri $uri -Method $endpoint.method -Headers $headers -Verbose -ContentType "application/json" | |
$elements #.list | Out-GridView | |
$campaignElements += $elements.list | |
} | |
#> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In the Orbit API configurator you can set the temp file directory in "TemporaryFile service", normally in c:\temp\Orbit...