dutybeat.psm1
|
# DutyBeat public API — PowerShell client. # Works on Windows PowerShell 5.1 and PowerShell 7+ (Core). No external dependencies. $script:DefaultBaseUri = 'https://api.dutybeat.com' # Session state set by Connect-DutyBeat (per module load). $script:DutyBeatContext = @{ ApiKey = $null; BaseUri = $script:DefaultBaseUri } function Connect-DutyBeat { <# .SYNOPSIS Stores the API key (and optional base URL) for subsequent calls in this session. .DESCRIPTION Saves your DutyBeat API key for the current PowerShell session so you don't have to pass it to every command. Alternatively, set the DUTYBEAT_API_KEY environment variable and skip this call. .PARAMETER ApiKey Your API key (starts with db_live_). Create one in the app under Configuración → Claves de API. If omitted, the DUTYBEAT_API_KEY environment variable is used. .PARAMETER BaseUri Base URL of the API. Defaults to the production API; override only for testing. .EXAMPLE Connect-DutyBeat -ApiKey 'db_live_...' .LINK https://dutybeat.com/manuales/api #> [CmdletBinding()] param( [string]$ApiKey, [string]$BaseUri = $script:DefaultBaseUri ) $key = if ($ApiKey) { $ApiKey } elseif ($env:DUTYBEAT_API_KEY) { $env:DUTYBEAT_API_KEY } else { $null } if (-not $key) { throw "Provide -ApiKey or set the DUTYBEAT_API_KEY environment variable." } $script:DutyBeatContext = @{ ApiKey = $key; BaseUri = $BaseUri.TrimEnd('/') } Write-Verbose "Connected to $($script:DutyBeatContext.BaseUri)" } function Invoke-DbApi { # Private transport: resolves the key/base URL, builds the URI, retries on 429/5xx honouring # Retry-After, and maps the API error envelope to a terminating error. Not exported. [CmdletBinding()] param( [string]$Method = 'GET', [Parameter(Mandatory)][string]$Path, [hashtable]$Query, [string]$ApiKey, [string]$BaseUri, [int]$MaxRetries = 2 ) $key = if ($ApiKey) { $ApiKey } elseif ($script:DutyBeatContext.ApiKey) { $script:DutyBeatContext.ApiKey } elseif ($env:DUTYBEAT_API_KEY) { $env:DUTYBEAT_API_KEY } else { $null } if (-not $key) { throw "No API key. Run Connect-DutyBeat -ApiKey '...' or set DUTYBEAT_API_KEY." } $base = if ($BaseUri) { $BaseUri.TrimEnd('/') } elseif ($script:DutyBeatContext.BaseUri) { $script:DutyBeatContext.BaseUri } else { $script:DefaultBaseUri } $uri = "$base$Path" if ($Query -and $Query.Count -gt 0) { $pairs = foreach ($k in $Query.Keys) { "$k=$([uri]::EscapeDataString([string]$Query[$k]))" } $uri += '?' + ($pairs -join '&') } $headers = @{ Authorization = "Bearer $key"; Accept = 'application/json' } for ($attempt = 0; ; $attempt++) { try { return Invoke-RestMethod -Method $Method -Uri $uri -Headers $headers -ErrorAction Stop } catch { $status = $null try { $status = [int]$_.Exception.Response.StatusCode } catch {} if (($status -in 429, 500, 502, 503, 504) -and $attempt -lt $MaxRetries) { $delay = [Math]::Min([Math]::Pow(2, $attempt), 8) try { $ra = $_.Exception.Response.Headers['Retry-After'] if ($ra) { $delay = [double]$ra } } catch {} Start-Sleep -Seconds $delay continue } $code = $null $message = $_.Exception.Message if ($_.ErrorDetails -and $_.ErrorDetails.Message) { try { $body = $_.ErrorDetails.Message | ConvertFrom-Json if ($body.error) { $code = $body.error.code; $message = $body.error.message } } catch {} } $prefix = 'DutyBeat API error' if ($status) { $prefix += " $status" } if ($code) { $prefix += " ($code)" } throw "${prefix}: $message" } } } function Get-DbUser { <# .SYNOPSIS Gets a user's full record (GET /api/v1/users/:id). .DESCRIPTION Returns the account and profile data for one user — the same fields shown in "Mi Perfil" in the app (department, work center, DNI, IBAN, SSN, and more). Requires a key with the users.get method enabled. A user from another company (or a non-existent id) raises a not-found error. .PARAMETER UserId The user's id within your company. .PARAMETER IncludeFolders Also return the employee's document folders (metadata only: id, name and document count). .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS PSCustomObject with the user's fields: id, full_name, email, role, status, department, work_center, profile (dni, iban, ssn, ...) and, if requested, folders. .EXAMPLE Get-DbUser -UserId '9f1c...' -IncludeFolders .EXAMPLE Connect-DutyBeat -ApiKey 'db_live_...' (Get-DbUser -UserId '9f1c...').profile.iban .LINK https://dutybeat.com/manuales/api/leer-usuario #> [CmdletBinding()] param( [Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName)][string]$UserId, [switch]$IncludeFolders, [string]$ApiKey, [string]$BaseUri ) process { $query = @{} if ($IncludeFolders) { $query['include_folders'] = 'true' } $response = Invoke-DbApi -Method 'GET' -Path "/api/v1/users/$UserId" -Query $query -ApiKey $ApiKey -BaseUri $BaseUri $response.data } } Export-ModuleMember -Function 'Connect-DutyBeat', 'Get-DbUser' |