Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ Configuration is managed through two files:
* `start_date`: (Optional) Only process tickets/articles updated since this date (e.g., `'2025-01-01'`).
* `ticket_status`: (Optional) Filter tickets by status (defaults to `['new', 'open', 'pending', 'hold', 'solved']`).
* `ticket_priority`: (Optional) Filter tickets by priority (defaults to all priorities).
* `excluded_organizations`: (Optional) An array of Zendesk organization names whose tickets should be skipped. The sync will abort if any name cannot be resolved.

For S3 buckets (`type: 's3'`):
* `bucket`: The S3 bucket name.
Expand Down Expand Up @@ -359,6 +360,7 @@ Configuration is managed through two files:
start_date: '2025-01-01'
ticket_status: ['open', 'pending']
ticket_priority: ['high']
excluded_organizations: ['Acme Corp', 'Internal Testing']
max_size: 1048576
database_config:
type: 'sqlite'
Expand Down
31 changes: 31 additions & 0 deletions doc2vec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,9 @@ export class Doc2Vec {
// transitioning to closed get updated rather than left stale)
const statusFilter = new Set(config.ticket_status || ['new', 'open', 'pending', 'hold', 'solved', 'closed']);

const excludedOrgNames = new Set((config.excluded_organizations || []).map(n => n.toLowerCase()));
const excludedOrgIds = new Set<number>();

const fetchWithRetry = async (url: string, retries = 3): Promise<any> => {
for (let attempt = 0; attempt < retries; attempt++) {
try {
Expand Down Expand Up @@ -1409,6 +1412,12 @@ export class Doc2Vec {
return;
}

// Skip tickets belonging to excluded organizations
if (ticket.organization_id && excludedOrgIds.has(ticket.organization_id)) {
logger.debug(`Ticket #${ticketId} belongs to excluded organization ${ticket.organization_id} — skipping`);
return;
}

// Skip tickets whose status is outside the configured filter
if (!statusFilter.has(ticket.status)) {
logger.debug(`Ticket #${ticketId} has status '${ticket.status}' outside configured filter — skipping`);
Expand Down Expand Up @@ -1441,6 +1450,28 @@ export class Doc2Vec {
await this.processChunksForUrl(chunks, url, dbConnection, logger);
};

if (excludedOrgNames.size > 0) {
logger.info(`Resolving ${excludedOrgNames.size} excluded organization name(s) to IDs`);
const resolvedNames = new Set<string>();
let orgsUrl: string | null = `${baseUrl}/organizations.json?page[size]=100`;
while (orgsUrl) {
const orgsData: any = await fetchWithRetry(orgsUrl);
for (const org of orgsData?.organizations || []) {
const orgName = (org.name || '').toLowerCase();
if (excludedOrgNames.has(orgName)) {
excludedOrgIds.add(org.id);
resolvedNames.add(orgName);
}
}
orgsUrl = orgsData?.meta?.has_more ? orgsData?.links?.next : null;
}
logger.info(`Excluding tickets from ${excludedOrgIds.size} organization(s): ${[...excludedOrgIds].join(', ')}`);
const unresolved = [...excludedOrgNames].filter(name => !resolvedNames.has(name));
if (unresolved.length > 0) {
throw new Error(`Cannot resolve excluded organization(s): ${unresolved.join(', ')}. Aborting to avoid syncing data for them.`);
}
}

logger.info(`Fetching Zendesk tickets updated since ${lastRunDate}`);

// Build query parameters — use the status filter for the search query
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "doc2vec",
"version": "2.9.2",
"version": "2.10.0",
"type": "commonjs",
"description": "",
"main": "dist/doc2vec.js",
Expand Down
1 change: 1 addition & 0 deletions types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface ZendeskSourceConfig extends BaseSourceConfig {
start_date?: string; // For incremental updates (default: start of current year)
ticket_status?: string[]; // Filter tickets by status (default: ['new', 'open', 'pending', 'hold', 'solved'])
ticket_priority?: string[]; // Filter tickets by priority (default: all)
excluded_organizations?: string[]; // Organization names whose tickets should be skipped
}

// Configuration specific to code sources (local directory or GitHub repo)
Expand Down