Openstatus www.openstatus.dev
at main 164 lines 4.8 kB view raw
1import { Code, ConnectError } from "@connectrpc/connect"; 2import { monitorPeriodicity } from "@openstatus/db/src/schema/constants"; 3import { monitorMethods } from "@openstatus/db/src/schema/monitors/constants"; 4import type { Periodicity, Region } from "@openstatus/proto/monitor/v1"; 5 6import { 7 MONITOR_DEFAULTS, 8 openTelemetryToDb, 9 periodicityToString, 10 regionsToDbString, 11 regionsToStrings, 12 validateRegions, 13} from "./converters"; 14 15type MonitorPeriodicity = (typeof monitorPeriodicity)[number]; 16type MonitorMethod = (typeof monitorMethods)[number]; 17 18/** 19 * Validate and convert periodicity string to enum type. 20 */ 21export function toValidPeriodicity( 22 value: string | undefined, 23): MonitorPeriodicity { 24 const valid = monitorPeriodicity as readonly string[]; 25 if (value && valid.includes(value)) { 26 return value as MonitorPeriodicity; 27 } 28 return "1m"; 29} 30 31/** 32 * Validate and convert method string to enum type. 33 */ 34export function toValidMethod(value: string | undefined): MonitorMethod { 35 const upper = value?.toUpperCase(); 36 const valid = monitorMethods as readonly string[]; 37 if (upper && valid.includes(upper)) { 38 return upper as MonitorMethod; 39 } 40 return "GET"; 41} 42 43/** 44 * Validate required monitor fields common to all monitor types. 45 * Note: name, url/uri, and periodicity are validated by protovalidate interceptor. 46 * Throws ConnectError if validation fails. 47 */ 48export function validateCommonMonitorFields(mon: { 49 regions?: Region[]; 50}): void { 51 if (mon.regions && mon.regions.length > 0) { 52 const regionStrings = regionsToStrings(mon.regions); 53 const invalidRegions = validateRegions(regionStrings); 54 if (invalidRegions.length > 0) { 55 throw new ConnectError( 56 `Invalid regions: ${invalidRegions.join(", ")}`, 57 Code.InvalidArgument, 58 ); 59 } 60 } 61} 62 63/** 64 * Extract common database values for all monitor types. 65 */ 66export function getCommonDbValues(mon: { 67 name: string; 68 periodicity?: Periodicity; 69 timeout?: bigint; 70 degradedAt?: bigint; 71 active?: boolean; 72 description?: string; 73 public?: boolean; 74 regions?: Region[]; 75 retry?: bigint; 76 openTelemetry?: Parameters<typeof openTelemetryToDb>[0]; 77}) { 78 const otelConfig = openTelemetryToDb(mon.openTelemetry); 79 80 const periodicityStr = mon.periodicity 81 ? periodicityToString(mon.periodicity) 82 : undefined; 83 const regionStrings = mon.regions ? regionsToStrings(mon.regions) : []; 84 85 return { 86 name: mon.name, 87 periodicity: toValidPeriodicity(periodicityStr), 88 timeout: mon.timeout ? Number(mon.timeout) : MONITOR_DEFAULTS.timeout, 89 degradedAfter: mon.degradedAt ? Number(mon.degradedAt) : undefined, 90 active: mon.active ?? MONITOR_DEFAULTS.active, 91 description: mon.description || MONITOR_DEFAULTS.description, 92 public: mon.public ?? MONITOR_DEFAULTS.public, 93 regions: regionsToDbString(regionStrings), 94 retry: mon.retry ? Number(mon.retry) : MONITOR_DEFAULTS.retry, 95 otelEndpoint: otelConfig.otelEndpoint, 96 otelHeaders: otelConfig.otelHeaders, 97 }; 98} 99 100/** 101 * Extract common database values for update operations. 102 * Only includes fields that are explicitly provided (not undefined). 103 * This enables partial updates where only specified fields are changed. 104 */ 105export function getCommonDbValuesForUpdate(mon: { 106 name?: string; 107 periodicity?: Periodicity; 108 timeout?: bigint; 109 degradedAt?: bigint; 110 active?: boolean; 111 description?: string; 112 public?: boolean; 113 regions?: Region[]; 114 retry?: bigint; 115 openTelemetry?: Parameters<typeof openTelemetryToDb>[0]; 116}) { 117 const result: Record<string, unknown> = {}; 118 119 if (mon.name !== undefined && mon.name !== "") { 120 result.name = mon.name; 121 } 122 123 if (mon.periodicity !== undefined && mon.periodicity !== 0) { 124 const periodicityStr = periodicityToString(mon.periodicity); 125 result.periodicity = toValidPeriodicity(periodicityStr); 126 } 127 128 if (mon.timeout !== undefined && mon.timeout !== BigInt(0)) { 129 result.timeout = Number(mon.timeout); 130 } 131 132 if (mon.degradedAt !== undefined) { 133 result.degradedAfter = Number(mon.degradedAt); 134 } 135 136 if (mon.active !== undefined) { 137 result.active = mon.active; 138 } 139 140 if (mon.description !== undefined) { 141 result.description = mon.description; 142 } 143 144 if (mon.public !== undefined) { 145 result.public = mon.public; 146 } 147 148 if (mon.regions !== undefined && mon.regions.length > 0) { 149 const regionStrings = regionsToStrings(mon.regions); 150 result.regions = regionsToDbString(regionStrings); 151 } 152 153 if (mon.retry !== undefined && mon.retry !== BigInt(0)) { 154 result.retry = Number(mon.retry); 155 } 156 157 if (mon.openTelemetry !== undefined) { 158 const otelConfig = openTelemetryToDb(mon.openTelemetry); 159 result.otelEndpoint = otelConfig.otelEndpoint; 160 result.otelHeaders = otelConfig.otelHeaders; 161 } 162 163 return result; 164}