/home/techb158/cosmic-risk.abdallabala.com/src/integrations/pm-clients
Edit: /home/techb158/cosmic-risk.abdallabala.com/src/integrations/pm-clients/asana-client.js (3249B)
const { fetchJson, buildRiskDescription } = require("./http-client");
class AsanaClient {
constructor(options = {}) {
this.accessToken = options.accessToken;
this.projectGid = options.projectGid;
this.fetchImpl = options.fetchImpl;
if (!this.accessToken) {
throw new Error("Asana live sync requires accessToken.");
}
}
headers() {
return { "Authorization": `Bearer ${this.accessToken}`, "Accept": "application/json", "Content-Type": "application/json" };
}
async testConnection() {
const result = await fetchJson("https://app.asana.com/api/1.0/users/me", { headers: this.headers() }, this.fetchImpl);
const user = result.data || result;
return { ok: true, provider: "ASANA", account: user.name || user.email || user.gid };
}
async createRiskWorkItem(risk, mitigations = [], integration = {}) {
const projectGid = integration.externalProjectKey || this.projectGid;
if (!projectGid) {
throw new Error("Asana live sync requires externalProjectKey or credentials.projectGid.");
}
const result = await fetchJson("https://app.asana.com/api/1.0/tasks", {
method: "POST",
headers: this.headers(),
body: JSON.stringify({ data: { name: `[COSMIC Risk] ${risk.title}`, notes: buildRiskDescription(risk, mitigations), projects: [projectGid] } })
}, this.fetchImpl);
const task = result.data || result;
return { externalId: task.gid, externalKey: task.gid, externalUrl: task.permalink_url || "", externalStatus: "Created" };
}
async fetchWorkItems(options = {}) {
const projectGid = options.projectGid || this.projectGid;
if (!projectGid) {
throw new Error("Asana fetchWorkItems requires externalProjectKey or credentials.projectGid.");
}
const limit = options.limit || 100;
const result = await fetchJson(`https://app.asana.com/api/1.0/projects/${encodeURIComponent(projectGid)}/tasks?limit=${limit}&opt_fields=gid,name,notes,due_on,due_at,assignee,completed,created_at,modified_at,permalink_url`, { headers: this.headers() }, this.fetchImpl);
const tasks = result.data || [];
return tasks.map(task => ({
id: task.gid,
title: task.name || "",
description: task.notes || "",
dueDate: task.due_on || task.due_at || null,
assignee: task.assignee ? { gid: task.assignee.gid, name: task.assignee.name } : null,
completed: task.completed || false,
externalUrl: task.permalink_url || "",
raw: task
}));
}
async discoverResources() { return []; }
async updateRiskWorkItem(mapping, risk, mitigations = []) {
const result = await fetchJson(`https://app.asana.com/api/1.0/tasks/${encodeURIComponent(mapping.externalItemId)}`, {
method: "PUT",
headers: this.headers(),
body: JSON.stringify({ data: { name: `[COSMIC Risk] ${risk.title}`, notes: buildRiskDescription(risk, mitigations) } })
}, this.fetchImpl);
const task = result.data || result;
return {
externalId: task.gid || mapping.externalItemId,
externalKey: task.gid || mapping.externalItemKey,
externalUrl: task.permalink_url || mapping.externalUrl,
externalStatus: "Updated"
};
}
}
module.exports = { AsanaClient };