/home/techb158/cosmic-risk.abdallabala.com/src/services
Edit: /home/techb158/cosmic-risk.abdallabala.com/src/services/import-service.js (6195B)
const { prisma } = require("../lib/prisma");
const { loadIntegrationCredentials } = require("./integration-service");
const { createPmClient, fetchIntegrationWorkItems } = require("../integrations/pm-clients");
const { externalStatusToLocalStatus, parseDimensionFromLabels, parsePhaseFromLabels } = require("../domain/integrations");
const { writeAuditEvent } = require("./audit-service");
async function getImportPreview(integrationId) {
const integration = await prisma.integration.findUnique({
where: { id: integrationId },
include: { workspace: { include: { projects: { take: 1 } } } }
});
if (!integration) {
const error = new Error(`Integration not found: ${integrationId}`);
error.status = 404;
throw error;
}
const credentials = await loadIntegrationCredentials(integration);
const items = await fetchIntegrationWorkItems(integration.provider, credentials, {
[integration.provider === "TRELLO" ? "listId" :
integration.provider === "JIRA" ? "projectKey" :
integration.provider === "ASANA" ? "projectGid" :
"planId"]: integration.externalProjectKey
});
const existingMappings = await prisma.externalWorkItemMapping.findMany({
where: { integrationId },
select: { externalItemId: true }
});
const existingIds = new Set(existingMappings.map(m => m.externalItemId));
const newItems = items.filter(item => !existingIds.has(item.id));
const existingItems = items.filter(item => existingIds.has(item.id));
return {
provider: integration.provider,
totalItems: items.length,
newItems: newItems.length,
existingItems: existingItems.length,
preview: newItems.map(item => ({
externalId: item.id,
title: item.title,
description: (item.description || "").slice(0, 200),
status: item.status || item.completed ? "Completed" : "Open",
dueDate: item.dueDate || null,
willCreate: true
}))
};
}
async function pullFromProvider(integrationId, actor, request) {
const integration = await prisma.integration.findUnique({
where: { id: integrationId },
include: { workspace: { include: { projects: { take: 1 } } } }
});
if (!integration) {
const error = new Error(`Integration not found: ${integrationId}`);
error.status = 404;
throw error;
}
const project = integration.workspace.projects[0];
if (!project) {
const error = new Error("Workspace has no projects. Create a project before importing.");
error.status = 400;
throw error;
}
const credentials = await loadIntegrationCredentials(integration);
const items = await fetchIntegrationWorkItems(integration.provider, credentials, {
[integration.provider === "TRELLO" ? "listId" :
integration.provider === "JIRA" ? "projectKey" :
integration.provider === "ASANA" ? "projectGid" :
"planId"]: integration.externalProjectKey
});
const existingMappings = await prisma.externalWorkItemMapping.findMany({
where: { integrationId },
select: { externalItemId: true }
});
const existingIds = new Set(existingMappings.map(m => m.externalItemId));
const startedAt = new Date();
let createdCount = 0;
let skippedCount = 0;
let failedCount = 0;
const failureLog = [];
for (const item of items) {
if (existingIds.has(item.id)) {
skippedCount++;
continue;
}
const labels = item.labels || [];
const dimension = parseDimensionFromLabels(labels);
const lifecyclePhase = parsePhaseFromLabels(labels);
const externalStatus = item.status || (item.completed ? "Completed" : "Open");
const localStatus = externalStatusToLocalStatus(integration.provider, externalStatus);
try {
const risk = await prisma.risk.create({
data: {
projectId: project.id,
title: item.title || "Imported item",
description: item.description || "",
dimension,
domain: "Technical risks",
lifecyclePhase,
probability: 3,
impact: 3,
detectability: 3,
status: localStatus,
ownerDisplayName: item.assignee?.displayName || item.assignee?.name || null,
dueDate: item.dueDate ? new Date(item.dueDate) : null
}
});
await prisma.externalWorkItemMapping.create({
data: {
integrationId,
projectId: project.id,
localEntityType: "Risk",
localEntityId: risk.id,
localTitle: risk.title,
externalItemType: integration.provider === "TRELLO" ? "Card" : integration.provider === "JIRA" ? "Issue" : "Task",
externalItemId: item.id,
externalItemKey: item.key || item.id,
externalUrl: item.externalUrl || null,
externalStatus,
syncStatus: "Imported",
lastSyncedAt: new Date(),
fieldMapping: { source: "pull-import", externalItem: { id: item.id, title: item.title } }
}
});
createdCount++;
} catch (err) {
failedCount++;
failureLog.push({ itemId: item.id, title: item.title, error: err.message });
}
}
const finishedAt = new Date();
const syncRun = await prisma.integrationSyncRun.create({
data: {
integrationId,
projectId: project.id,
provider: integration.provider,
status: failedCount > 0 ? "Partial" : "Completed",
startedAt,
finishedAt,
createdCount,
updatedCount: 0,
failedCount,
summary: `Pull import: ${createdCount} created, ${skippedCount} skipped, ${failedCount} failed.`,
failureLog
}
});
await prisma.integration.update({
where: { id: integrationId },
data: { connectionStatus: "CONNECTED", lastSyncAt: new Date() }
});
await writeAuditEvent({
organizationId: integration.workspace.organizationId,
workspaceId: integration.workspaceId,
actorUserId: actor?.id || null,
entityType: "IntegrationSyncRun",
entityId: syncRun.id,
action: "pull-import",
afterJson: { status: syncRun.status, createdCount, skippedCount, failedCount },
request
});
return { syncRun, createdCount, skippedCount, failedCount };
}
module.exports = {
getImportPreview,
pullFromProvider
};