MS Teams integrations

This commit is contained in:
2026-06-07 23:32:31 -05:00
parent 3b56fb5c61
commit c164395915
8 changed files with 416 additions and 3 deletions

View File

@@ -1387,6 +1387,61 @@ async function main() {
await new Promise((resolve) => hookServer.close(resolve));
}
// Microsoft Teams integration: posts alert digests to a Teams webhook (Adaptive Card / MessageCard).
const teamsHits = [];
const teamsServer = http.createServer((req, res) => {
let raw = '';
req.on('data', (chunk) => { raw += chunk; });
req.on('end', () => {
teamsHits.push({ raw, body: JSON.parse(raw || '{}') });
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end('1');
});
});
await new Promise((resolve) => teamsServer.listen(4300, '127.0.0.1', resolve));
try {
const teamsUrl = 'http://127.0.0.1:4300/teams';
const savedTeams = await request('/api/teams/settings', {
method: 'PUT', headers: auth,
body: JSON.stringify({ teams_enabled: true, teams_webhook_url: teamsUrl, teams_format: 'adaptive', teams_min_severity: 'info' })
});
if (savedTeams.settings.teams_webhook_url !== teamsUrl || savedTeams.settings.teams_format !== 'adaptive' || !savedTeams.settings.teams_enabled) {
throw new Error('Teams settings did not persist');
}
// Test card uses the Adaptive Card (Workflows) envelope.
const teamsTest = await request('/api/teams/test', { method: 'POST', headers: auth, body: JSON.stringify({}) });
if (!teamsTest.ok) throw new Error('Teams test card was not delivered');
const adaptiveHit = teamsHits.find((h) => h.body.type === 'message');
if (!adaptiveHit || !Array.isArray(adaptiveHit.body.attachments) ||
adaptiveHit.body.attachments[0].contentType !== 'application/vnd.microsoft.card.adaptive' ||
adaptiveHit.body.attachments[0].content.type !== 'AdaptiveCard') {
throw new Error('Teams adaptive card envelope was malformed');
}
// Disable the (now-closed) webhook so the next cycle is handled by Teams, then raise a fresh alert.
await request('/api/notifications/settings', { method: 'PUT', headers: auth, body: JSON.stringify({ webhook_enabled: false }) });
await request(`/api/assets/${assetId}/tasks`, {
method: 'POST', headers: auth,
body: JSON.stringify({ title: `Teams overdue ${suffix}`, type: 'inspection', due_date: '2019-02-02' })
});
const teamsCycle = await request('/api/alerts/run', { method: 'POST', headers: auth });
if (!teamsCycle.teamsPosted || !(teamsCycle.teamsDelivered > 0)) throw new Error('Alert cycle did not post alerts to Teams');
const digestHit = teamsHits.find((h) => h.body.type === 'message' && JSON.stringify(h.body).includes(`Teams overdue ${suffix}`));
if (!digestHit) throw new Error('Teams did not receive the alert digest card');
// Legacy MessageCard format produces a different envelope.
await request('/api/teams/settings', { method: 'PUT', headers: auth, body: JSON.stringify({ teams_format: 'messagecard' }) });
const mcTest = await request('/api/teams/test', { method: 'POST', headers: auth, body: JSON.stringify({}) });
if (!mcTest.ok) throw new Error('Teams MessageCard test was not delivered');
if (!teamsHits.some((h) => h.body['@type'] === 'MessageCard')) throw new Error('Teams MessageCard payload was not produced');
// Disable Teams so it doesn't interfere with later alert assertions.
await request('/api/teams/settings', { method: 'PUT', headers: auth, body: JSON.stringify({ teams_enabled: false }) });
} finally {
await new Promise((resolve) => teamsServer.close(resolve));
}
// ServiceNow integration: incident push (Table API) + CMDB asset pull, against a mock instance.
const snHits = [];
const snServer = http.createServer((req, res) => {