Modules/businessdev.ALbuild.Containers/Private/Resolve-BcWebClientUrl.ps1
|
function Resolve-BcWebClientUrl { <# .SYNOPSIS Builds the web client URL that opens one page or report, with company, tenant, filter and mode. .DESCRIPTION URL construction lives here, in PowerShell, rather than in the capture engine - because this is the half that can be tested without a browser, and because these are the parts of the contract that are actually DOCUMENTED and stable: page=<id> report=<id> filter=<expr> mode=View|Edit company=<name> tenant=<name> (Web client URL parameters, learn.microsoft.com/dynamics365/business-central/dev-itpro/ developer/devenv-web-client-urls#url-parameters) 'mode' is supported for CARD pages. For List, RoleCenter and CardPart the client silently stays in View - which is why the engine verifies afterwards that an Edit it was asked for actually arrived, instead of trusting the URL. 'Create' is deliberately not offered anywhere: it writes data, and this command promises a picture. .PARAMETER BaseUrl The container's web client URL, e.g. 'http://172.25.0.77/BC/'. .PARAMETER Kind 'page' or 'report'. .PARAMETER Id The object id. .PARAMETER Company Company to open. Empty = the container's default company. .PARAMETER Tenant Tenant. Default 'default'. .PARAMETER Filter A record filter, e.g. "'No.' IS '10000'" - how a card is addressed, since clicking a row in a list only selects it. .PARAMETER Mode 'View' (default) or 'Edit'. .OUTPUTS String. .EXAMPLE Resolve-BcWebClientUrl -BaseUrl 'http://10.1.0.42/BC/' -Kind page -Id 22 -Company 'CRONUS DE' #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $BaseUrl, [Parameter(Mandatory)] [ValidateSet('page', 'report')] [string] $Kind, [Parameter(Mandatory)] [int] $Id, [string] $Company, [string] $Tenant = 'default', [string] $Filter, [ValidateSet('', 'View', 'Edit')] [string] $Mode = '' ) # Keep whatever query the base URL already carries (Get-BcContainerWebClientUrl appends the # tenant) instead of appending a second '?' and producing a URL the client ignores. $split = "$BaseUrl" -split '\?', 2 $root = $split[0] if (-not $root.EndsWith('/')) { $root += '/' } $query = [System.Collections.Generic.List[string]]::new() $existing = @{} if ($split.Count -gt 1 -and $split[1]) { foreach ($pair in ($split[1] -split '&')) { if (-not $pair) { continue } $name = ($pair -split '=', 2)[0] $existing[$name.ToLowerInvariant()] = $true $query.Add($pair) } } # [uri]::EscapeDataString does NOT produce the same string on both hosts ALbuild targets: .NET # Framework follows RFC 2396, where an apostrophe is an unreserved 'mark' and survives unescaped, # while .NET (Core) follows RFC 3986 and writes %27. A record filter is built out of apostrophes - # "'No.' IS '10000'" - so the same call produced two different URLs depending on whether the task # ran under Windows PowerShell 5.1 or PowerShell 7. Escaping it afterwards is a no-op on .NET, # where it is already %27, and makes 5.1 agree. $escape = { param($value) ([uri]::EscapeDataString("$value")) -replace "'", '%27' } $add = { param($name, $value) if ([string]::IsNullOrWhiteSpace("$value")) { return } if ($existing.ContainsKey($name.ToLowerInvariant())) { return } $query.Add("$name=$(& $escape $value)") } & $add 'tenant' $Tenant & $add 'company' $Company $query.Add("$Kind=$Id") if ($Filter) { $query.Add("filter=$(& $escape $Filter)") } # Only for pages: a report has no view mode, and sending one is noise in a URL that is read by # a human when something goes wrong. if ($Mode -and $Kind -eq 'page') { $query.Add("mode=$Mode") } return $root + '?' + ($query -join '&') } |