eggi-ai-db-schema-2 12.54.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/CHANGELOG.md +750 -0
- package/README.md +655 -0
- package/dist/config/database.d.ts +28 -0
- package/dist/config/database.d.ts.map +1 -0
- package/dist/config/database.js +72 -0
- package/dist/config/database.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +199 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/database-service.d.ts +689 -0
- package/dist/lib/database-service.d.ts.map +1 -0
- package/dist/lib/database-service.js +1362 -0
- package/dist/lib/database-service.js.map +1 -0
- package/dist/lib/db-types.d.ts +167 -0
- package/dist/lib/db-types.d.ts.map +1 -0
- package/dist/lib/db-types.js +28 -0
- package/dist/lib/db-types.js.map +1 -0
- package/dist/lib/db.d.ts +58 -0
- package/dist/lib/db.d.ts.map +1 -0
- package/dist/lib/db.js +292 -0
- package/dist/lib/db.js.map +1 -0
- package/dist/lib/index.d.ts +11 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +26 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/pg-client.d.ts +50 -0
- package/dist/lib/pg-client.d.ts.map +1 -0
- package/dist/lib/pg-client.js +106 -0
- package/dist/lib/pg-client.js.map +1 -0
- package/dist/lib/schema.d.ts +298 -0
- package/dist/lib/schema.d.ts.map +1 -0
- package/dist/lib/schema.js +12 -0
- package/dist/lib/schema.js.map +1 -0
- package/dist/migration-manager.d.ts +49 -0
- package/dist/migration-manager.d.ts.map +1 -0
- package/dist/migration-manager.js +282 -0
- package/dist/migration-manager.js.map +1 -0
- package/dist/queries/minimal-connections.d.ts +31 -0
- package/dist/queries/minimal-connections.d.ts.map +1 -0
- package/dist/queries/minimal-connections.js +143 -0
- package/dist/queries/minimal-connections.js.map +1 -0
- package/dist/schema.ts +340 -0
- package/dist/seed.d.ts +8 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +40 -0
- package/dist/seed.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +23 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/types.d.ts +77 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/types.js +3 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils/authenticated-user-operations.d.ts +110 -0
- package/dist/utils/authenticated-user-operations.d.ts.map +1 -0
- package/dist/utils/authenticated-user-operations.js +292 -0
- package/dist/utils/authenticated-user-operations.js.map +1 -0
- package/dist/utils/authentication-operations.d.ts +48 -0
- package/dist/utils/authentication-operations.d.ts.map +1 -0
- package/dist/utils/authentication-operations.js +172 -0
- package/dist/utils/authentication-operations.js.map +1 -0
- package/dist/utils/company-mapping-job-operations.d.ts +103 -0
- package/dist/utils/company-mapping-job-operations.d.ts.map +1 -0
- package/dist/utils/company-mapping-job-operations.js +413 -0
- package/dist/utils/company-mapping-job-operations.js.map +1 -0
- package/dist/utils/company-sheet-upload-operations.d.ts +53 -0
- package/dist/utils/company-sheet-upload-operations.d.ts.map +1 -0
- package/dist/utils/company-sheet-upload-operations.js +135 -0
- package/dist/utils/company-sheet-upload-operations.js.map +1 -0
- package/dist/utils/contact-operations.d.ts +70 -0
- package/dist/utils/contact-operations.d.ts.map +1 -0
- package/dist/utils/contact-operations.js +294 -0
- package/dist/utils/contact-operations.js.map +1 -0
- package/dist/utils/forager-linkedin-operations.d.ts +74 -0
- package/dist/utils/forager-linkedin-operations.d.ts.map +1 -0
- package/dist/utils/forager-linkedin-operations.js +778 -0
- package/dist/utils/forager-linkedin-operations.js.map +1 -0
- package/dist/utils/ghost-genius-linkedin-operations.d.ts +23 -0
- package/dist/utils/ghost-genius-linkedin-operations.d.ts.map +1 -0
- package/dist/utils/ghost-genius-linkedin-operations.js +282 -0
- package/dist/utils/ghost-genius-linkedin-operations.js.map +1 -0
- package/dist/utils/index.d.ts +29 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +77 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/introduction-request-operations.d.ts +159 -0
- package/dist/utils/introduction-request-operations.d.ts.map +1 -0
- package/dist/utils/introduction-request-operations.js +481 -0
- package/dist/utils/introduction-request-operations.js.map +1 -0
- package/dist/utils/invitation-operations.d.ts +141 -0
- package/dist/utils/invitation-operations.d.ts.map +1 -0
- package/dist/utils/invitation-operations.js +749 -0
- package/dist/utils/invitation-operations.js.map +1 -0
- package/dist/utils/linkedin-account-operations.d.ts +45 -0
- package/dist/utils/linkedin-account-operations.d.ts.map +1 -0
- package/dist/utils/linkedin-account-operations.js +279 -0
- package/dist/utils/linkedin-account-operations.js.map +1 -0
- package/dist/utils/linkedin-account-relationship-operations.d.ts +77 -0
- package/dist/utils/linkedin-account-relationship-operations.d.ts.map +1 -0
- package/dist/utils/linkedin-account-relationship-operations.js +274 -0
- package/dist/utils/linkedin-account-relationship-operations.js.map +1 -0
- package/dist/utils/linkedin-data-operations.d.ts +102 -0
- package/dist/utils/linkedin-data-operations.d.ts.map +1 -0
- package/dist/utils/linkedin-data-operations.js +613 -0
- package/dist/utils/linkedin-data-operations.js.map +1 -0
- package/dist/utils/linkedin-identifier-utils.d.ts +31 -0
- package/dist/utils/linkedin-identifier-utils.d.ts.map +1 -0
- package/dist/utils/linkedin-identifier-utils.js +63 -0
- package/dist/utils/linkedin-identifier-utils.js.map +1 -0
- package/dist/utils/linkedin-profile-cache.d.ts +131 -0
- package/dist/utils/linkedin-profile-cache.d.ts.map +1 -0
- package/dist/utils/linkedin-profile-cache.js +418 -0
- package/dist/utils/linkedin-profile-cache.js.map +1 -0
- package/dist/utils/llm-inference-job-operations.d.ts +116 -0
- package/dist/utils/llm-inference-job-operations.d.ts.map +1 -0
- package/dist/utils/llm-inference-job-operations.js +266 -0
- package/dist/utils/llm-inference-job-operations.js.map +1 -0
- package/dist/utils/mapping-job-operations.d.ts +272 -0
- package/dist/utils/mapping-job-operations.d.ts.map +1 -0
- package/dist/utils/mapping-job-operations.js +833 -0
- package/dist/utils/mapping-job-operations.js.map +1 -0
- package/dist/utils/mapping-operations.d.ts +80 -0
- package/dist/utils/mapping-operations.d.ts.map +1 -0
- package/dist/utils/mapping-operations.js +318 -0
- package/dist/utils/mapping-operations.js.map +1 -0
- package/dist/utils/on-demand-mapping-operations.d.ts +199 -0
- package/dist/utils/on-demand-mapping-operations.d.ts.map +1 -0
- package/dist/utils/on-demand-mapping-operations.js +728 -0
- package/dist/utils/on-demand-mapping-operations.js.map +1 -0
- package/dist/utils/onboarding-operations.d.ts +53 -0
- package/dist/utils/onboarding-operations.d.ts.map +1 -0
- package/dist/utils/onboarding-operations.js +223 -0
- package/dist/utils/onboarding-operations.js.map +1 -0
- package/dist/utils/organization-assignment-job-operations.d.ts +258 -0
- package/dist/utils/organization-assignment-job-operations.d.ts.map +1 -0
- package/dist/utils/organization-assignment-job-operations.js +881 -0
- package/dist/utils/organization-assignment-job-operations.js.map +1 -0
- package/dist/utils/organization-assignment-operations.d.ts +59 -0
- package/dist/utils/organization-assignment-operations.d.ts.map +1 -0
- package/dist/utils/organization-assignment-operations.js +130 -0
- package/dist/utils/organization-assignment-operations.js.map +1 -0
- package/dist/utils/organization-operations.d.ts +275 -0
- package/dist/utils/organization-operations.d.ts.map +1 -0
- package/dist/utils/organization-operations.js +993 -0
- package/dist/utils/organization-operations.js.map +1 -0
- package/dist/utils/organization-relationship-operations.d.ts +59 -0
- package/dist/utils/organization-relationship-operations.d.ts.map +1 -0
- package/dist/utils/organization-relationship-operations.js +240 -0
- package/dist/utils/organization-relationship-operations.js.map +1 -0
- package/dist/utils/quota-operations.d.ts +107 -0
- package/dist/utils/quota-operations.d.ts.map +1 -0
- package/dist/utils/quota-operations.js +692 -0
- package/dist/utils/quota-operations.js.map +1 -0
- package/dist/utils/recursive-mapping-job-operations.d.ts +42 -0
- package/dist/utils/recursive-mapping-job-operations.d.ts.map +1 -0
- package/dist/utils/recursive-mapping-job-operations.js +169 -0
- package/dist/utils/recursive-mapping-job-operations.js.map +1 -0
- package/dist/utils/relationship-operations.d.ts +130 -0
- package/dist/utils/relationship-operations.d.ts.map +1 -0
- package/dist/utils/relationship-operations.js +329 -0
- package/dist/utils/relationship-operations.js.map +1 -0
- package/dist/utils/sales-pipeline-operations.d.ts +143 -0
- package/dist/utils/sales-pipeline-operations.d.ts.map +1 -0
- package/dist/utils/sales-pipeline-operations.js +649 -0
- package/dist/utils/sales-pipeline-operations.js.map +1 -0
- package/dist/utils/skills-operations.d.ts +117 -0
- package/dist/utils/skills-operations.d.ts.map +1 -0
- package/dist/utils/skills-operations.js +487 -0
- package/dist/utils/skills-operations.js.map +1 -0
- package/dist/utils/subscription-operations.d.ts +123 -0
- package/dist/utils/subscription-operations.d.ts.map +1 -0
- package/dist/utils/subscription-operations.js +391 -0
- package/dist/utils/subscription-operations.js.map +1 -0
- package/dist/utils/unipile-account-operations.d.ts +96 -0
- package/dist/utils/unipile-account-operations.d.ts.map +1 -0
- package/dist/utils/unipile-account-operations.js +255 -0
- package/dist/utils/unipile-account-operations.js.map +1 -0
- package/dist/utils/user-industry-operations.d.ts +80 -0
- package/dist/utils/user-industry-operations.d.ts.map +1 -0
- package/dist/utils/user-industry-operations.js +237 -0
- package/dist/utils/user-industry-operations.js.map +1 -0
- package/dist/utils/user-operations.d.ts +87 -0
- package/dist/utils/user-operations.d.ts.map +1 -0
- package/dist/utils/user-operations.js +212 -0
- package/dist/utils/user-operations.js.map +1 -0
- package/package.json +98 -0
|
@@ -0,0 +1,881 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Organization Assignment Job Operations
|
|
4
|
+
*
|
|
5
|
+
* This module provides functions for managing organization assignment job lifecycle.
|
|
6
|
+
* These jobs track when the organization_assignments_handler Lambda processes
|
|
7
|
+
* contributor and relationship assignments to organizations.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.createOrganizationAssignmentJob = createOrganizationAssignmentJob;
|
|
11
|
+
exports.completeOrganizationAssignmentJob = completeOrganizationAssignmentJob;
|
|
12
|
+
exports.updateOrganizationAssignmentJobMetadata = updateOrganizationAssignmentJobMetadata;
|
|
13
|
+
exports.getOrganizationAssignmentJobsForMappingJob = getOrganizationAssignmentJobsForMappingJob;
|
|
14
|
+
exports.getOrganizationAssignmentJobsForOrganization = getOrganizationAssignmentJobsForOrganization;
|
|
15
|
+
exports.getPendingOrganizationAssignmentJobs = getPendingOrganizationAssignmentJobs;
|
|
16
|
+
exports.getOrganizationAssignmentJobById = getOrganizationAssignmentJobById;
|
|
17
|
+
exports.getLatestOrganizationAssignmentJob = getLatestOrganizationAssignmentJob;
|
|
18
|
+
exports.getLatestOrganizationAssignmentJobForUser = getLatestOrganizationAssignmentJobForUser;
|
|
19
|
+
exports.checkExportAvailability = checkExportAvailability;
|
|
20
|
+
exports.checkExportAvailabilityForLinkedInAccount = checkExportAvailabilityForLinkedInAccount;
|
|
21
|
+
exports.getUserExports = getUserExports;
|
|
22
|
+
exports.getExportStatusByLinkedInIdentifier = getExportStatusByLinkedInIdentifier;
|
|
23
|
+
exports.validateOrganizationAssignmentJobCompletion = validateOrganizationAssignmentJobCompletion;
|
|
24
|
+
exports.deleteOrganizationAssignmentJob = deleteOrganizationAssignmentJob;
|
|
25
|
+
const pg_client_1 = require("../lib/pg-client");
|
|
26
|
+
const db_1 = require("../lib/db");
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// CREATE OPERATIONS
|
|
29
|
+
// =============================================================================
|
|
30
|
+
/**
|
|
31
|
+
* Create a new organization assignment job record
|
|
32
|
+
* Called when the organization_assignments_handler Lambda starts processing
|
|
33
|
+
*
|
|
34
|
+
* @param db - Database instance
|
|
35
|
+
* @param params - Job parameters
|
|
36
|
+
* @returns The created job record
|
|
37
|
+
*/
|
|
38
|
+
async function createOrganizationAssignmentJob(db, params) {
|
|
39
|
+
const { mappingJobId, organizationId, jobMetadata = {} } = params;
|
|
40
|
+
(0, db_1.debugLogDbOperation)("insert", "organization_assignment_jobs", { mappingJobId, organizationId }, undefined, { operation: "createOrganizationAssignmentJob" });
|
|
41
|
+
const sql = `
|
|
42
|
+
INSERT INTO monitoring.organization_assignment_jobs (mapping_job_id, organization_id, metadata, created_at)
|
|
43
|
+
VALUES ($1, $2, $3, NOW())
|
|
44
|
+
RETURNING id, mapping_job_id, organization_id, metadata, created_at, completed_at
|
|
45
|
+
`;
|
|
46
|
+
const job = await (0, pg_client_1.queryOne)(db, sql, [mappingJobId, organizationId, JSON.stringify(jobMetadata)]);
|
|
47
|
+
if (!job) {
|
|
48
|
+
throw new Error("Failed to create organization assignment job");
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
id: job.id,
|
|
52
|
+
mappingJobId: job.mapping_job_id,
|
|
53
|
+
organizationId: job.organization_id,
|
|
54
|
+
metadata: job.metadata,
|
|
55
|
+
createdAt: job.created_at,
|
|
56
|
+
completedAt: job.completed_at,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// LIFECYCLE MANAGEMENT
|
|
61
|
+
// =============================================================================
|
|
62
|
+
/**
|
|
63
|
+
* Mark an organization assignment job as completed
|
|
64
|
+
* Updates completed_at timestamp and stores final statistics
|
|
65
|
+
*
|
|
66
|
+
* @param db - Database instance
|
|
67
|
+
* @param organizationAssignmentJobId - Job ID
|
|
68
|
+
* @param completionMetadata - Final job statistics
|
|
69
|
+
* @returns Updated job record
|
|
70
|
+
*/
|
|
71
|
+
async function completeOrganizationAssignmentJob(db, organizationAssignmentJobId, completionMetadata) {
|
|
72
|
+
(0, db_1.debugLogDbOperation)("update", "organization_assignment_jobs", { organizationAssignmentJobId }, undefined, { operation: "completeOrganizationAssignmentJob" });
|
|
73
|
+
const updates = ["completed_at = NOW()"];
|
|
74
|
+
const params = [organizationAssignmentJobId];
|
|
75
|
+
if (completionMetadata) {
|
|
76
|
+
updates.push("metadata = $2");
|
|
77
|
+
params.push(JSON.stringify(completionMetadata));
|
|
78
|
+
}
|
|
79
|
+
const sql = `
|
|
80
|
+
UPDATE monitoring.organization_assignment_jobs
|
|
81
|
+
SET ${updates.join(", ")}
|
|
82
|
+
WHERE id = $1
|
|
83
|
+
RETURNING id, mapping_job_id, organization_id, metadata, created_at, completed_at
|
|
84
|
+
`;
|
|
85
|
+
const job = await (0, pg_client_1.queryOne)(db, sql, params);
|
|
86
|
+
if (!job) {
|
|
87
|
+
throw new Error(`Organization assignment job ${organizationAssignmentJobId} not found`);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
id: job.id,
|
|
91
|
+
mappingJobId: job.mapping_job_id,
|
|
92
|
+
organizationId: job.organization_id,
|
|
93
|
+
metadata: job.metadata,
|
|
94
|
+
createdAt: job.created_at,
|
|
95
|
+
completedAt: job.completed_at,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Update job metadata (for incremental statistics during processing)
|
|
100
|
+
*
|
|
101
|
+
* @param db - Database instance
|
|
102
|
+
* @param organizationAssignmentJobId - Job ID
|
|
103
|
+
* @param metadata - Updated metadata
|
|
104
|
+
* @returns Updated job record
|
|
105
|
+
*/
|
|
106
|
+
async function updateOrganizationAssignmentJobMetadata(db, organizationAssignmentJobId, metadata) {
|
|
107
|
+
(0, db_1.debugLogDbOperation)("update", "organization_assignment_jobs", { organizationAssignmentJobId }, undefined, { operation: "updateOrganizationAssignmentJobMetadata" });
|
|
108
|
+
const sql = `
|
|
109
|
+
UPDATE monitoring.organization_assignment_jobs
|
|
110
|
+
SET metadata = $1
|
|
111
|
+
WHERE id = $2
|
|
112
|
+
RETURNING id, mapping_job_id, organization_id, metadata, created_at, completed_at
|
|
113
|
+
`;
|
|
114
|
+
const job = await (0, pg_client_1.queryOne)(db, sql, [JSON.stringify(metadata), organizationAssignmentJobId]);
|
|
115
|
+
if (!job) {
|
|
116
|
+
throw new Error(`Organization assignment job ${organizationAssignmentJobId} not found`);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
id: job.id,
|
|
120
|
+
mappingJobId: job.mapping_job_id,
|
|
121
|
+
organizationId: job.organization_id,
|
|
122
|
+
metadata: job.metadata,
|
|
123
|
+
createdAt: job.created_at,
|
|
124
|
+
completedAt: job.completed_at,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// =============================================================================
|
|
128
|
+
// QUERY OPERATIONS
|
|
129
|
+
// =============================================================================
|
|
130
|
+
/**
|
|
131
|
+
* Get all organization assignment jobs for a specific mapping job
|
|
132
|
+
*
|
|
133
|
+
* @param db - Database instance
|
|
134
|
+
* @param mappingJobId - Mapping job ID
|
|
135
|
+
* @param options - Query options
|
|
136
|
+
* @returns Array of organization assignment jobs
|
|
137
|
+
*/
|
|
138
|
+
async function getOrganizationAssignmentJobsForMappingJob(db, mappingJobId, options = {}) {
|
|
139
|
+
const { limit = 100, onlyCompleted = false } = options;
|
|
140
|
+
(0, db_1.debugLogDbOperation)("select", "organization_assignment_jobs", { mappingJobId, limit, onlyCompleted }, undefined, { operation: "getOrganizationAssignmentJobsForMappingJob" });
|
|
141
|
+
const whereClause = onlyCompleted
|
|
142
|
+
? "WHERE mapping_job_id = $1 AND completed_at IS NOT NULL"
|
|
143
|
+
: "WHERE mapping_job_id = $1";
|
|
144
|
+
const sql = `
|
|
145
|
+
SELECT id, mapping_job_id, organization_id, metadata, created_at, completed_at
|
|
146
|
+
FROM monitoring.organization_assignment_jobs
|
|
147
|
+
${whereClause}
|
|
148
|
+
ORDER BY created_at DESC
|
|
149
|
+
LIMIT $2
|
|
150
|
+
`;
|
|
151
|
+
const results = await (0, pg_client_1.query)(db, sql, [mappingJobId, limit]);
|
|
152
|
+
return results.map(r => ({
|
|
153
|
+
id: r.id,
|
|
154
|
+
mappingJobId: r.mapping_job_id,
|
|
155
|
+
organizationId: r.organization_id,
|
|
156
|
+
metadata: r.metadata,
|
|
157
|
+
createdAt: r.created_at,
|
|
158
|
+
completedAt: r.completed_at,
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get all organization assignment jobs for a specific organization
|
|
163
|
+
*
|
|
164
|
+
* @param db - Database instance
|
|
165
|
+
* @param organizationId - Organization ID
|
|
166
|
+
* @param options - Query options
|
|
167
|
+
* @returns Array of organization assignment jobs
|
|
168
|
+
*/
|
|
169
|
+
async function getOrganizationAssignmentJobsForOrganization(db, organizationId, options = {}) {
|
|
170
|
+
const { limit = 100, onlyCompleted = false } = options;
|
|
171
|
+
(0, db_1.debugLogDbOperation)("select", "organization_assignment_jobs", { organizationId, limit, onlyCompleted }, undefined, { operation: "getOrganizationAssignmentJobsForOrganization" });
|
|
172
|
+
const whereClause = onlyCompleted
|
|
173
|
+
? "WHERE organization_id = $1 AND completed_at IS NOT NULL"
|
|
174
|
+
: "WHERE organization_id = $1";
|
|
175
|
+
const sql = `
|
|
176
|
+
SELECT id, mapping_job_id, organization_id, metadata, created_at, completed_at
|
|
177
|
+
FROM monitoring.organization_assignment_jobs
|
|
178
|
+
${whereClause}
|
|
179
|
+
ORDER BY created_at DESC
|
|
180
|
+
LIMIT $2
|
|
181
|
+
`;
|
|
182
|
+
const results = await (0, pg_client_1.query)(db, sql, [organizationId, limit]);
|
|
183
|
+
return results.map(r => ({
|
|
184
|
+
id: r.id,
|
|
185
|
+
mappingJobId: r.mapping_job_id,
|
|
186
|
+
organizationId: r.organization_id,
|
|
187
|
+
metadata: r.metadata,
|
|
188
|
+
createdAt: r.created_at,
|
|
189
|
+
completedAt: r.completed_at,
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get pending organization assignment jobs (not completed)
|
|
194
|
+
*
|
|
195
|
+
* @param db - Database instance
|
|
196
|
+
* @param options - Query options
|
|
197
|
+
* @returns Array of pending jobs
|
|
198
|
+
*/
|
|
199
|
+
async function getPendingOrganizationAssignmentJobs(db, options = {}) {
|
|
200
|
+
const { limit = 100 } = options;
|
|
201
|
+
(0, db_1.debugLogDbOperation)("select", "organization_assignment_jobs", { limit }, undefined, {
|
|
202
|
+
operation: "getPendingOrganizationAssignmentJobs",
|
|
203
|
+
});
|
|
204
|
+
const sql = `
|
|
205
|
+
SELECT id, mapping_job_id, organization_id, metadata, created_at, completed_at
|
|
206
|
+
FROM monitoring.organization_assignment_jobs
|
|
207
|
+
WHERE completed_at IS NULL
|
|
208
|
+
ORDER BY created_at DESC
|
|
209
|
+
LIMIT $1
|
|
210
|
+
`;
|
|
211
|
+
const results = await (0, pg_client_1.query)(db, sql, [limit]);
|
|
212
|
+
return results.map(r => ({
|
|
213
|
+
id: r.id,
|
|
214
|
+
mappingJobId: r.mapping_job_id,
|
|
215
|
+
organizationId: r.organization_id,
|
|
216
|
+
metadata: r.metadata,
|
|
217
|
+
createdAt: r.created_at,
|
|
218
|
+
completedAt: r.completed_at,
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Get a specific organization assignment job by ID
|
|
223
|
+
*
|
|
224
|
+
* @param db - Database instance
|
|
225
|
+
* @param organizationAssignmentJobId - Job ID
|
|
226
|
+
* @returns Job record or null if not found
|
|
227
|
+
*/
|
|
228
|
+
async function getOrganizationAssignmentJobById(db, organizationAssignmentJobId) {
|
|
229
|
+
(0, db_1.debugLogDbOperation)("select", "organization_assignment_jobs", { organizationAssignmentJobId }, undefined, { operation: "getOrganizationAssignmentJobById" });
|
|
230
|
+
const sql = `
|
|
231
|
+
SELECT id, mapping_job_id, organization_id, metadata, created_at, completed_at
|
|
232
|
+
FROM monitoring.organization_assignment_jobs
|
|
233
|
+
WHERE id = $1
|
|
234
|
+
LIMIT 1
|
|
235
|
+
`;
|
|
236
|
+
const job = await (0, pg_client_1.queryOne)(db, sql, [organizationAssignmentJobId]);
|
|
237
|
+
if (!job)
|
|
238
|
+
return null;
|
|
239
|
+
return {
|
|
240
|
+
id: job.id,
|
|
241
|
+
mappingJobId: job.mapping_job_id,
|
|
242
|
+
organizationId: job.organization_id,
|
|
243
|
+
metadata: job.metadata,
|
|
244
|
+
createdAt: job.created_at,
|
|
245
|
+
completedAt: job.completed_at,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get the latest organization assignment job for a specific organization and mapping job
|
|
250
|
+
*
|
|
251
|
+
* @param db - Database instance
|
|
252
|
+
* @param mappingJobId - Mapping job ID
|
|
253
|
+
* @param organizationId - Organization ID
|
|
254
|
+
* @returns Latest job record or null if not found
|
|
255
|
+
*/
|
|
256
|
+
async function getLatestOrganizationAssignmentJob(db, mappingJobId, organizationId) {
|
|
257
|
+
(0, db_1.debugLogDbOperation)("select", "organization_assignment_jobs", { mappingJobId, organizationId }, undefined, { operation: "getLatestOrganizationAssignmentJob" });
|
|
258
|
+
const sql = `
|
|
259
|
+
SELECT id, mapping_job_id, organization_id, metadata, created_at, started_at, completed_at
|
|
260
|
+
FROM monitoring.organization_assignment_jobs
|
|
261
|
+
WHERE mapping_job_id = $1 AND organization_id = $2
|
|
262
|
+
ORDER BY created_at DESC
|
|
263
|
+
LIMIT 1
|
|
264
|
+
`;
|
|
265
|
+
const job = await (0, pg_client_1.queryOne)(db, sql, [mappingJobId, organizationId]);
|
|
266
|
+
if (!job)
|
|
267
|
+
return null;
|
|
268
|
+
return {
|
|
269
|
+
id: job.id,
|
|
270
|
+
mappingJobId: job.mapping_job_id,
|
|
271
|
+
organizationId: job.organization_id,
|
|
272
|
+
metadata: job.metadata,
|
|
273
|
+
createdAt: job.created_at,
|
|
274
|
+
completedAt: job.completed_at,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Get the latest organization assignment job for a user in an organization
|
|
279
|
+
*
|
|
280
|
+
* Finds the most recent organization assignment job for a user by joining
|
|
281
|
+
* through mapping_jobs and linkedin_accounts to find the user's mapping job.
|
|
282
|
+
*
|
|
283
|
+
* @param db - Database instance
|
|
284
|
+
* @param userId - User ID
|
|
285
|
+
* @param organizationId - Organization ID
|
|
286
|
+
* @returns Latest organization assignment job or null if not found
|
|
287
|
+
*/
|
|
288
|
+
async function getLatestOrganizationAssignmentJobForUser(db, userId, organizationId) {
|
|
289
|
+
(0, db_1.debugLogDbOperation)("select", "organization_assignment_jobs", { userId, organizationId }, undefined, { operation: "getLatestOrganizationAssignmentJobForUser" });
|
|
290
|
+
const sql = `
|
|
291
|
+
SELECT
|
|
292
|
+
oaj.id,
|
|
293
|
+
oaj.mapping_job_id,
|
|
294
|
+
oaj.organization_id,
|
|
295
|
+
oaj.metadata,
|
|
296
|
+
oaj.created_at,
|
|
297
|
+
oaj.completed_at
|
|
298
|
+
FROM monitoring.organization_assignment_jobs oaj
|
|
299
|
+
INNER JOIN monitoring.mapping_jobs mj ON mj.id = oaj.mapping_job_id
|
|
300
|
+
INNER JOIN linkedin.accounts la ON la.id = mj.linkedin_account_id
|
|
301
|
+
WHERE la.user_id = $1 AND oaj.organization_id = $2
|
|
302
|
+
ORDER BY oaj.created_at DESC
|
|
303
|
+
LIMIT 1
|
|
304
|
+
`;
|
|
305
|
+
const job = await (0, pg_client_1.queryOne)(db, sql, [userId, organizationId]);
|
|
306
|
+
if (!job)
|
|
307
|
+
return null;
|
|
308
|
+
return {
|
|
309
|
+
id: job.id,
|
|
310
|
+
mappingJobId: job.mapping_job_id,
|
|
311
|
+
organizationId: job.organization_id,
|
|
312
|
+
metadata: job.metadata,
|
|
313
|
+
createdAt: job.created_at,
|
|
314
|
+
completedAt: job.completed_at,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
async function checkExportAvailability(db, mappingJobId, organizationId) {
|
|
318
|
+
(0, db_1.debugLogDbOperation)("select", "organization_assignment_jobs", { mappingJobId, organizationId }, undefined, { operation: "checkExportAvailability" });
|
|
319
|
+
// If mapping job is not completed, export is not available
|
|
320
|
+
if (!mappingJobId) {
|
|
321
|
+
// Get organization name
|
|
322
|
+
const orgSql = `
|
|
323
|
+
SELECT name
|
|
324
|
+
FROM public.organizations
|
|
325
|
+
WHERE id = $1
|
|
326
|
+
`;
|
|
327
|
+
const org = await (0, pg_client_1.queryOne)(db, orgSql, [
|
|
328
|
+
organizationId,
|
|
329
|
+
]).catch(() => null);
|
|
330
|
+
return {
|
|
331
|
+
can_export: false,
|
|
332
|
+
is_exported: false,
|
|
333
|
+
export_status: "not_available",
|
|
334
|
+
organization_id: organizationId,
|
|
335
|
+
organization_name: org?.name || null,
|
|
336
|
+
on_demand_mapping_job_id: null,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
try {
|
|
340
|
+
// Get organization name
|
|
341
|
+
const orgSql = `
|
|
342
|
+
SELECT name
|
|
343
|
+
FROM public.organizations
|
|
344
|
+
WHERE id = $1
|
|
345
|
+
`;
|
|
346
|
+
const org = await (0, pg_client_1.queryOne)(db, orgSql, [
|
|
347
|
+
organizationId,
|
|
348
|
+
]).catch(() => null);
|
|
349
|
+
const organizationName = org?.name || null;
|
|
350
|
+
const sql = `
|
|
351
|
+
SELECT
|
|
352
|
+
oaj.id,
|
|
353
|
+
oaj.created_at,
|
|
354
|
+
oaj.started_at,
|
|
355
|
+
oaj.completed_at
|
|
356
|
+
FROM monitoring.organization_assignment_jobs oaj
|
|
357
|
+
WHERE oaj.mapping_job_id = $1
|
|
358
|
+
AND oaj.organization_id = $2
|
|
359
|
+
ORDER BY oaj.created_at DESC
|
|
360
|
+
LIMIT 1
|
|
361
|
+
`;
|
|
362
|
+
const exportJob = await (0, pg_client_1.queryOne)(db, sql, [mappingJobId, organizationId]);
|
|
363
|
+
if (exportJob) {
|
|
364
|
+
// Already exported or in progress
|
|
365
|
+
const orgAssignmentJobDetails = {
|
|
366
|
+
id: exportJob.id,
|
|
367
|
+
created_at: exportJob.created_at,
|
|
368
|
+
started_at: exportJob.started_at,
|
|
369
|
+
completed_at: exportJob.completed_at,
|
|
370
|
+
};
|
|
371
|
+
// Find the on-demand mapping job for this mapping job
|
|
372
|
+
const onDemandJobSql = `
|
|
373
|
+
SELECT odmj.id
|
|
374
|
+
FROM monitoring.on_demand_mapping_jobs odmj
|
|
375
|
+
WHERE odmj.mapping_job_id = $1
|
|
376
|
+
ORDER BY odmj.created_at DESC
|
|
377
|
+
LIMIT 1
|
|
378
|
+
`;
|
|
379
|
+
const onDemandJob = await (0, pg_client_1.queryOne)(db, onDemandJobSql, [
|
|
380
|
+
mappingJobId,
|
|
381
|
+
]).catch(() => null);
|
|
382
|
+
if (exportJob.completed_at) {
|
|
383
|
+
return {
|
|
384
|
+
can_export: false,
|
|
385
|
+
is_exported: true,
|
|
386
|
+
export_status: "exported",
|
|
387
|
+
organization_id: organizationId,
|
|
388
|
+
organization_name: organizationName,
|
|
389
|
+
on_demand_mapping_job_id: onDemandJob?.id || null,
|
|
390
|
+
org_assignment_job: orgAssignmentJobDetails,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
// In progress (started or queued)
|
|
395
|
+
return {
|
|
396
|
+
can_export: false,
|
|
397
|
+
is_exported: false,
|
|
398
|
+
export_status: "exporting",
|
|
399
|
+
organization_id: organizationId,
|
|
400
|
+
organization_name: organizationName,
|
|
401
|
+
on_demand_mapping_job_id: onDemandJob?.id || null,
|
|
402
|
+
org_assignment_job: orgAssignmentJobDetails,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
// Not exported yet, can export
|
|
408
|
+
// Find the on-demand mapping job for this mapping job
|
|
409
|
+
const onDemandJobSql = `
|
|
410
|
+
SELECT odmj.id
|
|
411
|
+
FROM monitoring.on_demand_mapping_jobs odmj
|
|
412
|
+
WHERE odmj.mapping_job_id = $1
|
|
413
|
+
ORDER BY odmj.created_at DESC
|
|
414
|
+
LIMIT 1
|
|
415
|
+
`;
|
|
416
|
+
const onDemandJob = await (0, pg_client_1.queryOne)(db, onDemandJobSql, [
|
|
417
|
+
mappingJobId,
|
|
418
|
+
]).catch(() => null);
|
|
419
|
+
// Get LinkedIn identifier (ACoA) for direct export if no on-demand job exists
|
|
420
|
+
let linkedinIdentifierAcoa = null;
|
|
421
|
+
if (!onDemandJob) {
|
|
422
|
+
const linkedinAccountSql = `
|
|
423
|
+
SELECT la.linkedin_identifier_acoa
|
|
424
|
+
FROM linkedin.accounts la
|
|
425
|
+
INNER JOIN monitoring.mapping_jobs mj ON la.id = mj.linkedin_account_id
|
|
426
|
+
WHERE mj.id = $1
|
|
427
|
+
LIMIT 1
|
|
428
|
+
`;
|
|
429
|
+
const linkedinAccount = await (0, pg_client_1.queryOne)(db, linkedinAccountSql, [mappingJobId]).catch(() => null);
|
|
430
|
+
linkedinIdentifierAcoa = linkedinAccount?.linkedin_identifier_acoa || null;
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
can_export: true,
|
|
434
|
+
is_exported: false,
|
|
435
|
+
export_status: "not_exported",
|
|
436
|
+
organization_id: organizationId,
|
|
437
|
+
organization_name: organizationName,
|
|
438
|
+
on_demand_mapping_job_id: onDemandJob?.id || null,
|
|
439
|
+
mapping_job_id: mappingJobId, // Always include mapping job ID
|
|
440
|
+
linkedin_identifier_acoa: linkedinIdentifierAcoa, // Include ACoA for direct export
|
|
441
|
+
org_assignment_job: null,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
catch (error) {
|
|
446
|
+
console.error("Error checking export availability:", error);
|
|
447
|
+
// On error, assume not available
|
|
448
|
+
return {
|
|
449
|
+
can_export: false,
|
|
450
|
+
is_exported: false,
|
|
451
|
+
export_status: "not_available",
|
|
452
|
+
organization_id: organizationId,
|
|
453
|
+
organization_name: null,
|
|
454
|
+
org_assignment_job: null,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Check export availability for a LinkedIn account to a specific organization
|
|
460
|
+
* This checks ALL mapping jobs for the account, not just the latest one
|
|
461
|
+
*
|
|
462
|
+
* @param db - Database instance
|
|
463
|
+
* @param linkedinAccountId - LinkedIn account ID
|
|
464
|
+
* @param organizationId - Organization ID to check
|
|
465
|
+
* @returns Export availability information
|
|
466
|
+
*/
|
|
467
|
+
async function checkExportAvailabilityForLinkedInAccount(db, linkedinAccountId, organizationId) {
|
|
468
|
+
(0, db_1.debugLogDbOperation)("select", "organization_assignment_jobs + mapping_jobs", { linkedinAccountId, organizationId }, undefined, { operation: "checkExportAvailabilityForLinkedInAccount" });
|
|
469
|
+
try {
|
|
470
|
+
// First, get the organization name
|
|
471
|
+
const orgSql = `
|
|
472
|
+
SELECT name
|
|
473
|
+
FROM public.organizations
|
|
474
|
+
WHERE id = $1
|
|
475
|
+
`;
|
|
476
|
+
const org = await (0, pg_client_1.queryOne)(db, orgSql, [organizationId]);
|
|
477
|
+
const organizationName = org?.name || null;
|
|
478
|
+
// Check if there's an organization assignment job for ANY mapping job
|
|
479
|
+
// of this LinkedIn account for this organization
|
|
480
|
+
const sql = `
|
|
481
|
+
SELECT
|
|
482
|
+
oaj.id,
|
|
483
|
+
oaj.created_at,
|
|
484
|
+
oaj.started_at,
|
|
485
|
+
oaj.completed_at,
|
|
486
|
+
oaj.mapping_job_id
|
|
487
|
+
FROM monitoring.organization_assignment_jobs oaj
|
|
488
|
+
INNER JOIN monitoring.mapping_jobs mj ON oaj.mapping_job_id = mj.id
|
|
489
|
+
WHERE mj.linkedin_account_id = $1
|
|
490
|
+
AND oaj.organization_id = $2
|
|
491
|
+
ORDER BY oaj.created_at DESC
|
|
492
|
+
LIMIT 1
|
|
493
|
+
`;
|
|
494
|
+
const exportJob = await (0, pg_client_1.queryOne)(db, sql, [linkedinAccountId, organizationId]);
|
|
495
|
+
if (exportJob) {
|
|
496
|
+
// Already exported or in progress
|
|
497
|
+
const orgAssignmentJobDetails = {
|
|
498
|
+
id: exportJob.id,
|
|
499
|
+
created_at: exportJob.created_at,
|
|
500
|
+
started_at: exportJob.started_at,
|
|
501
|
+
completed_at: exportJob.completed_at,
|
|
502
|
+
};
|
|
503
|
+
if (exportJob.completed_at) {
|
|
504
|
+
return {
|
|
505
|
+
can_export: false,
|
|
506
|
+
is_exported: true,
|
|
507
|
+
export_status: "exported",
|
|
508
|
+
organization_id: organizationId,
|
|
509
|
+
organization_name: organizationName,
|
|
510
|
+
org_assignment_job: orgAssignmentJobDetails,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
// In progress (started or queued)
|
|
515
|
+
return {
|
|
516
|
+
can_export: false,
|
|
517
|
+
is_exported: false,
|
|
518
|
+
export_status: "exporting",
|
|
519
|
+
organization_id: organizationId,
|
|
520
|
+
organization_name: organizationName,
|
|
521
|
+
org_assignment_job: orgAssignmentJobDetails,
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
// Check if there's a completed mapping job for this account
|
|
527
|
+
// If yes, export is available. If no, export is not available.
|
|
528
|
+
const completedJobSql = `
|
|
529
|
+
SELECT id
|
|
530
|
+
FROM monitoring.mapping_jobs
|
|
531
|
+
WHERE linkedin_account_id = $1
|
|
532
|
+
AND completed_at IS NOT NULL
|
|
533
|
+
ORDER BY completed_at DESC
|
|
534
|
+
LIMIT 1
|
|
535
|
+
`;
|
|
536
|
+
const completedJob = await (0, pg_client_1.queryOne)(db, completedJobSql, [linkedinAccountId]);
|
|
537
|
+
if (completedJob) {
|
|
538
|
+
// Not exported yet, but mapping is complete - can export
|
|
539
|
+
// Find the on-demand mapping job for this completed mapping job
|
|
540
|
+
const onDemandJobSql = `
|
|
541
|
+
SELECT odmj.id
|
|
542
|
+
FROM monitoring.on_demand_mapping_jobs odmj
|
|
543
|
+
WHERE odmj.mapping_job_id = $1
|
|
544
|
+
ORDER BY odmj.created_at DESC
|
|
545
|
+
LIMIT 1
|
|
546
|
+
`;
|
|
547
|
+
const onDemandJob = await (0, pg_client_1.queryOne)(db, onDemandJobSql, [completedJob.id]).catch(() => null);
|
|
548
|
+
// Get LinkedIn identifier (ACoA) for direct export if no on-demand job exists
|
|
549
|
+
let linkedinIdentifierAcoa = null;
|
|
550
|
+
if (!onDemandJob) {
|
|
551
|
+
const linkedinAccountSql = `
|
|
552
|
+
SELECT la.linkedin_identifier_acoa
|
|
553
|
+
FROM linkedin.accounts la
|
|
554
|
+
WHERE la.id = $1
|
|
555
|
+
LIMIT 1
|
|
556
|
+
`;
|
|
557
|
+
const linkedinAccount = await (0, pg_client_1.queryOne)(db, linkedinAccountSql, [linkedinAccountId]).catch(() => null);
|
|
558
|
+
linkedinIdentifierAcoa = linkedinAccount?.linkedin_identifier_acoa || null;
|
|
559
|
+
}
|
|
560
|
+
return {
|
|
561
|
+
can_export: true,
|
|
562
|
+
is_exported: false,
|
|
563
|
+
export_status: "not_exported",
|
|
564
|
+
organization_id: organizationId,
|
|
565
|
+
organization_name: organizationName,
|
|
566
|
+
on_demand_mapping_job_id: onDemandJob?.id || null,
|
|
567
|
+
mapping_job_id: completedJob.id, // Always include mapping job ID
|
|
568
|
+
linkedin_identifier_acoa: linkedinIdentifierAcoa, // Include ACoA for direct export
|
|
569
|
+
org_assignment_job: null,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
// No completed mapping job - export not available
|
|
574
|
+
return {
|
|
575
|
+
can_export: false,
|
|
576
|
+
is_exported: false,
|
|
577
|
+
export_status: "not_available",
|
|
578
|
+
organization_id: organizationId,
|
|
579
|
+
organization_name: organizationName,
|
|
580
|
+
on_demand_mapping_job_id: null,
|
|
581
|
+
org_assignment_job: null,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
catch (error) {
|
|
587
|
+
console.error("Error checking export availability for LinkedIn account:", error);
|
|
588
|
+
// On error, assume not available
|
|
589
|
+
return {
|
|
590
|
+
can_export: false,
|
|
591
|
+
is_exported: false,
|
|
592
|
+
export_status: "not_available",
|
|
593
|
+
organization_id: organizationId,
|
|
594
|
+
organization_name: null,
|
|
595
|
+
org_assignment_job: null,
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
async function getUserExports(db, requesterLinkedinAccountId, options = {}) {
|
|
600
|
+
const { page = 1, pageSize = 10, sort = "desc", organizationId, status = "all" } = options;
|
|
601
|
+
(0, db_1.debugLogDbOperation)("select", "organization_assignment_jobs + mapping_jobs + on_demand_mapping_jobs", {
|
|
602
|
+
requesterLinkedinAccountId,
|
|
603
|
+
page,
|
|
604
|
+
pageSize,
|
|
605
|
+
sort,
|
|
606
|
+
organizationId,
|
|
607
|
+
status,
|
|
608
|
+
}, undefined, { operation: "getUserExports" });
|
|
609
|
+
// Validate pagination
|
|
610
|
+
const limit = Math.min(Math.max(1, pageSize), 50); // Max 50 per page
|
|
611
|
+
const offset = (Math.max(1, page) - 1) * limit;
|
|
612
|
+
const orderBy = sort === "asc" ? "ASC" : "DESC";
|
|
613
|
+
// Build WHERE clause for status filter
|
|
614
|
+
let statusFilter = "";
|
|
615
|
+
if (status === "completed") {
|
|
616
|
+
statusFilter = "AND oaj.completed_at IS NOT NULL";
|
|
617
|
+
}
|
|
618
|
+
else if (status === "pending") {
|
|
619
|
+
statusFilter = "AND oaj.completed_at IS NULL AND oaj.started_at IS NOT NULL";
|
|
620
|
+
}
|
|
621
|
+
else if (status === "failed") {
|
|
622
|
+
// Failed status would need to be tracked in metadata or a separate field
|
|
623
|
+
// For now, we'll treat it as pending if not completed
|
|
624
|
+
statusFilter = "AND oaj.completed_at IS NULL";
|
|
625
|
+
}
|
|
626
|
+
// Build WHERE clause for organization filter
|
|
627
|
+
// If organizationId is provided, return all exports for that organization
|
|
628
|
+
// Otherwise, return only exports where the user is the requester
|
|
629
|
+
let orgFilter = "";
|
|
630
|
+
let requesterFilter = "";
|
|
631
|
+
let paramIndex = 1;
|
|
632
|
+
const countParams = [];
|
|
633
|
+
if (organizationId) {
|
|
634
|
+
// When organizationId is provided, return all exports for that organization
|
|
635
|
+
// (all members should see all exports to their organization)
|
|
636
|
+
orgFilter = `oaj.organization_id = $${paramIndex}`;
|
|
637
|
+
countParams.push(organizationId);
|
|
638
|
+
paramIndex++;
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
// When organizationId is NOT provided, filter by requester (backward compatibility)
|
|
642
|
+
requesterFilter = `odmj.requester_linkedin_account_id = $${paramIndex}`;
|
|
643
|
+
countParams.push(requesterLinkedinAccountId);
|
|
644
|
+
paramIndex++;
|
|
645
|
+
}
|
|
646
|
+
const whereClause = [orgFilter, requesterFilter].filter(Boolean).join(" AND ");
|
|
647
|
+
const countSql = `
|
|
648
|
+
SELECT COUNT(DISTINCT oaj.id) as total
|
|
649
|
+
FROM monitoring.organization_assignment_jobs oaj
|
|
650
|
+
INNER JOIN monitoring.mapping_jobs mj ON oaj.mapping_job_id = mj.id
|
|
651
|
+
LEFT JOIN monitoring.on_demand_mapping_jobs odmj ON mj.id = odmj.mapping_job_id
|
|
652
|
+
WHERE ${whereClause}
|
|
653
|
+
${statusFilter}
|
|
654
|
+
`;
|
|
655
|
+
const totalResult = await (0, pg_client_1.queryOne)(db, countSql, countParams);
|
|
656
|
+
const totalItems = totalResult?.total || 0;
|
|
657
|
+
const totalPages = Math.ceil(totalItems / limit);
|
|
658
|
+
// Get paginated exports with full details
|
|
659
|
+
// Note: current_company comes from work_experience table (most recent current position)
|
|
660
|
+
const sql = `
|
|
661
|
+
SELECT
|
|
662
|
+
oaj.id,
|
|
663
|
+
oaj.created_at,
|
|
664
|
+
oaj.started_at,
|
|
665
|
+
oaj.completed_at,
|
|
666
|
+
oaj.mapping_job_id,
|
|
667
|
+
oaj.organization_id,
|
|
668
|
+
o.name as organization_name,
|
|
669
|
+
o.workspace_type,
|
|
670
|
+
CONCAT(COALESCE(la.first_name, ''), ' ', COALESCE(la.last_name, '')) as profile_name,
|
|
671
|
+
la.headline as profile_position,
|
|
672
|
+
we.company as current_company,
|
|
673
|
+
COALESCE(la.profile_image_cloudfront_url, la.profile_picture_url) as profile_image_url,
|
|
674
|
+
la.linkedin_identifier_acoa as linkedin_identifier,
|
|
675
|
+
CASE
|
|
676
|
+
WHEN la.public_identifier IS NOT NULL
|
|
677
|
+
THEN CONCAT('https://www.linkedin.com/in/', la.public_identifier)
|
|
678
|
+
ELSE NULL
|
|
679
|
+
END as linkedin_url
|
|
680
|
+
FROM monitoring.organization_assignment_jobs oaj
|
|
681
|
+
INNER JOIN monitoring.mapping_jobs mj ON oaj.mapping_job_id = mj.id
|
|
682
|
+
LEFT JOIN monitoring.on_demand_mapping_jobs odmj ON mj.id = odmj.mapping_job_id
|
|
683
|
+
INNER JOIN linkedin.accounts la ON mj.linkedin_account_id = la.id
|
|
684
|
+
INNER JOIN public.organizations o ON oaj.organization_id = o.id
|
|
685
|
+
LEFT JOIN LATERAL (
|
|
686
|
+
SELECT company
|
|
687
|
+
FROM linkedin.work_experience we
|
|
688
|
+
WHERE we.linkedin_account_id = la.id
|
|
689
|
+
AND we.is_current = true
|
|
690
|
+
ORDER BY we.start_date DESC NULLS LAST
|
|
691
|
+
LIMIT 1
|
|
692
|
+
) we ON true
|
|
693
|
+
WHERE ${whereClause}
|
|
694
|
+
${statusFilter}
|
|
695
|
+
ORDER BY oaj.created_at ${orderBy}
|
|
696
|
+
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
|
|
697
|
+
`;
|
|
698
|
+
const queryParams = [...countParams, limit, offset];
|
|
699
|
+
const results = await (0, pg_client_1.query)(db, sql, queryParams);
|
|
700
|
+
const exports = results.map(r => {
|
|
701
|
+
// Determine status based on completion
|
|
702
|
+
let exportStatus = "pending";
|
|
703
|
+
if (r.completed_at) {
|
|
704
|
+
exportStatus = "completed";
|
|
705
|
+
}
|
|
706
|
+
else if (r.started_at) {
|
|
707
|
+
exportStatus = "pending";
|
|
708
|
+
}
|
|
709
|
+
return {
|
|
710
|
+
id: r.id,
|
|
711
|
+
status: exportStatus,
|
|
712
|
+
created_at: r.created_at,
|
|
713
|
+
started_at: r.started_at,
|
|
714
|
+
completed_at: r.completed_at,
|
|
715
|
+
mapping_job_id: r.mapping_job_id,
|
|
716
|
+
organization: {
|
|
717
|
+
id: r.organization_id,
|
|
718
|
+
name: r.organization_name,
|
|
719
|
+
workspace_type: r.workspace_type,
|
|
720
|
+
},
|
|
721
|
+
profile: {
|
|
722
|
+
name: r.profile_name,
|
|
723
|
+
position: r.profile_position,
|
|
724
|
+
current_company: r.current_company,
|
|
725
|
+
profile_image_url: r.profile_image_url,
|
|
726
|
+
linkedin_identifier: r.linkedin_identifier,
|
|
727
|
+
linkedin_url: r.linkedin_url,
|
|
728
|
+
},
|
|
729
|
+
};
|
|
730
|
+
});
|
|
731
|
+
return {
|
|
732
|
+
exports,
|
|
733
|
+
pagination: {
|
|
734
|
+
page,
|
|
735
|
+
pageSize: limit,
|
|
736
|
+
totalItems,
|
|
737
|
+
totalPages,
|
|
738
|
+
},
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Get export status for a specific LinkedIn identifier (ACoA) in an organization
|
|
743
|
+
* Returns full export details including profile and organization information
|
|
744
|
+
*
|
|
745
|
+
* @param db - Database instance
|
|
746
|
+
* @param linkedinIdentifierAcoa - LinkedIn ACoA identifier
|
|
747
|
+
* @param organizationId - Organization ID
|
|
748
|
+
* @returns Export status result
|
|
749
|
+
*/
|
|
750
|
+
async function getExportStatusByLinkedInIdentifier(db, linkedinIdentifierAcoa, organizationId) {
|
|
751
|
+
(0, db_1.debugLogDbOperation)("select", "organization_assignment_jobs + mapping_jobs + linkedin.accounts", { linkedinIdentifierAcoa, organizationId }, undefined, { operation: "getExportStatusByLinkedInIdentifier" });
|
|
752
|
+
try {
|
|
753
|
+
// Single query to get all export details
|
|
754
|
+
const sql = `
|
|
755
|
+
SELECT
|
|
756
|
+
oaj.id,
|
|
757
|
+
oaj.created_at,
|
|
758
|
+
oaj.started_at,
|
|
759
|
+
oaj.completed_at,
|
|
760
|
+
oaj.mapping_job_id,
|
|
761
|
+
oaj.organization_id,
|
|
762
|
+
o.name as organization_name,
|
|
763
|
+
o.workspace_type,
|
|
764
|
+
CONCAT(COALESCE(la.first_name, ''), ' ', COALESCE(la.last_name, '')) as profile_name,
|
|
765
|
+
la.headline as profile_position,
|
|
766
|
+
we.company as current_company,
|
|
767
|
+
COALESCE(la.profile_image_cloudfront_url, la.profile_picture_url) as profile_image_url,
|
|
768
|
+
la.linkedin_identifier_acoa as linkedin_identifier,
|
|
769
|
+
CASE
|
|
770
|
+
WHEN la.public_identifier IS NOT NULL
|
|
771
|
+
THEN CONCAT('https://www.linkedin.com/in/', la.public_identifier)
|
|
772
|
+
ELSE NULL
|
|
773
|
+
END as linkedin_url
|
|
774
|
+
FROM monitoring.organization_assignment_jobs oaj
|
|
775
|
+
INNER JOIN monitoring.mapping_jobs mj ON oaj.mapping_job_id = mj.id
|
|
776
|
+
INNER JOIN linkedin.accounts la ON mj.linkedin_account_id = la.id
|
|
777
|
+
INNER JOIN public.organizations o ON oaj.organization_id = o.id
|
|
778
|
+
LEFT JOIN LATERAL (
|
|
779
|
+
SELECT company
|
|
780
|
+
FROM linkedin.work_experience we
|
|
781
|
+
WHERE we.linkedin_account_id = la.id
|
|
782
|
+
AND we.is_current = true
|
|
783
|
+
ORDER BY we.start_date DESC NULLS LAST
|
|
784
|
+
LIMIT 1
|
|
785
|
+
) we ON true
|
|
786
|
+
WHERE la.linkedin_identifier_acoa = $1
|
|
787
|
+
AND oaj.organization_id = $2
|
|
788
|
+
ORDER BY oaj.created_at DESC
|
|
789
|
+
LIMIT 1
|
|
790
|
+
`;
|
|
791
|
+
const result = await (0, pg_client_1.queryOne)(db, sql, [linkedinIdentifierAcoa, organizationId]);
|
|
792
|
+
if (!result) {
|
|
793
|
+
return {
|
|
794
|
+
found: false,
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
// Determine status
|
|
798
|
+
let exportStatus = "pending";
|
|
799
|
+
if (result.completed_at) {
|
|
800
|
+
exportStatus = "completed";
|
|
801
|
+
}
|
|
802
|
+
else if (result.started_at) {
|
|
803
|
+
exportStatus = "pending";
|
|
804
|
+
}
|
|
805
|
+
return {
|
|
806
|
+
found: true,
|
|
807
|
+
export: {
|
|
808
|
+
id: result.id,
|
|
809
|
+
status: exportStatus,
|
|
810
|
+
created_at: result.created_at,
|
|
811
|
+
started_at: result.started_at,
|
|
812
|
+
completed_at: result.completed_at,
|
|
813
|
+
mapping_job_id: result.mapping_job_id,
|
|
814
|
+
organization: {
|
|
815
|
+
id: result.organization_id,
|
|
816
|
+
name: result.organization_name,
|
|
817
|
+
workspace_type: result.workspace_type,
|
|
818
|
+
},
|
|
819
|
+
profile: {
|
|
820
|
+
name: result.profile_name || "Unknown",
|
|
821
|
+
position: result.profile_position || null,
|
|
822
|
+
current_company: result.current_company || null,
|
|
823
|
+
profile_image_url: result.profile_image_url || null,
|
|
824
|
+
linkedin_identifier: result.linkedin_identifier,
|
|
825
|
+
linkedin_url: result.linkedin_url || null,
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
catch (error) {
|
|
831
|
+
console.error("Error getting export status by LinkedIn identifier:", error);
|
|
832
|
+
throw error;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
// =============================================================================
|
|
836
|
+
// VALIDATION & MANAGEMENT
|
|
837
|
+
// =============================================================================
|
|
838
|
+
/**
|
|
839
|
+
* Validate that an organization assignment job exists and is completed
|
|
840
|
+
*
|
|
841
|
+
* @param db - Database instance
|
|
842
|
+
* @param organizationAssignmentJobId - Job ID
|
|
843
|
+
* @returns True if valid and completed, false otherwise
|
|
844
|
+
*/
|
|
845
|
+
async function validateOrganizationAssignmentJobCompletion(db, organizationAssignmentJobId) {
|
|
846
|
+
(0, db_1.debugLogDbOperation)("select", "organization_assignment_jobs", { organizationAssignmentJobId }, undefined, { operation: "validateOrganizationAssignmentJobCompletion" });
|
|
847
|
+
const job = await getOrganizationAssignmentJobById(db, organizationAssignmentJobId);
|
|
848
|
+
if (!job) {
|
|
849
|
+
return false;
|
|
850
|
+
}
|
|
851
|
+
return job.completedAt !== null;
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Delete an organization assignment job
|
|
855
|
+
* Use with caution - this will cascade delete related records
|
|
856
|
+
*
|
|
857
|
+
* @param db - Database instance
|
|
858
|
+
* @param organizationAssignmentJobId - Job ID
|
|
859
|
+
* @returns Deleted job record
|
|
860
|
+
*/
|
|
861
|
+
async function deleteOrganizationAssignmentJob(db, organizationAssignmentJobId) {
|
|
862
|
+
(0, db_1.debugLogDbOperation)("delete", "organization_assignment_jobs", { organizationAssignmentJobId }, undefined, { operation: "deleteOrganizationAssignmentJob" });
|
|
863
|
+
const sql = `
|
|
864
|
+
DELETE FROM monitoring.organization_assignment_jobs
|
|
865
|
+
WHERE id = $1
|
|
866
|
+
RETURNING id, mapping_job_id, organization_id, metadata, created_at, completed_at
|
|
867
|
+
`;
|
|
868
|
+
const deleted = await (0, pg_client_1.queryOne)(db, sql, [organizationAssignmentJobId]);
|
|
869
|
+
if (!deleted) {
|
|
870
|
+
throw new Error(`Organization assignment job ${organizationAssignmentJobId} not found`);
|
|
871
|
+
}
|
|
872
|
+
return {
|
|
873
|
+
id: deleted.id,
|
|
874
|
+
mappingJobId: deleted.mapping_job_id,
|
|
875
|
+
organizationId: deleted.organization_id,
|
|
876
|
+
metadata: deleted.metadata,
|
|
877
|
+
createdAt: deleted.created_at,
|
|
878
|
+
completedAt: deleted.completed_at,
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
//# sourceMappingURL=organization-assignment-job-operations.js.map
|