Private/New-DataverseHeaders.ps1
function New-DataverseHeaders { <# .SYNOPSIS Build Dataverse HTTP headers from common options. .DESCRIPTION Produces a case-insensitive hashtable of HTTP headers for Dataverse, including auth and OData defaults. Allows composing Prefer directives, conditional headers, correlation IDs, and localization. .PARAMETER AccessToken OAuth access token (raw JWT string). Required for Authorization header. .PARAMETER Accept Accept header value. Defaults to application/json. .PARAMETER ContentType Content-Type header value. Optional; usually application/json when sending a body. .PARAMETER ODataVersion OData-Version header. Defaults to 4.0. .PARAMETER ODataMaxVersion OData-MaxVersion header. Defaults to 4.0. .PARAMETER PreferReturnRepresentation When true, include Prefer: return=representation. .PARAMETER PreferODataMaxPageSize Include odata.maxpagesize in Prefer header. .PARAMETER PreferIncludeAnnotations Include odata.include-annotations in Prefer header (e.g. "*"). .PARAMETER ConsistencyLevel ConsistencyLevel header (e.g., "eventual"). .PARAMETER IfMatch If-Match header value (e.g., "*"). .PARAMETER IfNoneMatch If-None-Match header value. .PARAMETER CorrelationId Correlation identifier (GUID string). Sent as x-ms-client-request-id. .PARAMETER SuppressDuplicateDetection When specified, set MSCRM.SuppressDuplicateDetection=true. .PARAMETER AcceptLanguage Accept-Language header value (e.g., "en-US"). .PARAMETER ExtraHeaders Additional headers to merge in last, overriding built values when conflicts occur. .OUTPUTS Hashtable #> [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string] $AccessToken, [string] $Accept = 'application/json', [string] $ContentType, [string] $ODataVersion = '4.0', [string] $ODataMaxVersion = '4.0', [switch] $PreferReturnRepresentation, [int] $PreferODataMaxPageSize, [string] $PreferIncludeAnnotations, [ValidateSet('eventual')] [string] $ConsistencyLevel, [string] $IfMatch, [string] $IfNoneMatch, [string] $CorrelationId, [switch] $SuppressDuplicateDetection, [string] $AcceptLanguage, [hashtable] $ExtraHeaders ) # Start with core headers # Note: We intentionally keep this builder deterministic: # - Always sets Authorization + OData defaults # - Accept/Content-Type are optional (Content-Type typically set by caller/core when body exists) # - ExtraHeaders apply LAST and can override, except for a composed Prefer we build below $headers = @{} $headers['Authorization'] = "Bearer $AccessToken" if ($Accept) { $headers['Accept'] = $Accept } if ($ContentType) { $headers['Content-Type'] = $ContentType } if ($ODataVersion) { $headers['OData-Version'] = $ODataVersion } if ($ODataMaxVersion) { $headers['OData-MaxVersion'] = $ODataMaxVersion } # Compose Prefer # We support multiple Prefer parts; if the caller also provides a Prefer header via ExtraHeaders, # we MERGE the missing parts instead of overwriting. This ensures flags like return=representation # or odata.maxpagesize are preserved while still honoring caller-provided entries. $preferComposed = $false $preferParts = @() if ($PreferReturnRepresentation.IsPresent) { $preferParts += 'return=representation' } if ($PreferODataMaxPageSize) { $preferParts += "odata.maxpagesize=$PreferODataMaxPageSize" } if ($PreferIncludeAnnotations) { $preferParts += "odata.include-annotations=$PreferIncludeAnnotations" } if ($preferParts.Count -gt 0) { # If ExtraHeaders already contains Prefer, append parts after a comma unless identical $existingPrefer = $null if ($ExtraHeaders -and $ExtraHeaders.ContainsKey('Prefer')) { $existingPrefer = [string]$ExtraHeaders['Prefer'] } if ($existingPrefer) { $toAdd = ($preferParts | Where-Object { $existingPrefer -notmatch [regex]::Escape($_) }) if ($toAdd.Count -gt 0) { $headers['Prefer'] = ($existingPrefer.TrimEnd(',') + ',' + ($toAdd -join ',')).Trim(',') } else { $headers['Prefer'] = $existingPrefer } } else { $headers['Prefer'] = ($preferParts -join ',') } $preferComposed = $true } if ($ConsistencyLevel) { $headers['ConsistencyLevel'] = $ConsistencyLevel } if ($IfMatch) { $headers['If-Match'] = $IfMatch } if ($IfNoneMatch) { $headers['If-None-Match'] = $IfNoneMatch } if ($CorrelationId) { $headers['x-ms-client-request-id'] = $CorrelationId } if ($SuppressDuplicateDetection.IsPresent) { $headers['MSCRM.SuppressDuplicateDetection'] = 'true' } if ($AcceptLanguage) { $headers['Accept-Language'] = $AcceptLanguage } # Merge extra headers last to allow explicit override by caller # Special-case: if we already composed a Prefer header, we skip overwriting it so the # merged value is retained. Callers that need full control can pass all parts in ExtraHeaders.Prefer. if ($ExtraHeaders) { foreach ($k in $ExtraHeaders.Keys) { if ($k -eq 'Prefer' -and $preferComposed) { continue } $headers[$k] = $ExtraHeaders[$k] } } return $headers } |