Public/Import-PagerArchiveAlias.ps1
|
function Import-PagerArchiveAlias { <# .SYNOPSIS Imports aliases from the pipeline, as one batch. .DESCRIPTION Takes alias rows from the pipeline, usually from Import-Csv, and applies them together (R27a, R30, R58, R63): - New capcodes and patterns are added. One template is created for each new agency, with the profile -AgencyProfile maps it to. - Existing aliases are left as they are, and each difference is reported as a "Different" result. With -Update, differences are applied ("Updated"), and recorded in each alias's history as yours. - Rows for the same capcode that are identical are squashed into the first ("Squashed"). Rows for the same capcode that differ are a conflict: nothing is imported, and each row involved is reported as an error, so you can leave out or fix rows in the pipeline (for example with Where-Object) and import again. - A row without a valid capcode or pattern, or without a name, is reported as an error and skipped. It asks once for the whole batch, with a summary such as "412 aliases: 380 new, 12 different". -WhatIf is the dry run: nothing changes, not even new capcodes, and every row's result is still output, with Applied $false, so you can filter the results before importing for real. The CSV's columns bind by name: address, alias, agency, color and ignore. Other columns (an icon name, an export's id) are never read. .EXAMPLE Import-Csv -Path aliases.csv | Import-PagerArchiveAlias -AgencyProfile @{ 'Example Fire' = 'example-fire' } Index Result Pattern Name Changes Applied ----- ------ ------- ---- ------- ------- 1 Added 0300001 Testville Brigade True 2 Added 0300002 Testville Tanker True .EXAMPLE Import-Csv -Path aliases.csv | Import-PagerArchiveAlias -WhatIf | Where-Object -Property Result -EQ 'Different' Shows how existing aliases differ from the file, changing nothing. .EXAMPLE Import-Csv -Path aliases.csv | Import-PagerArchiveAlias -AgencyProfile (Get-Content -Path profiles.json -Raw | ConvertFrom-Json -AsHashtable) Loads the agency-to-profile mapping from a JSON file the deployment keeps. .EXAMPLE Import-Csv -Path aliases.csv | Import-PagerArchiveAlias -Update Also applies the differences to existing aliases, after asking. .INPUTS System.Management.Automation.PSObject. Rows with Pattern (or address), Name (or alias), Template (or agency), Colour (or color) and Ignored (or ignore) properties, such as Import-Csv's rows or PagerArchive.Alias objects. .OUTPUTS PagerArchive.ImportResult. One per row: Index (the row's position in the pipeline, from 1), Result (Added, Unchanged, Different, Updated or Squashed), Pattern, Name, Changes (each difference, e.g. name: "Brigade" -> "Brigade North"), SquashedInto (the index of the row a squashed row matched) and Applied ($false under -WhatIf or when the batch was declined). .LINK Get-PagerArchiveAlias .LINK New-PagerArchiveAlias .LINK about_PagerArchive #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] [OutputType('PagerArchive.ImportResult')] param( # The capcode (1 to 7 digits) or pattern (with % and _). Binds the # CSV's address column. No wildcards. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [Alias('Address')] [AllowEmptyString()] [string] $Pattern, # The alias's name. Binds the CSV's alias column. No wildcards. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [Alias('Alias')] [AllowEmptyString()] [string] $Name, # The template's name, also the agency's for a new template. Binds # the CSV's agency column. Default: none (text only). [Parameter(ValueFromPipelineByPropertyName)] [Alias('Agency')] [AllowEmptyString()] [AllowNull()] [string] $Template, # The colour for a new template. Binds the CSV's color column. # Default: none. [Parameter(ValueFromPipelineByPropertyName)] [Alias('Color')] [AllowEmptyString()] [AllowNull()] [string] $Colour, # Hide the alias's pages from the default message list. Binds the # CSV's ignore column: true, false, 1, 0, yes, no or empty. Off by # default. [Parameter(ValueFromPipelineByPropertyName)] [Alias('Ignore')] [PagerArchiveBoolTransformation()] [switch] $Ignored, # Which agency profile each new agency's template gets, as # @{ 'Agency name' = 'profile-name' }. Agencies not listed are text # only. An unknown profile name refuses the whole import. Default: # none. [PSDefaultValue(Help = 'none')] [hashtable] $AgencyProfile = @{}, # Apply differences to existing aliases (R58). Asks for # confirmation unless -Confirm:$false is given. Off by default. [switch] $Update ) begin { # -Update counts as ConfirmImpact High (guidelines section 4): ask # by default, as for High, unless -Confirm was given. if ($Update -and -not $PSBoundParameters.ContainsKey('Confirm') -and $ConfirmPreference -eq 'High') { $ConfirmPreference = 'Medium' } $rows = [System.Collections.Generic.List[object]]::new() $inputs = [System.Collections.Generic.List[object]]::new() } process { $rows.Add(@{ pattern = $Pattern; name = $Name; template = [string]$Template; colour = [string]$Colour; ignored = [bool]$Ignored }) $inputs.Add($_) } end { if ($rows.Count -eq 0) { return } $profiles = @{} foreach ($key in $AgencyProfile.Keys) { $profiles[[string]$key] = [string]$AgencyProfile[$key] } $request = @{ rows = $rows.ToArray(); agency_profiles = $profiles; update = [bool]$Update; dry_run = $true; plan = '' } try { Write-Progress -Activity 'Importing aliases' -Status "Checking $($rows.Count) rows" $report = Invoke-PaApi -Method POST -Path '/api/v1/admin/aliases/import' -Body $request -Accept 409 if ($report.PSObject.Properties['code']) { # A conflict: nothing was applied, and each row involved is # reported (guidelines section 7). foreach ($conflict in $report.report.conflicts) { foreach ($index in $conflict.rows) { $row = $rows[$index - 1] $others = ($conflict.rows | Where-Object -FilterScript { $_ -ne $index }) -join ', ' $PSCmdlet.WriteError((New-PaErrorRecord -ErrorId 'import_conflict' -Status 409 -TargetObject $inputs[$index - 1] ` -Message "Row $index ($($conflict.pattern), '$($row.name)') differs from row(s) $others for the same capcode, so nothing was imported. Leave out or fix the rows, and import again.")) } } return } $summary = Get-PaImportSummary -Report $report $action = if ($Update) { 'Import aliases and update existing ones' } else { 'Import aliases' } $applied = $false $changes = @($report.rows | Where-Object -FilterScript { $_.result -in @('added', 'updated') }).Count if ($PSCmdlet.ShouldProcess($summary, $action)) { if ($changes -gt 0 -or @($report.templates_created).Count -gt 0) { Write-Progress -Activity 'Importing aliases' -Status "Applying $($rows.Count) rows" $request.dry_run = $false $request.plan = $report.plan $report = Invoke-PaApi -Method POST -Path '/api/v1/admin/aliases/import' -Body $request -Accept 409 if ($report.PSObject.Properties['code']) { throw (New-PaErrorRecord -ErrorId $report.code -Status 409 -Message $report.error) } } $applied = $true } Write-Progress -Activity 'Importing aliases' -Completed foreach ($repeated in $report.repeated_names) { Write-Verbose -Message "$(@($repeated.patterns).Count) aliases share one name: $($repeated.patterns -join ', ')" } foreach ($result in $report.rows) { if ($result.result -eq 'invalid') { $PSCmdlet.WriteError((New-PaErrorRecord -ErrorId 'invalid_row' -Category InvalidData -TargetObject $(if ($result.index -le $inputs.Count) { $inputs[$result.index - 1] }) ` -Message "Row $($result.index) was skipped: $($result.problem)")) continue } [pscustomobject]@{ PSTypeName = 'PagerArchive.ImportResult' Index = [int]$result.index Result = (Get-Culture).TextInfo.ToTitleCase($result.result) Pattern = $result.pattern Name = $result.name Changes = @($result.PSObject.Properties['changes'] ? $result.changes : @()) SquashedInto = if ($result.PSObject.Properties['squashed_into']) { [int]$result.squashed_into } else { $null } Applied = $applied } } } catch { Write-Progress -Activity 'Importing aliases' -Completed $PSCmdlet.ThrowTerminatingError((Copy-PaErrorRecord -ErrorRecord $_)) } } } |