Introduces integration-based webhook registration, message parsing, dry-run monitoring, JSM ticket creation, and OAuth token refresh for DC Ops spaces. Co-authored-by: Cursor <cursoragent@cursor.com>
153 lines
5.1 KiB
JavaScript
153 lines
5.1 KiB
JavaScript
import fetch from 'node-fetch';
|
|
|
|
const WEBHOOKS_URL = 'https://webexapis.com/v1/webhooks';
|
|
|
|
export function findWebhookForRoom(webhooks, { targetUrl, roomId }) {
|
|
const filter = `roomId=${roomId}`;
|
|
return (webhooks || []).find(webhook =>
|
|
webhook.targetUrl === targetUrl
|
|
&& webhook.resource === 'messages'
|
|
&& webhook.event === 'created'
|
|
&& webhook.filter === filter
|
|
) || null;
|
|
}
|
|
|
|
export function createWebexWebhookManager({ webexOAuth, log }) {
|
|
async function listWebhooks(accessToken) {
|
|
const response = await fetch(WEBHOOKS_URL, {
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
Accept: 'application/json',
|
|
},
|
|
});
|
|
|
|
const responseText = await response.text();
|
|
let data;
|
|
|
|
try {
|
|
data = responseText ? JSON.parse(responseText) : {};
|
|
} catch {
|
|
throw new Error(`Invalid webhooks list response: ${responseText}`);
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const error = new Error(data.message || responseText || 'Failed to list webhooks');
|
|
error.status = response.status;
|
|
throw error;
|
|
}
|
|
|
|
return data.items || [];
|
|
}
|
|
|
|
async function createWebhook(accessToken, payload) {
|
|
const response = await fetch(WEBHOOKS_URL, {
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
Accept: 'application/json',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(payload),
|
|
});
|
|
|
|
const responseText = await response.text();
|
|
let data;
|
|
|
|
try {
|
|
data = responseText ? JSON.parse(responseText) : {};
|
|
} catch {
|
|
throw new Error(`Invalid webhook create response: ${responseText}`);
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const error = new Error(data.message || responseText || 'Failed to create webhook');
|
|
error.status = response.status;
|
|
error.details = data;
|
|
throw error;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
async function updateWebhook(accessToken, webhookId, payload) {
|
|
const response = await fetch(`${WEBHOOKS_URL}/${encodeURIComponent(webhookId)}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
Accept: 'application/json',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(payload),
|
|
});
|
|
|
|
const responseText = await response.text();
|
|
let data;
|
|
|
|
try {
|
|
data = responseText ? JSON.parse(responseText) : {};
|
|
} catch {
|
|
throw new Error(`Invalid webhook update response: ${responseText}`);
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const error = new Error(data.message || responseText || 'Failed to update webhook');
|
|
error.status = response.status;
|
|
error.details = data;
|
|
throw error;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
async function ensureWebhooks({ accessToken, webexRooms, config }) {
|
|
if (!accessToken) {
|
|
throw new Error('Integration access token is required to manage webhooks');
|
|
}
|
|
|
|
const targetUrl = config.webex.webhookTargetUrl;
|
|
const secret = config.webex.webhookSecret;
|
|
const existingWebhooks = await listWebhooks(accessToken);
|
|
const results = [];
|
|
|
|
for (const [configKey, room] of Object.entries(webexRooms || {})) {
|
|
if (room.enabled === false || !room.roomId) {
|
|
continue;
|
|
}
|
|
|
|
const filter = `roomId=${room.roomId}`;
|
|
const payload = {
|
|
name: `jiracloud-${configKey}`,
|
|
targetUrl,
|
|
resource: 'messages',
|
|
event: 'created',
|
|
filter,
|
|
secret,
|
|
};
|
|
|
|
let webhook = findWebhookForRoom(existingWebhooks, { targetUrl, roomId: room.roomId });
|
|
|
|
if (!webhook) {
|
|
webhook = await createWebhook(accessToken, payload);
|
|
log.logger('webexWebhookManager', `Created webhook ${webhook.id} for room ${configKey}`);
|
|
results.push({ configKey, roomId: room.roomId, action: 'created', webhookId: webhook.id });
|
|
} else if (webhook.status !== 'active') {
|
|
webhook = await updateWebhook(accessToken, webhook.id, { status: 'active' });
|
|
log.logger('webexWebhookManager', `Reactivated webhook ${webhook.id} for room ${configKey}`);
|
|
results.push({ configKey, roomId: room.roomId, action: 'reactivated', webhookId: webhook.id });
|
|
} else {
|
|
log.logger('webexWebhookManager', `Webhook already active for room ${configKey} (${webhook.id})`);
|
|
results.push({ configKey, roomId: room.roomId, action: 'exists', webhookId: webhook.id });
|
|
}
|
|
|
|
webexOAuth.setWebhookId(room.roomId, webhook.id);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
return {
|
|
ensureWebhooks,
|
|
findWebhookForRoom,
|
|
listWebhooks,
|
|
};
|
|
}
|