Private/Uri.ps1
|
function ConvertTo-DattoApiPathSegment { [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] [AllowEmptyString()] [string]$Value ) process { [uri]::EscapeDataString($Value) } } function ConvertTo-DattoApiInvariantString { [CmdletBinding()] param( [Parameter(Mandatory)] [AllowEmptyString()] [object]$Value ) if ($Value -is [bool]) { return $Value.ToString().ToLowerInvariant() } if ($Value -is [datetimeoffset]) { return $Value.ToString('o', [Globalization.CultureInfo]::InvariantCulture) } if ($Value -is [datetime]) { return $Value.ToString('o', [Globalization.CultureInfo]::InvariantCulture) } if ($Value -is [System.IFormattable]) { return $Value.ToString($null, [Globalization.CultureInfo]::InvariantCulture) } return [string]$Value } function ConvertTo-DattoApiQueryString { [CmdletBinding()] param( [Parameter()] [AllowNull()] [System.Collections.IDictionary]$Query ) if ($null -eq $Query -or $Query.Count -eq 0) { return '' } $pairs = [System.Collections.Generic.List[string]]::new() foreach ($key in @($Query.Keys | Sort-Object)) { $value = $Query[$key] if ($null -eq $value) { continue } if (($value -is [System.Collections.IEnumerable]) -and -not ($value -is [string]) -and -not ($value -is [System.Collections.IDictionary])) { $parts = [System.Collections.Generic.List[string]]::new() foreach ($part in $value) { if ($null -ne $part) { $parts.Add((ConvertTo-DattoApiInvariantString -Value $part)) } } $valueText = $parts -join ',' } else { $valueText = ConvertTo-DattoApiInvariantString -Value $value } $encodedKey = [uri]::EscapeDataString([string]$key) $encodedValue = [uri]::EscapeDataString($valueText) $pairs.Add(('{0}={1}' -f $encodedKey, $encodedValue)) } return ($pairs -join '&') } function Copy-DattoApiQuery { [CmdletBinding()] param( [Parameter()] [AllowNull()] [System.Collections.IDictionary]$Query ) $copy = @{} if ($null -ne $Query) { foreach ($key in $Query.Keys) { $copy[$key] = $Query[$key] } } return $copy } function New-DattoApiRequestUri { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.IDictionary]$ConnectionState, [Parameter(Mandatory)] [string]$Path, [Parameter()] [AllowNull()] [System.Collections.IDictionary]$Query ) if (-not $Path.StartsWith('/v1/', [StringComparison]::OrdinalIgnoreCase)) { throw [System.ArgumentException]::new("Datto API paths must begin with '/v1/'. Received '$Path'.") } if ($Path.StartsWith('//') -or $Path.Contains('://') -or $Path.Contains('?') -or $Path.Contains('#') -or $Path.Contains('\')) { throw [System.ArgumentException]::new('The API path must be a relative Datto v1 path without a host, query string, fragment, or backslash.') } foreach ($character in $Path.ToCharArray()) { if ([char]::IsControl($character)) { throw [System.ArgumentException]::new('The API path contains a control character.') } } $decodedPath = [uri]::UnescapeDataString($Path) foreach ($character in $decodedPath.ToCharArray()) { if ([char]::IsControl($character)) { throw [System.ArgumentException]::new('The decoded API path contains a control character.') } } foreach ($segment in $decodedPath.Split('/')) { if ($segment -eq '.' -or $segment -eq '..') { throw [System.ArgumentException]::new('The API path cannot contain dot or parent-directory segments.') } } $baseUri = [uri]$ConnectionState.BaseUri $uriText = $baseUri.AbsoluteUri.TrimEnd('/') + $Path $queryString = ConvertTo-DattoApiQueryString -Query $Query if (-not [string]::IsNullOrWhiteSpace($queryString)) { $uriText += '?' + $queryString } return [uri]$uriText } |