lib/functions/Private/New-OmadaQueryRequest.ps1
|
function New-OmadaQueryRequest { <# .SYNOPSIS Build the method, URI and body for one Omada request in the execute path, from plain values. .DESCRIPTION Every URL and body shape the execute path uses, in one place, expressed as a pure function of a context hashtable: no $Script: reads, no logging, no network. That makes it usable from two places that must never disagree - the UI-thread functions (Save-Query, New-TemporarySqlQueryObject, Remove-SqlQueryObject, Invoke-ExecuteQuery) and Invoke-OmadaExecutePipeline, which runs the same sequence inside a background worker (issue #40, C1-5). Written as one function with a -Kind rather than six small ones so that the request shapes sit side by side: they share a base URL, an entity path and a body convention, and a change to one is usually a change to its neighbours. Being pure is also what makes these testable at all. Before this, "which URL does saving a query use, and when is the body empty?" could only be answered by running the app against a tenant. .PARAMETER Kind Which request to build: SaveExistingQuery PUT the current query. Returns $null when nothing changed - the caller must treat that as "no request needed", exactly as Save-Query's "No changes detected!" branch always has. ExecuteQuery POST GetPagingData for a target query id. TempQueryProbe GET the TMP_<guid> object by name, including deleted ones. TempQueryUndelete POST UndeleteDataObject for a soft-deleted temporary object. TempQueryUpsert PUT (reuse) or POST (create) the temporary object carrying the selection. DeleteQuery DELETE a query object by id. .PARAMETER Context Plain values gathered on the UI thread. Keys used, by Kind: BaseUrl all QueryDoId SaveExistingQuery QueryText SaveExistingQuery CurrentQueryText SaveExistingQuery (the text last known to be saved) SavedQueryText SaveExistingQuery (C_QUERY from the freshly fetched object) DisplayName SaveExistingQuery (what the Display name box holds now) CurrentDisplayName SaveExistingQuery (the name last known to be saved) DataConnectionDoId SaveExistingQuery, TempQueryUpsert TargetQueryDoId ExecuteQuery TempName TempQueryProbe, TempQueryUpsert TempQueryDoId TempQueryUndelete, TempQueryUpsert (reuse), DeleteQuery SelectionText TempQueryUpsert .OUTPUTS Hashtable @{ Method; Uri; Body }, or $null when no request is needed. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateSet("SaveExistingQuery", "ExecuteQuery", "TempQueryProbe", "TempQueryUndelete", "TempQueryUpsert", "DeleteQuery")] [string]$Kind, [Parameter(Mandatory = $true)] [hashtable]$Context ) $Private:BaseUrl = [string]$Context.BaseUrl $Private:EntityUrl = "{0}/odata/dataobjects/C_P_SQLTROUBLESHOOTING" -f $Private:BaseUrl switch ($Kind) { "SaveExistingQuery" { # The diff rules, preserved exactly as Save-Query has always applied them: the query text # is sent when it differs from either what this session last saved OR what the server # actually holds, and the name is sent when the Display name box no longer matches the # name last saved. An empty body means nothing changed. $Private:Body = @{} if ($Context.QueryText -ne $Context.CurrentQueryText -or $Context.QueryText -ne $Context.SavedQueryText) { $Private:Body.Add("C_QUERY", $Context.QueryText) if (![string]::IsNullOrWhiteSpace($Context.DataConnectionDoId)) { $Private:Body.Add("C_SQLTROUBLESHOOTING_DATACONNECTION", @{ Id = $Context.DataConnectionDoId }) } } if ($Context.CurrentDisplayName -ne $Context.DisplayName) { $Private:Body.Add("NAME", $Context.DisplayName) } if (($Private:Body.Keys | Measure-Object).Count -le 0) { return $null } return @{ Method = "PUT" Uri = "{0}({1})" -f $Private:EntityUrl, $Context.QueryDoId Body = $Private:Body } } "ExecuteQuery" { return @{ Method = "POST" Uri = "{0}/webservice/jQGridPopulationWebService.asmx/GetPagingData" -f $Private:BaseUrl Body = @{ "dataType" = "SqlDataProducer" "dataTypeArgs" = @{ "targetId" = $Context.TargetQueryDoId } "page" = 1 "rows" = 100000 "sidx" = $null "sord" = "asc" "_search" = $false "searchField" = $null "searchString" = $null "filters" = $null "searchOper" = $null } } } "TempQueryProbe" { # DeletedStatus=Both on purpose: the temporary object is soft-deleted after every run, so # the one being looked for is normally in the deleted set. return @{ Method = "GET" Uri = "{0}?DeletedStatus=Both&`$filter=NAME eq '{1}'" -f $Private:EntityUrl, $Context.TempName Body = $null } } "TempQueryUndelete" { return @{ Method = "POST" Uri = "{0}/WebService/DataObjectWebService.asmx/UndeleteDataObject" -f $Private:BaseUrl # Pre-serialized, as this endpoint has always been called: it is an ASMX method that # wants a JSON string rather than the object the OData endpoints take. Body = (@{ id = $Context.TempQueryDoId } | ConvertTo-Json) } } "TempQueryUpsert" { $Private:Body = @{ "NAME" = $Context.TempName "C_QUERY" = $Context.SelectionText } if (![string]::IsNullOrWhiteSpace($Context.DataConnectionDoId)) { $Private:Body.Add("C_SQLTROUBLESHOOTING_DATACONNECTION", @{ Id = $Context.DataConnectionDoId }) } # PUT onto the recovered object when there is one, POST a new one otherwise. The name is # always TMP_<InstanceGuid> - never a fresh guid - so a missing temporary object is # recreated under the same name instead of piling up stale ones on the tenant. if (![string]::IsNullOrWhiteSpace($Context.TempQueryDoId)) { return @{ Method = "PUT" Uri = "{0}({1})" -f $Private:EntityUrl, $Context.TempQueryDoId Body = $Private:Body } } return @{ Method = "POST" Uri = $Private:EntityUrl Body = $Private:Body } } "DeleteQuery" { return @{ Method = "DELETE" Uri = "{0}({1})" -f $Private:EntityUrl, $Context.TempQueryDoId Body = $null } } } } |