namespace _heimdall.order { /** */ function schema_active( ) : _heimdall.helpers.json_schema.type_schema { return { "description": "whether the check shall be executed", "type": "boolean", "default": true, }; } /** */ function schema_threshold( ) : _heimdall.helpers.json_schema.type_schema { return { "description": "how often a condition has to occur in order to be reported", "type": "integer", "minimum": 1, "default": 3, }; } /** */ function schema_annoy( ) : _heimdall.helpers.json_schema.type_schema { return { "description": "whether notifications about non-ok states shall be kept sending after the threshold has been surpassed", "type": "boolean", "default": false, }; } /** */ function schema_interval( allow_null : boolean, default_ ) : _heimdall.helpers.json_schema.type_schema { return { "anyOf": ( [] .concat( allow_null ? [ { "type": "null", } ] : [] ) .concat( [ { "description": "in seconds", "type": "integer", "exclusiveMinimum": 0, }, { "description": "as text", "type": "string", "enum": [ "minute", "hour", "day", "week", ] }, ] ) ), "default": default_, }; } /** */ function schema_schedule( ) : _heimdall.helpers.json_schema.type_schema { return { "type": "object", "additionalProperties": false, "properties": { "regular_interval": schema_interval(false, (60 * 60)), "attentive_interval": schema_interval(false, (60 * 2)), "reminding_interval": schema_interval(true, (60 * 60 * 24)), }, "required": [ ], }; } /** */ function schema_notifications( ) : _heimdall.helpers.json_schema.type_schema { const notification_kind_implementations = _heimdall.notification_kinds.get_implementations(); return { "type": "array", "items": { "anyOf": ( Object.entries(notification_kind_implementations) .map( ([key, value]) => ({ "title": ("notification channel '" + key + "'"), "type": "object", "unevaluatedProperties": false, "properties": { "kind": { "type": "string", "enum": [key] }, "parameters": value.parameters_schema(), }, "required": [ "kind", "parameters" ], }) ) ) }, "default": [ { "kind": "console", "parameters": { } }, ], }; } /** */ export function schema_root( ) : _heimdall.helpers.json_schema.type_schema { const check_kind_implementations = _heimdall.check_kinds.get_implementations(); return { "type": "object", "additionalProperties": false, "properties": { "defaults": { "description": "default values for checks", "type": "object", "additionalProperties": false, "properties": { "active": schema_active(), "threshold": schema_threshold(), "annoy": schema_annoy(), "schedule": schema_schedule(), "notifications": schema_notifications(), }, "required": [ ], }, "includes": { "type": "array", "items": { "type": "string" }, "default": [], "description": "list of relative or absolute paths to other hmdl files on the local machine, which shall be subsumed in the overall monitoring task" }, "checks": { "type": "array", "items": { "allOf": [ { "description": "should represent a specific check", "type": "object", "unevaluatedProperties": false, "properties": { "name": { "type": "string" }, "title": { "type": "string" }, "active": schema_active(), "threshold": schema_threshold(), "annoy": schema_annoy(), "schedule": schema_schedule(), "notifications": schema_notifications(), }, "required": [ "name", ], }, { "anyOf": ( Object.entries(check_kind_implementations) .map( ([key, value]) => ({ "title": ("check '" + key + "'"), "type": "object", "unevaluatedProperties": false, "properties": { "kind": { "type": "string", "enum": [key] }, "parameters": value.parameters_schema(), "custom": { "type": "any", "description": "custom data, which shall be attached to notifications", "default": null, }, }, "required": [ "kind", "parameters", ] }) ) ), }, ] } } }, "required": [ ] }; } /** */ function normalize_interval( interval_raw ) : (null | _heimdall.type_interval) { if (interval_raw === null) { return null; } else { if (typeof(interval_raw) === "number") { return interval_raw; } else if (typeof(interval_raw) === "string") { const map_ : Record<("minute" | "hour" | "day" | "week"), int> = { "minute": (60), "hour": (60 * 60), "day": (60 * 60 * 24), "week": (60 * 60 * 24 * 7), }; if (! (interval_raw in map_)) { throw new Error("invalid string interval value: " + interval_raw); } else { return map_[interval_raw]; } } else { throw new Error("invalid type for interval value"); } } } /** */ function normalize_schedule( node ) : _heimdall.type_schedule { const node_ = Object.assign( { "regular_interval": (60 * 60), "attentive_interval": (60 * 2), "reminding_interval": (60 * 60 * 24), }, node ); return { "regular_interval": normalize_interval(node_["regular_interval"]), "attentive_interval": normalize_interval(node_["attentive_interval"]), "reminding_interval": normalize_interval(node_["reminding_interval"]), }; } /** */ function normalize_notification( node : any ) : type_notification { const notification_kind_implementations = _heimdall.notification_kinds.get_implementations(); if (! (node["kind"] in notification_kind_implementations)) { throw new Error("invalid notification kind: " + node["kind"]); } else { return { "kind": node["kind"], "parameters": notification_kind_implementations[node["kind"]].normalize_order_node(node["parameters"]), }; } } /** */ function normalize_defaults( node : any ) : type_check_common { const node_ = Object.assign( { "active": true, "threshold": 3, "annoy": false, "schedule": { "regular_interval": (60 * 60), "attentive_interval": (60 * 2), "reminding_interval": (60 * 60 * 24), }, "notifications": [ ], }, node ) return { "active": node_["active"], "threshold": node_["threshold"], "annoy": node_["annoy"], "schedule": node_["schedule"], "notifications": ( node_["notifications"] .map(x => normalize_notification(x)) ), }; } /** */ function normalize_check( defaults : type_check_common, node : { name : string; kind : string; } ) : type_check { const check_kind_implementations = _heimdall.check_kinds.get_implementations(); const notification_kind_implementations = _heimdall.notification_kinds.get_implementations(); if (! ("name" in node)) { throw new Error("missing mandatory field in 'check' node: 'name'"); } else { if (! ("kind" in node)) { throw new Error("missing mandatory field in 'check' node: 'kind'"); } else { if (! (node.kind in check_kind_implementations)) { throw new Error("invalid check kind: " + node.kind); } else { const node_ : type_check = Object.assign( lib_plankton.object.patched( defaults, { "title": node.name, "parameters": {}, "custom": null, }, ), node ) as type_check; let check : type_check = { "name": node_.name, "title": node_.title, "kind": node_.kind, "parameters": check_kind_implementations[node_.kind].normalize_order_node(node_.parameters), "custom": node_.custom, }; if ("active" in node_) { check.active = node_["active"]; } if ("threshold" in node_) { check.threshold = node_["threshold"]; } if ("annoy" in node_) { check.annoy = node_["annoy"]; } if ("schedule" in node_) { check.schedule = normalize_schedule(node_["schedule"]); } if ("notifications" in node_) { check.notifications = ( node_["notifications"] .map( x => normalize_notification(x), ) ); } if ("custom" in node_) { check.custom = node_["custom"]; } return check; } } } } /** */ function normalize_root( node : any, options : { use_implicit_default_values ?: boolean; } = {} ) : type_order { options = Object.assign( { "use_implicit_default_values": true, }, options ); let counts : Record = {}; const checks_raw : Array = ( ("checks" in node) ? node["checks"] : [] ); checks_raw.forEach( node_ => { if (! (node_["name"] in counts)) { counts[node_["name"]] = 0; } counts[node_["name"]] += 1; } ); const fails : Array<[string, int]> = ( Object.entries(counts) .filter( ([key, value]) => (value > 1) ) ); if (fails.length > 0) { throw new Error( lib_plankton.string.coin( "ambiguous check names: {{names}}", { "names": fails.map(([key, value]) => key).join(","), } ) ); } else { const defaults_raw : any = ( ("defaults" in node) ? node["defaults"] : {} ); const defaults : type_check_common = ( options.use_implicit_default_values ? normalize_defaults( defaults_raw ) : defaults_raw ); const includes : Array = ( ("includes" in node) ? node["includes"] : [] ); return { "defaults": defaults, "includes": includes, "checks": ( checks_raw .map( node_ => normalize_check( defaults, node_ ) ) ) }; } } /** */ export async function load( path : string, options : { root ?: boolean; already_included ?: Array; } = {} ) : Promise { const nm_path = require("path"); options = Object.assign( { "root": true, "already_included": [], }, options ); if (options.already_included.includes(path)) { throw new Error("circular dependency detected") } else { const order_raw : { includes ?: Array; checks ?: Array; } = lib_plankton.json.decode(await lib_plankton.file.read(path)); let includes : Array = ( ("includes" in order_raw) ? order_raw.includes : [] ); for (let index = 0; index < includes.length; index += 1) { const path_ : string = includes[index]; const sub_order : type_order = await load( ( nm_path.isAbsolute(path_) ? path_ : nm_path.join(nm_path.dirname(path), path_) ), { "root": false, "already_included": options.already_included.concat([path]), } ) if (! ("checks" in order_raw)) { order_raw.checks = []; } order_raw.checks = order_raw.checks.concat( sub_order.checks .map( check => Object.assign( check, { "name": lib_plankton.string.coin( "{{prefix}}.{{original_name}}", { "prefix": nm_path.basename(path_).split(".")[0], "original_name": check["name"], } ), } ), sub_order.checks ) ); order_raw.includes = []; } return normalize_root( order_raw, { "use_implicit_default_values": options["root"], } ); } } }