|
| 1 | +import {Command, Flags} from '@oclif/core' |
| 2 | +import type {ZodType} from 'zod' |
| 3 | +import {globalFlags, buildClient, display} from '../../lib/base-command.js' |
| 4 | +import {apiGetPage} from '../../lib/api-client.js' |
| 5 | +import {schemas as apiSchemas} from '../../lib/api-zod.generated.js' |
| 6 | +import type {components} from '../../lib/api.generated.js' |
| 7 | +import {uuidFlag} from '../../lib/validators.js' |
| 8 | + |
| 9 | +type MaintenanceWindowDto = components['schemas']['MaintenanceWindowDto'] |
| 10 | + |
| 11 | +// API-supported filter values for `GET /api/v1/maintenance-windows?filter=...`. |
| 12 | +// The server understands `active` (in-progress now) and `upcoming` (future). |
| 13 | +// Past windows are not server-filterable today — pass no filter and inspect |
| 14 | +// the timestamps locally if you need them. |
| 15 | +const STATUS_OPTIONS = ['active', 'upcoming'] as const |
| 16 | + |
| 17 | +export default class MaintenanceWindowsList extends Command { |
| 18 | + static description = 'List all maintenance windows' |
| 19 | + static examples = [ |
| 20 | + '<%= config.bin %> maintenance-windows list', |
| 21 | + '<%= config.bin %> maintenance-windows list --status active', |
| 22 | + '<%= config.bin %> maintenance-windows list --monitor <uuid>', |
| 23 | + ] |
| 24 | + static flags = { |
| 25 | + ...globalFlags, |
| 26 | + 'page-size': Flags.integer({description: 'Number of items per API request (1–200)', default: 200}), |
| 27 | + status: Flags.string({ |
| 28 | + description: 'Filter by lifecycle state (server-supported: active, upcoming)', |
| 29 | + options: [...STATUS_OPTIONS], |
| 30 | + }), |
| 31 | + monitor: uuidFlag({description: 'Only show windows attached to this monitor ID'}), |
| 32 | + } |
| 33 | + |
| 34 | + async run() { |
| 35 | + const {flags} = await this.parse(MaintenanceWindowsList) |
| 36 | + const client = buildClient(flags) |
| 37 | + const schema = apiSchemas.MaintenanceWindowDto as ZodType<MaintenanceWindowDto> |
| 38 | + |
| 39 | + // Roll our own pagination loop because the shared |
| 40 | + // `fetchPaginatedValidated` helper accepts only page/size — this |
| 41 | + // endpoint also takes `monitorId` and `filter` query params, and |
| 42 | + // appending them to the path string would race against openapi-fetch's |
| 43 | + // own query serialiser. |
| 44 | + const items: MaintenanceWindowDto[] = [] |
| 45 | + let page = 0 |
| 46 | + while (true) { |
| 47 | + const resp = await apiGetPage<MaintenanceWindowDto>( |
| 48 | + client, |
| 49 | + '/api/v1/maintenance-windows', |
| 50 | + schema, |
| 51 | + { |
| 52 | + query: { |
| 53 | + page, |
| 54 | + size: flags['page-size'], |
| 55 | + ...(flags.status ? {filter: flags.status} : {}), |
| 56 | + ...(flags.monitor ? {monitorId: flags.monitor} : {}), |
| 57 | + }, |
| 58 | + }, |
| 59 | + ) |
| 60 | + items.push(...resp.data) |
| 61 | + if (!resp.hasNext) break |
| 62 | + page++ |
| 63 | + } |
| 64 | + |
| 65 | + display(this, items, flags.output, [ |
| 66 | + {header: 'ID', get: (r: MaintenanceWindowDto) => r.id ?? ''}, |
| 67 | + {header: 'MONITOR', get: (r: MaintenanceWindowDto) => r.monitorId ?? '(org-wide)'}, |
| 68 | + {header: 'STARTS', get: (r: MaintenanceWindowDto) => r.startsAt ?? ''}, |
| 69 | + {header: 'ENDS', get: (r: MaintenanceWindowDto) => r.endsAt ?? ''}, |
| 70 | + {header: 'STATUS', get: (r: MaintenanceWindowDto) => computeStatus(r)}, |
| 71 | + {header: 'SUPPRESS', get: (r: MaintenanceWindowDto) => String(r.suppressAlerts ?? '')}, |
| 72 | + {header: 'REASON', get: (r: MaintenanceWindowDto) => r.reason ?? ''}, |
| 73 | + ]) |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +// Best-effort lifecycle label derived from the timestamps. The server only |
| 78 | +// distinguishes `active` and `upcoming`; we surface `past` so the table |
| 79 | +// is still readable when no filter was applied. |
| 80 | +function computeStatus(window: MaintenanceWindowDto): string { |
| 81 | + const now = Date.now() |
| 82 | + const start = Date.parse(window.startsAt ?? '') |
| 83 | + const end = Date.parse(window.endsAt ?? '') |
| 84 | + if (Number.isFinite(end) && end < now) return 'past' |
| 85 | + if (Number.isFinite(start) && start > now) return 'upcoming' |
| 86 | + return 'active' |
| 87 | +} |
0 commit comments