Converted to PowerShell
This commit is contained in:
@@ -0,0 +1,87 @@
|
|||||||
|
<#
|
||||||
|
Minimal client for the Polaris Leap (PAPI) Notices endpoint.
|
||||||
|
PowerShell port of ../polaris_client.py -- see that file's docstrings and
|
||||||
|
../README.md ("Notes on the API") for the non-obvious quirks this encodes.
|
||||||
|
#>
|
||||||
|
|
||||||
|
class PolarisClient {
|
||||||
|
[string]$AuthUrl
|
||||||
|
[string]$BaseUrl
|
||||||
|
[string]$SiteDomain
|
||||||
|
[string]$AccessToken
|
||||||
|
[string]$AccessSecret
|
||||||
|
|
||||||
|
PolarisClient(
|
||||||
|
[string]$Server,
|
||||||
|
[string]$SiteDomain,
|
||||||
|
[string]$OrganizationId,
|
||||||
|
[string]$WorkstationId,
|
||||||
|
[string]$Language,
|
||||||
|
[string]$ProductId
|
||||||
|
) {
|
||||||
|
# authentication/staffuser is the one route that omits siteDomain/orgId/workstationId --
|
||||||
|
# those describe a *logged-on* session, and you're not logged on yet at this point.
|
||||||
|
$this.AuthUrl = "https://$Server/Polaris.ApplicationServices/api/v1/$Language/$ProductId/authentication/staffuser"
|
||||||
|
$this.BaseUrl = "https://$Server/Polaris.ApplicationServices/api/v1/$Language/$ProductId/$SiteDomain/$OrganizationId/$WorkstationId"
|
||||||
|
$this.SiteDomain = $SiteDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
# Exchange a staff username/password for an AccessToken/AccessSecret.
|
||||||
|
# Username must include the domain, e.g. MYLIB\Aladdin.
|
||||||
|
[void] Authenticate([string]$Username, [string]$Password) {
|
||||||
|
$pair = "${Username}:${Password}"
|
||||||
|
$basicAuth = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($pair))
|
||||||
|
$headers = @{
|
||||||
|
Authorization = "Basic $basicAuth"
|
||||||
|
Accept = "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = Invoke-RestMethod -Uri $this.AuthUrl -Method Post -Headers $headers
|
||||||
|
} catch {
|
||||||
|
$statusCode = $null
|
||||||
|
if ($_.Exception.Response) { $statusCode = [int]$_.Exception.Response.StatusCode }
|
||||||
|
if ($statusCode -eq 401) {
|
||||||
|
throw "Authentication failed: check username/password."
|
||||||
|
}
|
||||||
|
throw "Authentication failed ($statusCode): $($_.ErrorDetails.Message)"
|
||||||
|
}
|
||||||
|
|
||||||
|
$this.AccessToken = $response.AccessToken
|
||||||
|
$this.AccessSecret = $response.AccessSecret
|
||||||
|
}
|
||||||
|
|
||||||
|
hidden [string] AuthHeader() {
|
||||||
|
if (-not $this.AccessToken -or -not $this.AccessSecret) {
|
||||||
|
throw "Not authenticated yet -- call Authenticate() first."
|
||||||
|
}
|
||||||
|
return "PAS $($this.SiteDomain):$($this.AccessToken):$($this.AccessSecret)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# POST notices for the given notice type to the given org IDs.
|
||||||
|
# DeliveryOption: only 1 (Mail) is currently supported by the API.
|
||||||
|
[bool] PostNotices([int]$NoticeType, [int[]]$OrganizationIds, [int]$DeliveryOption) {
|
||||||
|
$orgIdsCsv = ($OrganizationIds -join ",")
|
||||||
|
$uri = "$($this.BaseUrl)/notices/post?noticeType=$NoticeType&deliveryOption=$DeliveryOption"
|
||||||
|
$headers = @{
|
||||||
|
Authorization = $this.AuthHeader()
|
||||||
|
Accept = "application/json"
|
||||||
|
}
|
||||||
|
# Polaris expects the org-id CSV as a JSON-encoded string literal, matching
|
||||||
|
# json.dumps(org_ids_csv) on the Python side -- not a raw/unquoted string.
|
||||||
|
$bodyJson = $orgIdsCsv | ConvertTo-Json
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -ContentType "application/json" -Body $bodyJson
|
||||||
|
} catch {
|
||||||
|
$statusCode = $null
|
||||||
|
if ($_.Exception.Response) { $statusCode = [int]$_.Exception.Response.StatusCode }
|
||||||
|
throw "Notice post failed ($statusCode): $($_.ErrorDetails.Message)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($result -ne $true) {
|
||||||
|
throw "Polaris reported failure (returned $result) for noticeType=$NoticeType, organizationIds=$orgIdsCsv"
|
||||||
|
}
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# PowerShell version
|
||||||
|
|
||||||
|
Same functionality as the Python scripts in the repo root, ported for running on Windows
|
||||||
|
Servers via Task Scheduler without needing a Python install.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
No dependencies to install — `Invoke-RestMethod` is built into Windows PowerShell 5.1+.
|
||||||
|
|
||||||
|
Uses the same `.env` file as the Python version (see `../.env.example`); by default it looks
|
||||||
|
for `..\.env` relative to this script. Pass `-EnvFile` to point elsewhere, e.g. if you'd rather
|
||||||
|
keep credentials outside the repo checkout entirely.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\post_notices.ps1 -NoticeType 2 -OrgIds 3,4,5,6,7,8
|
||||||
|
```
|
||||||
|
|
||||||
|
- `-NoticeType`: the Notification Type ID to post.
|
||||||
|
- `-OrgIds`: comma-separated Organization IDs to post notices for.
|
||||||
|
- `-DeliveryOption`: optional, defaults to `1` (Mail) — currently the only value Polaris accepts.
|
||||||
|
|
||||||
|
Same caveat as the Python version: every run sends real notices to real patrons, and there's no
|
||||||
|
dry-run mode.
|
||||||
|
|
||||||
|
## Task Scheduler
|
||||||
|
|
||||||
|
Action: `powershell.exe`
|
||||||
|
Arguments: `-NoProfile -ExecutionPolicy Bypass -File "C:\path\to\PowerShell\post_notices.ps1" -NoticeType 2 -OrgIds 3,4,5,6,7,8`
|
||||||
|
|
||||||
|
If your Windows PowerShell execution policy blocks unsigned scripts by default, `-ExecutionPolicy
|
||||||
|
Bypass` on the scheduled task action avoids having to change the machine-wide policy.
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Posts notices to Polaris via the Leap (PAPI) API. PowerShell port of ../post_notices.py.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\post_notices.ps1 -NoticeType 2 -OrgIds 3,4,5,6,7,8
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[int]$NoticeType,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$OrgIds,
|
||||||
|
|
||||||
|
[int]$DeliveryOption = 1,
|
||||||
|
|
||||||
|
# Defaults to a .env file next to the repo root (one level up from this script),
|
||||||
|
# matching where the Python version expects it.
|
||||||
|
[string]$EnvFile = (Join-Path $PSScriptRoot "..\.env")
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
. (Join-Path $PSScriptRoot "PolarisClient.ps1")
|
||||||
|
|
||||||
|
function Import-DotEnv {
|
||||||
|
param([string]$Path)
|
||||||
|
if (-not (Test-Path -LiteralPath $Path)) { return }
|
||||||
|
|
||||||
|
foreach ($line in Get-Content -LiteralPath $Path) {
|
||||||
|
$trimmed = $line.Trim()
|
||||||
|
if (-not $trimmed -or $trimmed.StartsWith('#')) { continue }
|
||||||
|
|
||||||
|
$idx = $trimmed.IndexOf('=')
|
||||||
|
if ($idx -lt 1) { continue }
|
||||||
|
|
||||||
|
$key = $trimmed.Substring(0, $idx).Trim()
|
||||||
|
$value = $trimmed.Substring($idx + 1).Trim()
|
||||||
|
if ($value.Length -ge 2 -and (
|
||||||
|
($value[0] -eq '"' -and $value[-1] -eq '"') -or
|
||||||
|
($value[0] -eq "'" -and $value[-1] -eq "'")
|
||||||
|
)) {
|
||||||
|
$value = $value.Substring(1, $value.Length - 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Don't clobber a value already set in the environment (e.g. via Task Scheduler).
|
||||||
|
if (-not (Get-Item -Path "Env:$key" -ErrorAction SilentlyContinue).Value) {
|
||||||
|
Set-Item -Path "Env:$key" -Value $value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Import-DotEnv -Path $EnvFile
|
||||||
|
|
||||||
|
$requiredEnv = @(
|
||||||
|
"POLARIS_SERVER",
|
||||||
|
"POLARIS_SITE_DOMAIN",
|
||||||
|
"POLARIS_ORGANIZATION_ID",
|
||||||
|
"POLARIS_WORKSTATION_ID",
|
||||||
|
"POLARIS_USERNAME",
|
||||||
|
"POLARIS_PASSWORD"
|
||||||
|
)
|
||||||
|
$missing = $requiredEnv | Where-Object { [string]::IsNullOrWhiteSpace((Get-Item -Path "Env:$_" -ErrorAction SilentlyContinue).Value) }
|
||||||
|
if ($missing) {
|
||||||
|
Write-Error "Missing required .env values: $($missing -join ', ')"
|
||||||
|
Write-Error "Copy .env.example to .env and fill it in (or set these as environment variables)."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$orgIdList = $OrgIds -split "," | Where-Object { $_.Trim() } | ForEach-Object { [int]$_.Trim() }
|
||||||
|
|
||||||
|
$language = if ($env:POLARIS_LANGUAGE) { $env:POLARIS_LANGUAGE } else { "eng" }
|
||||||
|
$productId = if ($env:POLARIS_PRODUCT_ID) { $env:POLARIS_PRODUCT_ID } else { "7" }
|
||||||
|
|
||||||
|
$client = [PolarisClient]::new(
|
||||||
|
$env:POLARIS_SERVER,
|
||||||
|
$env:POLARIS_SITE_DOMAIN,
|
||||||
|
$env:POLARIS_ORGANIZATION_ID,
|
||||||
|
$env:POLARIS_WORKSTATION_ID,
|
||||||
|
$language,
|
||||||
|
$productId
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$client.Authenticate($env:POLARIS_USERNAME, $env:POLARIS_PASSWORD)
|
||||||
|
$client.PostNotices($NoticeType, $orgIdList, $DeliveryOption) | Out-Null
|
||||||
|
} catch {
|
||||||
|
Write-Error "Error: $($_.Exception.Message)"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "Notices posted successfully: noticeType=$NoticeType, orgIds=$($orgIdList -join ','), deliveryOption=$DeliveryOption"
|
||||||
Reference in New Issue
Block a user