@sonde/packs 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test.log +53 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/CHANGELOG.md +19 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -2
- package/dist/index.js.map +1 -1
- package/dist/integrations/citrix.d.ts +13 -0
- package/dist/integrations/citrix.d.ts.map +1 -0
- package/dist/integrations/citrix.js +415 -0
- package/dist/integrations/citrix.js.map +1 -0
- package/dist/integrations/citrix.test.d.ts +2 -0
- package/dist/integrations/citrix.test.d.ts.map +1 -0
- package/dist/integrations/citrix.test.js +464 -0
- package/dist/integrations/citrix.test.js.map +1 -0
- package/dist/integrations/graph.d.ts +9 -0
- package/dist/integrations/graph.d.ts.map +1 -0
- package/dist/integrations/graph.js +285 -0
- package/dist/integrations/graph.js.map +1 -0
- package/dist/integrations/graph.test.d.ts +2 -0
- package/dist/integrations/graph.test.d.ts.map +1 -0
- package/dist/integrations/graph.test.js +356 -0
- package/dist/integrations/graph.test.js.map +1 -0
- package/dist/integrations/httpbin.d.ts +3 -0
- package/dist/integrations/httpbin.d.ts.map +1 -0
- package/dist/integrations/httpbin.js +65 -0
- package/dist/integrations/httpbin.js.map +1 -0
- package/dist/integrations/nutanix.d.ts +18 -0
- package/dist/integrations/nutanix.d.ts.map +1 -0
- package/dist/integrations/nutanix.js +1116 -0
- package/dist/integrations/nutanix.js.map +1 -0
- package/dist/integrations/nutanix.test.d.ts +2 -0
- package/dist/integrations/nutanix.test.d.ts.map +1 -0
- package/dist/integrations/nutanix.test.js +978 -0
- package/dist/integrations/nutanix.test.js.map +1 -0
- package/dist/integrations/proxmox.d.ts +12 -0
- package/dist/integrations/proxmox.d.ts.map +1 -0
- package/dist/integrations/proxmox.js +728 -0
- package/dist/integrations/proxmox.js.map +1 -0
- package/dist/integrations/proxmox.test.d.ts +2 -0
- package/dist/integrations/proxmox.test.d.ts.map +1 -0
- package/dist/integrations/proxmox.test.js +697 -0
- package/dist/integrations/proxmox.test.js.map +1 -0
- package/dist/integrations/servicenow.d.ts +3 -0
- package/dist/integrations/servicenow.d.ts.map +1 -0
- package/dist/integrations/servicenow.js +251 -0
- package/dist/integrations/servicenow.js.map +1 -0
- package/dist/integrations/servicenow.test.d.ts +2 -0
- package/dist/integrations/servicenow.test.d.ts.map +1 -0
- package/dist/integrations/servicenow.test.js +217 -0
- package/dist/integrations/servicenow.test.js.map +1 -0
- package/dist/integrations/splunk.d.ts +9 -0
- package/dist/integrations/splunk.d.ts.map +1 -0
- package/dist/integrations/splunk.js +237 -0
- package/dist/integrations/splunk.js.map +1 -0
- package/dist/integrations/splunk.test.d.ts +2 -0
- package/dist/integrations/splunk.test.d.ts.map +1 -0
- package/dist/integrations/splunk.test.js +323 -0
- package/dist/integrations/splunk.test.js.map +1 -0
- package/dist/mysql/index.d.ts +3 -0
- package/dist/mysql/index.d.ts.map +1 -0
- package/dist/mysql/index.js +13 -0
- package/dist/mysql/index.js.map +1 -0
- package/dist/mysql/manifest.d.ts +3 -0
- package/dist/mysql/manifest.d.ts.map +1 -0
- package/dist/mysql/manifest.js +69 -0
- package/dist/mysql/manifest.js.map +1 -0
- package/dist/mysql/probes/databases-list.d.ts +13 -0
- package/dist/mysql/probes/databases-list.d.ts.map +1 -0
- package/dist/mysql/probes/databases-list.js +31 -0
- package/dist/mysql/probes/databases-list.js.map +1 -0
- package/dist/mysql/probes/databases-list.test.d.ts +2 -0
- package/dist/mysql/probes/databases-list.test.d.ts.map +1 -0
- package/dist/mysql/probes/databases-list.test.js +54 -0
- package/dist/mysql/probes/databases-list.test.js.map +1 -0
- package/dist/mysql/probes/processlist.d.ts +18 -0
- package/dist/mysql/probes/processlist.d.ts.map +1 -0
- package/dist/mysql/probes/processlist.js +36 -0
- package/dist/mysql/probes/processlist.js.map +1 -0
- package/dist/mysql/probes/processlist.test.d.ts +2 -0
- package/dist/mysql/probes/processlist.test.d.ts.map +1 -0
- package/dist/mysql/probes/processlist.test.js +41 -0
- package/dist/mysql/probes/processlist.test.js.map +1 -0
- package/dist/mysql/probes/status.d.ts +14 -0
- package/dist/mysql/probes/status.d.ts.map +1 -0
- package/dist/mysql/probes/status.js +40 -0
- package/dist/mysql/probes/status.js.map +1 -0
- package/dist/mysql/probes/status.test.d.ts +2 -0
- package/dist/mysql/probes/status.test.d.ts.map +1 -0
- package/dist/mysql/probes/status.test.js +43 -0
- package/dist/mysql/probes/status.test.js.map +1 -0
- package/dist/nginx/index.d.ts +3 -0
- package/dist/nginx/index.d.ts.map +1 -0
- package/dist/nginx/index.js +13 -0
- package/dist/nginx/index.js.map +1 -0
- package/dist/nginx/manifest.d.ts +3 -0
- package/dist/nginx/manifest.d.ts.map +1 -0
- package/dist/nginx/manifest.js +68 -0
- package/dist/nginx/manifest.js.map +1 -0
- package/dist/nginx/probes/access-log-tail.d.ts +9 -0
- package/dist/nginx/probes/access-log-tail.d.ts.map +1 -0
- package/dist/nginx/probes/access-log-tail.js +14 -0
- package/dist/nginx/probes/access-log-tail.js.map +1 -0
- package/dist/nginx/probes/access-log-tail.test.d.ts +2 -0
- package/dist/nginx/probes/access-log-tail.test.d.ts.map +1 -0
- package/dist/nginx/probes/access-log-tail.test.js +40 -0
- package/dist/nginx/probes/access-log-tail.test.js.map +1 -0
- package/dist/nginx/probes/config-test.d.ts +8 -0
- package/dist/nginx/probes/config-test.d.ts.map +1 -0
- package/dist/nginx/probes/config-test.js +18 -0
- package/dist/nginx/probes/config-test.js.map +1 -0
- package/dist/nginx/probes/config-test.test.d.ts +2 -0
- package/dist/nginx/probes/config-test.test.d.ts.map +1 -0
- package/dist/nginx/probes/config-test.test.js +35 -0
- package/dist/nginx/probes/config-test.test.js.map +1 -0
- package/dist/nginx/probes/error-log-tail.d.ts +9 -0
- package/dist/nginx/probes/error-log-tail.d.ts.map +1 -0
- package/dist/nginx/probes/error-log-tail.js +14 -0
- package/dist/nginx/probes/error-log-tail.js.map +1 -0
- package/dist/nginx/probes/error-log-tail.test.d.ts +2 -0
- package/dist/nginx/probes/error-log-tail.test.d.ts.map +1 -0
- package/dist/nginx/probes/error-log-tail.test.js +34 -0
- package/dist/nginx/probes/error-log-tail.test.js.map +1 -0
- package/dist/postgres/index.d.ts +3 -0
- package/dist/postgres/index.d.ts.map +1 -0
- package/dist/postgres/index.js +13 -0
- package/dist/postgres/index.js.map +1 -0
- package/dist/postgres/manifest.d.ts +3 -0
- package/dist/postgres/manifest.d.ts.map +1 -0
- package/dist/postgres/manifest.js +90 -0
- package/dist/postgres/manifest.js.map +1 -0
- package/dist/postgres/probes/connections-active.d.ts +17 -0
- package/dist/postgres/probes/connections-active.d.ts.map +1 -0
- package/dist/postgres/probes/connections-active.js +37 -0
- package/dist/postgres/probes/connections-active.js.map +1 -0
- package/dist/postgres/probes/connections-active.test.d.ts +2 -0
- package/dist/postgres/probes/connections-active.test.d.ts.map +1 -0
- package/dist/postgres/probes/connections-active.test.js +36 -0
- package/dist/postgres/probes/connections-active.test.js.map +1 -0
- package/dist/postgres/probes/databases-list.d.ts +14 -0
- package/dist/postgres/probes/databases-list.d.ts.map +1 -0
- package/dist/postgres/probes/databases-list.js +34 -0
- package/dist/postgres/probes/databases-list.js.map +1 -0
- package/dist/postgres/probes/databases-list.test.d.ts +2 -0
- package/dist/postgres/probes/databases-list.test.d.ts.map +1 -0
- package/dist/postgres/probes/databases-list.test.js +49 -0
- package/dist/postgres/probes/databases-list.test.js.map +1 -0
- package/dist/postgres/probes/query-slow.d.ts +17 -0
- package/dist/postgres/probes/query-slow.d.ts.map +1 -0
- package/dist/postgres/probes/query-slow.js +37 -0
- package/dist/postgres/probes/query-slow.js.map +1 -0
- package/dist/postgres/probes/query-slow.test.d.ts +2 -0
- package/dist/postgres/probes/query-slow.test.d.ts.map +1 -0
- package/dist/postgres/probes/query-slow.test.js +30 -0
- package/dist/postgres/probes/query-slow.test.js.map +1 -0
- package/dist/proxmox/index.d.ts +3 -0
- package/dist/proxmox/index.d.ts.map +1 -0
- package/dist/proxmox/index.js +23 -0
- package/dist/proxmox/index.js.map +1 -0
- package/dist/proxmox/manifest.d.ts +3 -0
- package/dist/proxmox/manifest.d.ts.map +1 -0
- package/dist/proxmox/manifest.js +75 -0
- package/dist/proxmox/manifest.js.map +1 -0
- package/dist/proxmox/probes/ceph-status.d.ts +36 -0
- package/dist/proxmox/probes/ceph-status.d.ts.map +1 -0
- package/dist/proxmox/probes/ceph-status.js +71 -0
- package/dist/proxmox/probes/ceph-status.js.map +1 -0
- package/dist/proxmox/probes/ceph-status.test.d.ts +2 -0
- package/dist/proxmox/probes/ceph-status.test.d.ts.map +1 -0
- package/dist/proxmox/probes/ceph-status.test.js +115 -0
- package/dist/proxmox/probes/ceph-status.test.js.map +1 -0
- package/dist/proxmox/probes/cluster-config.d.ts +31 -0
- package/dist/proxmox/probes/cluster-config.d.ts.map +1 -0
- package/dist/proxmox/probes/cluster-config.js +72 -0
- package/dist/proxmox/probes/cluster-config.js.map +1 -0
- package/dist/proxmox/probes/cluster-config.test.d.ts +2 -0
- package/dist/proxmox/probes/cluster-config.test.d.ts.map +1 -0
- package/dist/proxmox/probes/cluster-config.test.js +107 -0
- package/dist/proxmox/probes/cluster-config.test.js.map +1 -0
- package/dist/proxmox/probes/ha-status.d.ts +18 -0
- package/dist/proxmox/probes/ha-status.d.ts.map +1 -0
- package/dist/proxmox/probes/ha-status.js +38 -0
- package/dist/proxmox/probes/ha-status.js.map +1 -0
- package/dist/proxmox/probes/ha-status.test.d.ts +2 -0
- package/dist/proxmox/probes/ha-status.test.d.ts.map +1 -0
- package/dist/proxmox/probes/ha-status.test.js +66 -0
- package/dist/proxmox/probes/ha-status.test.js.map +1 -0
- package/dist/proxmox/probes/lvm.d.ts +35 -0
- package/dist/proxmox/probes/lvm.d.ts.map +1 -0
- package/dist/proxmox/probes/lvm.js +75 -0
- package/dist/proxmox/probes/lvm.js.map +1 -0
- package/dist/proxmox/probes/lvm.test.d.ts +2 -0
- package/dist/proxmox/probes/lvm.test.d.ts.map +1 -0
- package/dist/proxmox/probes/lvm.test.js +128 -0
- package/dist/proxmox/probes/lvm.test.js.map +1 -0
- package/dist/proxmox/probes/lxc-config.d.ts +29 -0
- package/dist/proxmox/probes/lxc-config.d.ts.map +1 -0
- package/dist/proxmox/probes/lxc-config.js +67 -0
- package/dist/proxmox/probes/lxc-config.js.map +1 -0
- package/dist/proxmox/probes/lxc-config.test.d.ts +2 -0
- package/dist/proxmox/probes/lxc-config.test.d.ts.map +1 -0
- package/dist/proxmox/probes/lxc-config.test.js +77 -0
- package/dist/proxmox/probes/lxc-config.test.js.map +1 -0
- package/dist/proxmox/probes/lxc-list.d.ts +20 -0
- package/dist/proxmox/probes/lxc-list.d.ts.map +1 -0
- package/dist/proxmox/probes/lxc-list.js +49 -0
- package/dist/proxmox/probes/lxc-list.js.map +1 -0
- package/dist/proxmox/probes/lxc-list.test.d.ts +2 -0
- package/dist/proxmox/probes/lxc-list.test.d.ts.map +1 -0
- package/dist/proxmox/probes/lxc-list.test.js +51 -0
- package/dist/proxmox/probes/lxc-list.test.js.map +1 -0
- package/dist/proxmox/probes/vm-config.d.ts +21 -0
- package/dist/proxmox/probes/vm-config.d.ts.map +1 -0
- package/dist/proxmox/probes/vm-config.js +58 -0
- package/dist/proxmox/probes/vm-config.js.map +1 -0
- package/dist/proxmox/probes/vm-config.test.d.ts +2 -0
- package/dist/proxmox/probes/vm-config.test.d.ts.map +1 -0
- package/dist/proxmox/probes/vm-config.test.js +80 -0
- package/dist/proxmox/probes/vm-config.test.js.map +1 -0
- package/dist/proxmox/probes/vm-locks.d.ts +16 -0
- package/dist/proxmox/probes/vm-locks.d.ts.map +1 -0
- package/dist/proxmox/probes/vm-locks.js +35 -0
- package/dist/proxmox/probes/vm-locks.js.map +1 -0
- package/dist/proxmox/probes/vm-locks.test.d.ts +2 -0
- package/dist/proxmox/probes/vm-locks.test.d.ts.map +1 -0
- package/dist/proxmox/probes/vm-locks.test.js +54 -0
- package/dist/proxmox/probes/vm-locks.test.js.map +1 -0
- package/dist/redis/index.d.ts +3 -0
- package/dist/redis/index.d.ts.map +1 -0
- package/dist/redis/index.js +13 -0
- package/dist/redis/index.js.map +1 -0
- package/dist/redis/manifest.d.ts +3 -0
- package/dist/redis/manifest.d.ts.map +1 -0
- package/dist/redis/manifest.js +51 -0
- package/dist/redis/manifest.js.map +1 -0
- package/dist/redis/probes/info.d.ts +15 -0
- package/dist/redis/probes/info.d.ts.map +1 -0
- package/dist/redis/probes/info.js +32 -0
- package/dist/redis/probes/info.js.map +1 -0
- package/dist/redis/probes/info.test.d.ts +2 -0
- package/dist/redis/probes/info.test.d.ts.map +1 -0
- package/dist/redis/probes/info.test.js +64 -0
- package/dist/redis/probes/info.test.js.map +1 -0
- package/dist/redis/probes/keys-count.d.ts +13 -0
- package/dist/redis/probes/keys-count.d.ts.map +1 -0
- package/dist/redis/probes/keys-count.js +24 -0
- package/dist/redis/probes/keys-count.js.map +1 -0
- package/dist/redis/probes/keys-count.test.d.ts +2 -0
- package/dist/redis/probes/keys-count.test.d.ts.map +1 -0
- package/dist/redis/probes/keys-count.test.js +37 -0
- package/dist/redis/probes/keys-count.test.js.map +1 -0
- package/dist/redis/probes/memory-usage.d.ts +16 -0
- package/dist/redis/probes/memory-usage.d.ts.map +1 -0
- package/dist/redis/probes/memory-usage.js +31 -0
- package/dist/redis/probes/memory-usage.js.map +1 -0
- package/dist/redis/probes/memory-usage.test.d.ts +2 -0
- package/dist/redis/probes/memory-usage.test.d.ts.map +1 -0
- package/dist/redis/probes/memory-usage.test.js +48 -0
- package/dist/redis/probes/memory-usage.test.js.map +1 -0
- package/dist/runbooks/nutanix.d.ts +3 -0
- package/dist/runbooks/nutanix.d.ts.map +1 -0
- package/dist/runbooks/nutanix.js +619 -0
- package/dist/runbooks/nutanix.js.map +1 -0
- package/dist/runbooks/nutanix.test.d.ts +2 -0
- package/dist/runbooks/nutanix.test.d.ts.map +1 -0
- package/dist/runbooks/nutanix.test.js +971 -0
- package/dist/runbooks/nutanix.test.js.map +1 -0
- package/dist/runbooks/proxmox.d.ts +3 -0
- package/dist/runbooks/proxmox.d.ts.map +1 -0
- package/dist/runbooks/proxmox.js +451 -0
- package/dist/runbooks/proxmox.js.map +1 -0
- package/dist/runbooks/proxmox.test.d.ts +2 -0
- package/dist/runbooks/proxmox.test.d.ts.map +1 -0
- package/dist/runbooks/proxmox.test.js +700 -0
- package/dist/runbooks/proxmox.test.js.map +1 -0
- package/dist/signatures.d.ts +2 -0
- package/dist/signatures.d.ts.map +1 -0
- package/dist/signatures.js +2 -0
- package/dist/signatures.js.map +1 -0
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +2 -0
- package/dist/system/index.js.map +1 -1
- package/dist/system/manifest.d.ts.map +1 -1
- package/dist/system/manifest.js +19 -1
- package/dist/system/manifest.js.map +1 -1
- package/dist/system/probes/ping.d.ts +20 -0
- package/dist/system/probes/ping.d.ts.map +1 -0
- package/dist/system/probes/ping.js +54 -0
- package/dist/system/probes/ping.js.map +1 -0
- package/dist/system/probes/ping.test.d.ts +2 -0
- package/dist/system/probes/ping.test.d.ts.map +1 -0
- package/dist/system/probes/ping.test.js +127 -0
- package/dist/system/probes/ping.test.js.map +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts +6 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +10 -1
- package/dist/validation.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +60 -6
- package/src/integrations/citrix.test.ts +592 -0
- package/src/integrations/citrix.ts +553 -0
- package/src/integrations/graph.test.ts +478 -0
- package/src/integrations/graph.ts +409 -0
- package/src/integrations/httpbin.ts +68 -0
- package/src/integrations/nutanix.test.ts +1508 -0
- package/src/integrations/nutanix.ts +1456 -0
- package/src/integrations/proxmox.test.ts +1020 -0
- package/src/integrations/proxmox.ts +985 -0
- package/src/integrations/servicenow.test.ts +314 -0
- package/src/integrations/servicenow.ts +280 -0
- package/src/integrations/splunk.test.ts +440 -0
- package/src/integrations/splunk.ts +352 -0
- package/src/mysql/index.ts +14 -0
- package/src/mysql/manifest.ts +70 -0
- package/src/mysql/probes/databases-list.test.ts +62 -0
- package/src/mysql/probes/databases-list.ts +45 -0
- package/src/mysql/probes/processlist.test.ts +47 -0
- package/src/mysql/probes/processlist.ts +55 -0
- package/src/mysql/probes/status.test.ts +50 -0
- package/src/mysql/probes/status.ts +56 -0
- package/src/nginx/index.ts +14 -0
- package/src/nginx/manifest.ts +69 -0
- package/src/nginx/probes/access-log-tail.test.ts +51 -0
- package/src/nginx/probes/access-log-tail.ts +23 -0
- package/src/nginx/probes/config-test.test.ts +47 -0
- package/src/nginx/probes/config-test.ts +24 -0
- package/src/nginx/probes/error-log-tail.test.ts +44 -0
- package/src/nginx/probes/error-log-tail.ts +23 -0
- package/src/postgres/index.ts +14 -0
- package/src/postgres/manifest.ts +91 -0
- package/src/postgres/probes/connections-active.test.ts +42 -0
- package/src/postgres/probes/connections-active.ts +55 -0
- package/src/postgres/probes/databases-list.test.ts +57 -0
- package/src/postgres/probes/databases-list.ts +49 -0
- package/src/postgres/probes/query-slow.test.ts +37 -0
- package/src/postgres/probes/query-slow.ts +55 -0
- package/src/proxmox/index.ts +24 -0
- package/src/proxmox/manifest.ts +76 -0
- package/src/proxmox/probes/ceph-status.test.ts +126 -0
- package/src/proxmox/probes/ceph-status.ts +116 -0
- package/src/proxmox/probes/cluster-config.test.ts +118 -0
- package/src/proxmox/probes/cluster-config.ts +97 -0
- package/src/proxmox/probes/ha-status.test.ts +76 -0
- package/src/proxmox/probes/ha-status.ts +56 -0
- package/src/proxmox/probes/lvm.test.ts +140 -0
- package/src/proxmox/probes/lvm.ts +121 -0
- package/src/proxmox/probes/lxc-config.test.ts +89 -0
- package/src/proxmox/probes/lxc-config.ts +90 -0
- package/src/proxmox/probes/lxc-list.test.ts +60 -0
- package/src/proxmox/probes/lxc-list.ts +67 -0
- package/src/proxmox/probes/vm-config.test.ts +93 -0
- package/src/proxmox/probes/vm-config.ts +77 -0
- package/src/proxmox/probes/vm-locks.test.ts +63 -0
- package/src/proxmox/probes/vm-locks.ts +49 -0
- package/src/redis/index.ts +14 -0
- package/src/redis/manifest.ts +52 -0
- package/src/redis/probes/info.test.ts +73 -0
- package/src/redis/probes/info.ts +46 -0
- package/src/redis/probes/keys-count.test.ts +44 -0
- package/src/redis/probes/keys-count.ts +38 -0
- package/src/redis/probes/memory-usage.test.ts +54 -0
- package/src/redis/probes/memory-usage.ts +46 -0
- package/src/runbooks/nutanix.test.ts +1138 -0
- package/src/runbooks/nutanix.ts +941 -0
- package/src/runbooks/proxmox.test.ts +838 -0
- package/src/runbooks/proxmox.ts +626 -0
- package/src/signatures.ts +1 -0
- package/src/system/index.ts +2 -0
- package/src/system/manifest.ts +21 -1
- package/src/system/probes/ping.test.ts +163 -0
- package/src/system/probes/ping.ts +89 -0
- package/src/types.ts +62 -0
- package/src/validation.ts +21 -1
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import type { IntegrationConfig, IntegrationCredentials } from '@sonde/shared';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { servicenowPack } from './servicenow.js';
|
|
4
|
+
|
|
5
|
+
const config: IntegrationConfig = {
|
|
6
|
+
endpoint: 'https://instance.service-now.com',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const basicCreds: IntegrationCredentials = {
|
|
10
|
+
packName: 'servicenow',
|
|
11
|
+
authMethod: 'api_key',
|
|
12
|
+
credentials: { username: 'admin', password: 'secret' },
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const oauthCreds: IntegrationCredentials = {
|
|
16
|
+
packName: 'servicenow',
|
|
17
|
+
authMethod: 'oauth2',
|
|
18
|
+
credentials: {},
|
|
19
|
+
oauth2: { accessToken: 'my-token' },
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const handler = (name: string) => {
|
|
23
|
+
const h = servicenowPack.handlers[name];
|
|
24
|
+
if (!h) throw new Error(`Handler ${name} not found`);
|
|
25
|
+
return h;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Get a specific call's args from a mock, with bounds check */
|
|
29
|
+
function callArgs(fn: ReturnType<typeof vi.fn>, index: number): unknown[] {
|
|
30
|
+
const args = fn.mock.calls[index];
|
|
31
|
+
if (!args) throw new Error(`No call at index ${index}`);
|
|
32
|
+
return args;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Create a mock fetch that returns ServiceNow-shaped JSON */
|
|
36
|
+
function mockFetch(result: unknown[] = []) {
|
|
37
|
+
return vi.fn().mockResolvedValue(
|
|
38
|
+
new Response(JSON.stringify({ result }), {
|
|
39
|
+
status: 200,
|
|
40
|
+
headers: { 'Content-Type': 'application/json' },
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Create a mock fetch that returns a non-200 response */
|
|
46
|
+
function mockFetchError(status: number) {
|
|
47
|
+
return vi.fn().mockResolvedValue(new Response('Error', { status, statusText: 'Error' }));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Create a multi-step mock: first call returns lookup result, second returns data */
|
|
51
|
+
function mockFetchMultiStep(lookupResult: unknown[], dataResult: unknown[]) {
|
|
52
|
+
let callCount = 0;
|
|
53
|
+
return vi.fn().mockImplementation(() => {
|
|
54
|
+
callCount++;
|
|
55
|
+
const body = callCount === 1 ? { result: lookupResult } : { result: dataResult };
|
|
56
|
+
return Promise.resolve(
|
|
57
|
+
new Response(JSON.stringify(body), {
|
|
58
|
+
status: 200,
|
|
59
|
+
headers: { 'Content-Type': 'application/json' },
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
describe('servicenow pack', () => {
|
|
66
|
+
describe('auth headers', () => {
|
|
67
|
+
it('uses basic auth for api_key method', async () => {
|
|
68
|
+
const fetchFn = mockFetch([{ name: 'web01' }]);
|
|
69
|
+
await handler('ci.lookup')({ query: 'web01' }, config, basicCreds, fetchFn);
|
|
70
|
+
|
|
71
|
+
const init = callArgs(fetchFn, 0)[1] as { headers: Record<string, string> };
|
|
72
|
+
const expected = `Basic ${Buffer.from('admin:secret').toString('base64')}`;
|
|
73
|
+
expect(init.headers.Authorization).toBe(expected);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('uses bearer token for oauth2 method', async () => {
|
|
77
|
+
const fetchFn = mockFetch([{ name: 'web01' }]);
|
|
78
|
+
await handler('ci.lookup')({ query: 'web01' }, config, oauthCreds, fetchFn);
|
|
79
|
+
|
|
80
|
+
const init = callArgs(fetchFn, 0)[1] as { headers: Record<string, string> };
|
|
81
|
+
expect(init.headers.Authorization).toBe('Bearer my-token');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('testConnection', () => {
|
|
86
|
+
it('returns true on 200', async () => {
|
|
87
|
+
const fetchFn = mockFetch([]);
|
|
88
|
+
const result = await servicenowPack.testConnection(config, basicCreds, fetchFn);
|
|
89
|
+
expect(result).toBe(true);
|
|
90
|
+
|
|
91
|
+
const [url] = callArgs(fetchFn, 0);
|
|
92
|
+
expect(url).toContain('/api/now/table/sys_properties');
|
|
93
|
+
expect(url).toContain('sysparm_limit=1');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('returns false on non-200', async () => {
|
|
97
|
+
const fetchFn = mockFetchError(401);
|
|
98
|
+
const result = await servicenowPack.testConnection(config, basicCreds, fetchFn);
|
|
99
|
+
expect(result).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('throws on network error', async () => {
|
|
103
|
+
const fetchFn = vi.fn().mockRejectedValue(new Error('ECONNREFUSED'));
|
|
104
|
+
await expect(servicenowPack.testConnection(config, basicCreds, fetchFn))
|
|
105
|
+
.rejects.toThrow('ECONNREFUSED');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('ci.lookup', () => {
|
|
110
|
+
it('queries by name or IP with default server type', async () => {
|
|
111
|
+
const fetchFn = mockFetch([{ name: 'web01', ip_address: '10.0.0.1' }]);
|
|
112
|
+
const result = await handler('ci.lookup')({ query: 'web01' }, config, basicCreds, fetchFn);
|
|
113
|
+
|
|
114
|
+
const [url] = callArgs(fetchFn, 0);
|
|
115
|
+
expect(url).toContain('/api/now/table/cmdb_ci_server');
|
|
116
|
+
expect(url).toContain('name%3Dweb01%5EORip_address%3Dweb01');
|
|
117
|
+
expect(result).toEqual([{ name: 'web01', ip_address: '10.0.0.1' }]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('uses custom CI type when provided', async () => {
|
|
121
|
+
const fetchFn = mockFetch([]);
|
|
122
|
+
await handler('ci.lookup')(
|
|
123
|
+
{ query: 'desk01', type: 'computer' },
|
|
124
|
+
config,
|
|
125
|
+
basicCreds,
|
|
126
|
+
fetchFn,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const [url] = callArgs(fetchFn, 0);
|
|
130
|
+
expect(url).toContain('/api/now/table/cmdb_ci_computer');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('throws when query is missing', async () => {
|
|
134
|
+
const fetchFn = mockFetch();
|
|
135
|
+
await expect(handler('ci.lookup')({}, config, basicCreds, fetchFn)).rejects.toThrow(
|
|
136
|
+
'query parameter is required',
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('includes common params', async () => {
|
|
141
|
+
const fetchFn = mockFetch([]);
|
|
142
|
+
await handler('ci.lookup')({ query: 'test' }, config, basicCreds, fetchFn);
|
|
143
|
+
|
|
144
|
+
const [url] = callArgs(fetchFn, 0);
|
|
145
|
+
expect(url).toContain('sysparm_display_value=true');
|
|
146
|
+
expect(url).toContain('sysparm_exclude_reference_link=true');
|
|
147
|
+
expect(url).toContain('sysparm_limit=1000');
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('ci.owner', () => {
|
|
152
|
+
it('returns ownership fields', async () => {
|
|
153
|
+
const ownerData = [{ name: 'web01', assigned_to: 'Alice', support_group: 'Infra' }];
|
|
154
|
+
const fetchFn = mockFetch(ownerData);
|
|
155
|
+
const result = await handler('ci.owner')({ name: 'web01' }, config, basicCreds, fetchFn);
|
|
156
|
+
|
|
157
|
+
const [url] = callArgs(fetchFn, 0);
|
|
158
|
+
expect(url).toContain(
|
|
159
|
+
'sysparm_fields=name%2Cassigned_to%2Csupport_group%2Cmanaged_by%2Cowned_by',
|
|
160
|
+
);
|
|
161
|
+
expect(result).toEqual(ownerData);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('throws when name is missing', async () => {
|
|
165
|
+
await expect(handler('ci.owner')({}, config, basicCreds, mockFetch())).rejects.toThrow(
|
|
166
|
+
'name parameter is required',
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('ci.relationships', () => {
|
|
172
|
+
it('looks up sys_id then queries relationships', async () => {
|
|
173
|
+
const fetchFn = mockFetchMultiStep(
|
|
174
|
+
[{ sys_id: 'abc123', name: 'web01' }],
|
|
175
|
+
[{ parent: 'abc123', child: 'def456', type: 'Runs on' }],
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const result = await handler('ci.relationships')(
|
|
179
|
+
{ name: 'web01' },
|
|
180
|
+
config,
|
|
181
|
+
basicCreds,
|
|
182
|
+
fetchFn,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
expect(fetchFn).toHaveBeenCalledTimes(2);
|
|
186
|
+
const [lookupUrl] = callArgs(fetchFn, 0);
|
|
187
|
+
expect(lookupUrl).toContain('/api/now/table/cmdb_ci_server');
|
|
188
|
+
const [relUrl] = callArgs(fetchFn, 1);
|
|
189
|
+
expect(relUrl).toContain('/api/now/table/cmdb_rel_ci');
|
|
190
|
+
expect(relUrl).toContain('parent%3Dabc123%5EORchild%3Dabc123');
|
|
191
|
+
expect(result).toEqual([{ parent: 'abc123', child: 'def456', type: 'Runs on' }]);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('throws when CI not found', async () => {
|
|
195
|
+
const fetchFn = mockFetch([]);
|
|
196
|
+
await expect(
|
|
197
|
+
handler('ci.relationships')({ name: 'missing' }, config, basicCreds, fetchFn),
|
|
198
|
+
).rejects.toThrow('CI not found: missing');
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('changes.recent', () => {
|
|
203
|
+
it('looks up sys_id then queries change_request with default 7 days', async () => {
|
|
204
|
+
const fetchFn = mockFetchMultiStep(
|
|
205
|
+
[{ sys_id: 'srv01' }],
|
|
206
|
+
[{ number: 'CHG0001', short_description: 'Update' }],
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
const result = await handler('changes.recent')(
|
|
210
|
+
{ name: 'web01' },
|
|
211
|
+
config,
|
|
212
|
+
basicCreds,
|
|
213
|
+
fetchFn,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const [changeUrl] = callArgs(fetchFn, 1);
|
|
217
|
+
expect(changeUrl).toContain('/api/now/table/change_request');
|
|
218
|
+
expect(changeUrl).toContain('cmdb_ci%3Dsrv01');
|
|
219
|
+
expect(changeUrl).toContain('gs.daysAgoStart%287%29');
|
|
220
|
+
expect(result).toEqual([{ number: 'CHG0001', short_description: 'Update' }]);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('respects custom days parameter', async () => {
|
|
224
|
+
const fetchFn = mockFetchMultiStep([{ sys_id: 'srv01' }], []);
|
|
225
|
+
|
|
226
|
+
await handler('changes.recent')({ name: 'web01', days: 30 }, config, basicCreds, fetchFn);
|
|
227
|
+
|
|
228
|
+
const [changeUrl] = callArgs(fetchFn, 1);
|
|
229
|
+
expect(changeUrl).toContain('gs.daysAgoStart%2830%29');
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
describe('incidents.open', () => {
|
|
234
|
+
it('queries incidents not in resolved/closed/canceled states', async () => {
|
|
235
|
+
const incidents = [{ number: 'INC0001', state: '2', short_description: 'Server down' }];
|
|
236
|
+
const fetchFn = mockFetch(incidents);
|
|
237
|
+
const result = await handler('incidents.open')(
|
|
238
|
+
{ name: 'web01' },
|
|
239
|
+
config,
|
|
240
|
+
basicCreds,
|
|
241
|
+
fetchFn,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
const [url] = callArgs(fetchFn, 0);
|
|
245
|
+
expect(url).toContain('/api/now/table/incident');
|
|
246
|
+
expect(url).toContain('cmdb_ci.name%3Dweb01');
|
|
247
|
+
expect(url).toContain('stateNOT+IN6%2C7%2C8');
|
|
248
|
+
expect(result).toEqual(incidents);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe('service.health', () => {
|
|
253
|
+
it('fetches service then child relationships', async () => {
|
|
254
|
+
const fetchFn = mockFetchMultiStep(
|
|
255
|
+
[{ sys_id: 'svc01', name: 'Email', operational_status: '1' }],
|
|
256
|
+
[{ child: 'srv01', type: 'Depends on' }],
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
const result = (await handler('service.health')(
|
|
260
|
+
{ service_name: 'Email' },
|
|
261
|
+
config,
|
|
262
|
+
basicCreds,
|
|
263
|
+
fetchFn,
|
|
264
|
+
)) as { service: { name: string }; children: unknown[] };
|
|
265
|
+
|
|
266
|
+
expect(fetchFn).toHaveBeenCalledTimes(2);
|
|
267
|
+
expect(result.service.name).toBe('Email');
|
|
268
|
+
expect(result.children).toEqual([{ child: 'srv01', type: 'Depends on' }]);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('throws when service not found', async () => {
|
|
272
|
+
const fetchFn = mockFetch([]);
|
|
273
|
+
await expect(
|
|
274
|
+
handler('service.health')({ service_name: 'Missing' }, config, basicCreds, fetchFn),
|
|
275
|
+
).rejects.toThrow('Service not found: Missing');
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe('ci.lifecycle', () => {
|
|
280
|
+
it('returns lifecycle fields', async () => {
|
|
281
|
+
const lifecycle = [{ name: 'web01', install_date: '2023-01-15', asset_tag: 'A001' }];
|
|
282
|
+
const fetchFn = mockFetch(lifecycle);
|
|
283
|
+
const result = await handler('ci.lifecycle')({ name: 'web01' }, config, basicCreds, fetchFn);
|
|
284
|
+
|
|
285
|
+
const [url] = callArgs(fetchFn, 0);
|
|
286
|
+
expect(url).toContain(
|
|
287
|
+
'sysparm_fields=name%2Cinstall_date%2Cwarranty_expiration%2Cend_of_life%2Casset_tag%2Cmodel_id',
|
|
288
|
+
);
|
|
289
|
+
expect(result).toEqual(lifecycle);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
describe('error handling', () => {
|
|
294
|
+
it('throws on non-200 API response', async () => {
|
|
295
|
+
const fetchFn = mockFetchError(403);
|
|
296
|
+
await expect(
|
|
297
|
+
handler('ci.lookup')({ query: 'web01' }, config, basicCreds, fetchFn),
|
|
298
|
+
).rejects.toThrow('ServiceNow API returned 403');
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe('manifest', () => {
|
|
303
|
+
it('has correct name and probe count', () => {
|
|
304
|
+
expect(servicenowPack.manifest.name).toBe('servicenow');
|
|
305
|
+
expect(servicenowPack.manifest.probes).toHaveLength(7);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('all handlers match manifest probes', () => {
|
|
309
|
+
const probeNames = servicenowPack.manifest.probes.map((p) => p.name);
|
|
310
|
+
const handlerNames = Object.keys(servicenowPack.handlers);
|
|
311
|
+
expect(handlerNames.sort()).toEqual(probeNames.sort());
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
});
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FetchFn,
|
|
3
|
+
IntegrationConfig,
|
|
4
|
+
IntegrationCredentials,
|
|
5
|
+
IntegrationPack,
|
|
6
|
+
IntegrationProbeHandler,
|
|
7
|
+
} from '@sonde/shared';
|
|
8
|
+
|
|
9
|
+
/** Build Authorization header based on auth method */
|
|
10
|
+
function buildAuthHeaders(credentials: IntegrationCredentials): Record<string, string> {
|
|
11
|
+
if (credentials.authMethod === 'oauth2' && credentials.oauth2?.accessToken) {
|
|
12
|
+
return { Authorization: `Bearer ${credentials.oauth2.accessToken}` };
|
|
13
|
+
}
|
|
14
|
+
// api_key = basic auth with username:password
|
|
15
|
+
const { username, password } = credentials.credentials;
|
|
16
|
+
if (username && password) {
|
|
17
|
+
const encoded = Buffer.from(`${username}:${password}`).toString('base64');
|
|
18
|
+
return { Authorization: `Basic ${encoded}` };
|
|
19
|
+
}
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Common query params for all ServiceNow Table API calls */
|
|
24
|
+
const COMMON_PARAMS = {
|
|
25
|
+
sysparm_display_value: 'true',
|
|
26
|
+
sysparm_exclude_reference_link: 'true',
|
|
27
|
+
sysparm_limit: '1000',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Build a ServiceNow Table API URL */
|
|
31
|
+
function buildUrl(endpoint: string, table: string, query?: string, fields?: string[]): string {
|
|
32
|
+
const url = new URL(`/api/now/table/${table}`, endpoint);
|
|
33
|
+
for (const [key, value] of Object.entries(COMMON_PARAMS)) {
|
|
34
|
+
url.searchParams.set(key, value);
|
|
35
|
+
}
|
|
36
|
+
if (query) url.searchParams.set('sysparm_query', query);
|
|
37
|
+
if (fields?.length) url.searchParams.set('sysparm_fields', fields.join(','));
|
|
38
|
+
return url.toString();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Fetch helper that adds auth headers and checks response */
|
|
42
|
+
async function snowFetch(
|
|
43
|
+
url: string,
|
|
44
|
+
config: IntegrationConfig,
|
|
45
|
+
credentials: IntegrationCredentials,
|
|
46
|
+
fetchFn: FetchFn,
|
|
47
|
+
): Promise<{ result: unknown[] }> {
|
|
48
|
+
const headers: Record<string, string> = {
|
|
49
|
+
Accept: 'application/json',
|
|
50
|
+
...buildAuthHeaders(credentials),
|
|
51
|
+
...config.headers,
|
|
52
|
+
};
|
|
53
|
+
const res = await fetchFn(url, { headers });
|
|
54
|
+
if (!res.ok) throw new Error(`ServiceNow API returned ${res.status}: ${res.statusText}`);
|
|
55
|
+
return (await res.json()) as { result: unknown[] };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Look up a CI's sys_id by name */
|
|
59
|
+
async function lookupSysId(
|
|
60
|
+
name: string,
|
|
61
|
+
config: IntegrationConfig,
|
|
62
|
+
credentials: IntegrationCredentials,
|
|
63
|
+
fetchFn: FetchFn,
|
|
64
|
+
): Promise<string> {
|
|
65
|
+
const url = buildUrl(config.endpoint, 'cmdb_ci_server', `name=${name}`, ['sys_id', 'name']);
|
|
66
|
+
const data = await snowFetch(url, config, credentials, fetchFn);
|
|
67
|
+
const results = data.result as Array<{ sys_id: string }>;
|
|
68
|
+
const first = results[0];
|
|
69
|
+
if (!first) throw new Error(`CI not found: ${name}`);
|
|
70
|
+
return first.sys_id;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// --- Probe handlers ---
|
|
74
|
+
|
|
75
|
+
const ciLookup: IntegrationProbeHandler = async (params, config, credentials, fetchFn) => {
|
|
76
|
+
const query = params?.query as string;
|
|
77
|
+
if (!query) throw new Error('query parameter is required');
|
|
78
|
+
const type = (params?.type as string) || 'server';
|
|
79
|
+
const table = `cmdb_ci_${type}`;
|
|
80
|
+
const url = buildUrl(config.endpoint, table, `name=${query}^ORip_address=${query}`);
|
|
81
|
+
const data = await snowFetch(url, config, credentials, fetchFn);
|
|
82
|
+
return data.result;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const ciOwner: IntegrationProbeHandler = async (params, config, credentials, fetchFn) => {
|
|
86
|
+
const name = params?.name as string;
|
|
87
|
+
if (!name) throw new Error('name parameter is required');
|
|
88
|
+
const url = buildUrl(config.endpoint, 'cmdb_ci_server', `name=${name}`, [
|
|
89
|
+
'name',
|
|
90
|
+
'assigned_to',
|
|
91
|
+
'support_group',
|
|
92
|
+
'managed_by',
|
|
93
|
+
'owned_by',
|
|
94
|
+
]);
|
|
95
|
+
const data = await snowFetch(url, config, credentials, fetchFn);
|
|
96
|
+
return data.result;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const ciRelationships: IntegrationProbeHandler = async (params, config, credentials, fetchFn) => {
|
|
100
|
+
const name = params?.name as string;
|
|
101
|
+
if (!name) throw new Error('name parameter is required');
|
|
102
|
+
const sysId = await lookupSysId(name, config, credentials, fetchFn);
|
|
103
|
+
const url = buildUrl(config.endpoint, 'cmdb_rel_ci', `parent=${sysId}^ORchild=${sysId}`);
|
|
104
|
+
const data = await snowFetch(url, config, credentials, fetchFn);
|
|
105
|
+
return data.result;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const changesRecent: IntegrationProbeHandler = async (params, config, credentials, fetchFn) => {
|
|
109
|
+
const name = params?.name as string;
|
|
110
|
+
if (!name) throw new Error('name parameter is required');
|
|
111
|
+
const days = (params?.days as number) || 7;
|
|
112
|
+
const sysId = await lookupSysId(name, config, credentials, fetchFn);
|
|
113
|
+
const url = buildUrl(
|
|
114
|
+
config.endpoint,
|
|
115
|
+
'change_request',
|
|
116
|
+
`cmdb_ci=${sysId}^sys_created_on>=javascript:gs.daysAgoStart(${days})`,
|
|
117
|
+
);
|
|
118
|
+
const data = await snowFetch(url, config, credentials, fetchFn);
|
|
119
|
+
return data.result;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const incidentsOpen: IntegrationProbeHandler = async (params, config, credentials, fetchFn) => {
|
|
123
|
+
const name = params?.name as string;
|
|
124
|
+
if (!name) throw new Error('name parameter is required');
|
|
125
|
+
const url = buildUrl(config.endpoint, 'incident', `cmdb_ci.name=${name}^stateNOT IN6,7,8`);
|
|
126
|
+
const data = await snowFetch(url, config, credentials, fetchFn);
|
|
127
|
+
return data.result;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const serviceHealth: IntegrationProbeHandler = async (params, config, credentials, fetchFn) => {
|
|
131
|
+
const serviceName = params?.service_name as string;
|
|
132
|
+
if (!serviceName) throw new Error('service_name parameter is required');
|
|
133
|
+
// Get the service CI
|
|
134
|
+
const serviceUrl = buildUrl(config.endpoint, 'cmdb_ci_service', `name=${serviceName}`);
|
|
135
|
+
const serviceData = await snowFetch(serviceUrl, config, credentials, fetchFn);
|
|
136
|
+
const services = serviceData.result as Array<{
|
|
137
|
+
sys_id: string;
|
|
138
|
+
name: string;
|
|
139
|
+
operational_status: string;
|
|
140
|
+
}>;
|
|
141
|
+
const service = services[0];
|
|
142
|
+
if (!service) throw new Error(`Service not found: ${serviceName}`);
|
|
143
|
+
// Get child CIs via relationships
|
|
144
|
+
const relUrl = buildUrl(config.endpoint, 'cmdb_rel_ci', `parent=${service.sys_id}`);
|
|
145
|
+
const relData = await snowFetch(relUrl, config, credentials, fetchFn);
|
|
146
|
+
return { service, children: relData.result };
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const ciLifecycle: IntegrationProbeHandler = async (params, config, credentials, fetchFn) => {
|
|
150
|
+
const name = params?.name as string;
|
|
151
|
+
if (!name) throw new Error('name parameter is required');
|
|
152
|
+
const url = buildUrl(config.endpoint, 'cmdb_ci_server', `name=${name}`, [
|
|
153
|
+
'name',
|
|
154
|
+
'install_date',
|
|
155
|
+
'warranty_expiration',
|
|
156
|
+
'end_of_life',
|
|
157
|
+
'asset_tag',
|
|
158
|
+
'model_id',
|
|
159
|
+
]);
|
|
160
|
+
const data = await snowFetch(url, config, credentials, fetchFn);
|
|
161
|
+
return data.result;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const servicenowPack: IntegrationPack = {
|
|
165
|
+
manifest: {
|
|
166
|
+
name: 'servicenow',
|
|
167
|
+
type: 'integration',
|
|
168
|
+
version: '0.1.0',
|
|
169
|
+
description:
|
|
170
|
+
'ServiceNow CMDB integration for CI lookup, ownership, incidents, and change tracking',
|
|
171
|
+
requires: { groups: [], files: [], commands: [] },
|
|
172
|
+
probes: [
|
|
173
|
+
{
|
|
174
|
+
name: 'ci.lookup',
|
|
175
|
+
description: 'Look up a configuration item by name or IP address',
|
|
176
|
+
capability: 'observe',
|
|
177
|
+
params: {
|
|
178
|
+
query: {
|
|
179
|
+
type: 'string',
|
|
180
|
+
description: 'CI name or IP address to search for',
|
|
181
|
+
required: true,
|
|
182
|
+
},
|
|
183
|
+
type: {
|
|
184
|
+
type: 'string',
|
|
185
|
+
description: 'CI type (e.g. server, computer, linux_server). Defaults to server',
|
|
186
|
+
required: false,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
timeout: 15000,
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: 'ci.owner',
|
|
193
|
+
description: 'Get ownership and support group information for a CI',
|
|
194
|
+
capability: 'observe',
|
|
195
|
+
params: {
|
|
196
|
+
name: { type: 'string', description: 'CI name', required: true },
|
|
197
|
+
},
|
|
198
|
+
timeout: 15000,
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: 'ci.relationships',
|
|
202
|
+
description: 'Get all upstream and downstream relationships for a CI',
|
|
203
|
+
capability: 'observe',
|
|
204
|
+
params: {
|
|
205
|
+
name: { type: 'string', description: 'CI name', required: true },
|
|
206
|
+
},
|
|
207
|
+
timeout: 15000,
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'changes.recent',
|
|
211
|
+
description: 'Get recent change requests associated with a CI',
|
|
212
|
+
capability: 'observe',
|
|
213
|
+
params: {
|
|
214
|
+
name: { type: 'string', description: 'CI name', required: true },
|
|
215
|
+
days: {
|
|
216
|
+
type: 'number',
|
|
217
|
+
description: 'Number of days to look back (default: 7)',
|
|
218
|
+
required: false,
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
timeout: 15000,
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: 'incidents.open',
|
|
225
|
+
description: 'Get open incidents associated with a CI',
|
|
226
|
+
capability: 'observe',
|
|
227
|
+
params: {
|
|
228
|
+
name: { type: 'string', description: 'CI name', required: true },
|
|
229
|
+
},
|
|
230
|
+
timeout: 15000,
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: 'service.health',
|
|
234
|
+
description: 'Get a business service and its child CIs',
|
|
235
|
+
capability: 'observe',
|
|
236
|
+
params: {
|
|
237
|
+
service_name: { type: 'string', description: 'Business service name', required: true },
|
|
238
|
+
},
|
|
239
|
+
timeout: 15000,
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: 'ci.lifecycle',
|
|
243
|
+
description: 'Get lifecycle and asset information for a CI',
|
|
244
|
+
capability: 'observe',
|
|
245
|
+
params: {
|
|
246
|
+
name: { type: 'string', description: 'CI name', required: true },
|
|
247
|
+
},
|
|
248
|
+
timeout: 15000,
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
runbook: {
|
|
252
|
+
category: 'cmdb',
|
|
253
|
+
probes: ['ci.lookup', 'ci.owner', 'incidents.open'],
|
|
254
|
+
parallel: false,
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
handlers: {
|
|
259
|
+
'ci.lookup': ciLookup,
|
|
260
|
+
'ci.owner': ciOwner,
|
|
261
|
+
'ci.relationships': ciRelationships,
|
|
262
|
+
'changes.recent': changesRecent,
|
|
263
|
+
'incidents.open': incidentsOpen,
|
|
264
|
+
'service.health': serviceHealth,
|
|
265
|
+
'ci.lifecycle': ciLifecycle,
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
testConnection: async (config, credentials, fetchFn) => {
|
|
269
|
+
const url = buildUrl(config.endpoint, 'sys_properties', undefined, ['name']);
|
|
270
|
+
const parsed = new URL(url);
|
|
271
|
+
parsed.searchParams.set('sysparm_limit', '1');
|
|
272
|
+
const headers: Record<string, string> = {
|
|
273
|
+
Accept: 'application/json',
|
|
274
|
+
...buildAuthHeaders(credentials),
|
|
275
|
+
...config.headers,
|
|
276
|
+
};
|
|
277
|
+
const res = await fetchFn(parsed.toString(), { headers });
|
|
278
|
+
return res.ok;
|
|
279
|
+
},
|
|
280
|
+
};
|