@hasna/connectors 0.4.0 → 0.4.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/bin/index.js +189 -2
- package/bin/mcp.js +176 -1
- package/bin/serve.js +175 -0
- package/connectors/connect-airtable/.env.example +11 -0
- package/connectors/connect-airtable/CLAUDE.md +121 -0
- package/connectors/connect-airtable/README.md +193 -0
- package/connectors/connect-airtable/package.json +50 -0
- package/connectors/connect-airtable/src/api/client.ts +139 -0
- package/connectors/connect-airtable/src/api/index.ts +405 -0
- package/connectors/connect-airtable/src/cli/index.ts +637 -0
- package/connectors/connect-airtable/src/index.ts +20 -0
- package/connectors/connect-airtable/src/types/index.ts +349 -0
- package/connectors/connect-airtable/src/utils/config.ts +197 -0
- package/connectors/connect-airtable/tsconfig.json +16 -0
- package/connectors/connect-amplitude/.env.example +11 -0
- package/connectors/connect-amplitude/CLAUDE.md +121 -0
- package/connectors/connect-amplitude/README.md +193 -0
- package/connectors/connect-amplitude/package.json +51 -0
- package/connectors/connect-amplitude/src/api/client.ts +144 -0
- package/connectors/connect-amplitude/src/api/index.ts +215 -0
- package/connectors/connect-amplitude/src/cli/index.ts +532 -0
- package/connectors/connect-amplitude/src/index.ts +22 -0
- package/connectors/connect-amplitude/src/types/index.ts +329 -0
- package/connectors/connect-amplitude/src/utils/config.ts +208 -0
- package/connectors/connect-amplitude/tsconfig.json +16 -0
- package/connectors/connect-calendly/.env.example +11 -0
- package/connectors/connect-calendly/CLAUDE.md +272 -0
- package/connectors/connect-calendly/README.md +193 -0
- package/connectors/connect-calendly/package.json +51 -0
- package/connectors/connect-calendly/scripts/release.ts +179 -0
- package/connectors/connect-calendly/src/api/client.ts +213 -0
- package/connectors/connect-calendly/src/api/example.ts +48 -0
- package/connectors/connect-calendly/src/api/index.ts +51 -0
- package/connectors/connect-calendly/src/cli/index.ts +254 -0
- package/connectors/connect-calendly/src/index.ts +103 -0
- package/connectors/connect-calendly/src/types/index.ts +237 -0
- package/connectors/connect-calendly/src/utils/auth.ts +274 -0
- package/connectors/connect-calendly/src/utils/bulk.ts +212 -0
- package/connectors/connect-calendly/src/utils/config.ts +326 -0
- package/connectors/connect-calendly/src/utils/output.ts +175 -0
- package/connectors/connect-calendly/src/utils/settings.ts +114 -0
- package/connectors/connect-calendly/src/utils/storage.ts +198 -0
- package/connectors/connect-calendly/tsconfig.json +16 -0
- package/connectors/connect-convertkit/.env.example +11 -0
- package/connectors/connect-convertkit/CLAUDE.md +272 -0
- package/connectors/connect-convertkit/README.md +193 -0
- package/connectors/connect-convertkit/package.json +51 -0
- package/connectors/connect-convertkit/scripts/release.ts +179 -0
- package/connectors/connect-convertkit/src/api/client.ts +213 -0
- package/connectors/connect-convertkit/src/api/example.ts +48 -0
- package/connectors/connect-convertkit/src/api/index.ts +51 -0
- package/connectors/connect-convertkit/src/cli/index.ts +254 -0
- package/connectors/connect-convertkit/src/index.ts +103 -0
- package/connectors/connect-convertkit/src/types/index.ts +237 -0
- package/connectors/connect-convertkit/src/utils/auth.ts +274 -0
- package/connectors/connect-convertkit/src/utils/bulk.ts +212 -0
- package/connectors/connect-convertkit/src/utils/config.ts +326 -0
- package/connectors/connect-convertkit/src/utils/output.ts +175 -0
- package/connectors/connect-convertkit/src/utils/settings.ts +114 -0
- package/connectors/connect-convertkit/src/utils/storage.ts +198 -0
- package/connectors/connect-convertkit/tsconfig.json +16 -0
- package/connectors/connect-crisp/.env.example +11 -0
- package/connectors/connect-crisp/CLAUDE.md +272 -0
- package/connectors/connect-crisp/README.md +193 -0
- package/connectors/connect-crisp/package.json +51 -0
- package/connectors/connect-crisp/scripts/release.ts +179 -0
- package/connectors/connect-crisp/src/api/client.ts +213 -0
- package/connectors/connect-crisp/src/api/example.ts +48 -0
- package/connectors/connect-crisp/src/api/index.ts +51 -0
- package/connectors/connect-crisp/src/cli/index.ts +254 -0
- package/connectors/connect-crisp/src/index.ts +103 -0
- package/connectors/connect-crisp/src/types/index.ts +237 -0
- package/connectors/connect-crisp/src/utils/auth.ts +274 -0
- package/connectors/connect-crisp/src/utils/bulk.ts +212 -0
- package/connectors/connect-crisp/src/utils/config.ts +326 -0
- package/connectors/connect-crisp/src/utils/output.ts +175 -0
- package/connectors/connect-crisp/src/utils/settings.ts +114 -0
- package/connectors/connect-crisp/src/utils/storage.ts +198 -0
- package/connectors/connect-crisp/tsconfig.json +16 -0
- package/connectors/connect-docusign/.env.example +11 -0
- package/connectors/connect-docusign/CLAUDE.md +128 -0
- package/connectors/connect-docusign/README.md +193 -0
- package/connectors/connect-docusign/package.json +52 -0
- package/connectors/connect-docusign/src/api/client.ts +133 -0
- package/connectors/connect-docusign/src/api/index.ts +178 -0
- package/connectors/connect-docusign/src/cli/index.ts +381 -0
- package/connectors/connect-docusign/src/index.ts +23 -0
- package/connectors/connect-docusign/src/types/index.ts +208 -0
- package/connectors/connect-docusign/src/utils/config.ts +125 -0
- package/connectors/connect-docusign/src/utils/output.ts +119 -0
- package/connectors/connect-docusign/tsconfig.json +16 -0
- package/connectors/connect-drift/.env.example +11 -0
- package/connectors/connect-drift/CLAUDE.md +272 -0
- package/connectors/connect-drift/README.md +193 -0
- package/connectors/connect-drift/package.json +51 -0
- package/connectors/connect-drift/scripts/release.ts +179 -0
- package/connectors/connect-drift/src/api/client.ts +213 -0
- package/connectors/connect-drift/src/api/example.ts +48 -0
- package/connectors/connect-drift/src/api/index.ts +51 -0
- package/connectors/connect-drift/src/cli/index.ts +254 -0
- package/connectors/connect-drift/src/index.ts +103 -0
- package/connectors/connect-drift/src/types/index.ts +237 -0
- package/connectors/connect-drift/src/utils/auth.ts +274 -0
- package/connectors/connect-drift/src/utils/bulk.ts +212 -0
- package/connectors/connect-drift/src/utils/config.ts +326 -0
- package/connectors/connect-drift/src/utils/output.ts +175 -0
- package/connectors/connect-drift/src/utils/settings.ts +114 -0
- package/connectors/connect-drift/src/utils/storage.ts +198 -0
- package/connectors/connect-drift/tsconfig.json +16 -0
- package/connectors/connect-freshdesk/.env.example +11 -0
- package/connectors/connect-freshdesk/CLAUDE.md +272 -0
- package/connectors/connect-freshdesk/README.md +193 -0
- package/connectors/connect-freshdesk/package.json +51 -0
- package/connectors/connect-freshdesk/scripts/release.ts +179 -0
- package/connectors/connect-freshdesk/src/api/client.ts +213 -0
- package/connectors/connect-freshdesk/src/api/example.ts +48 -0
- package/connectors/connect-freshdesk/src/api/index.ts +51 -0
- package/connectors/connect-freshdesk/src/cli/index.ts +254 -0
- package/connectors/connect-freshdesk/src/index.ts +103 -0
- package/connectors/connect-freshdesk/src/types/index.ts +237 -0
- package/connectors/connect-freshdesk/src/utils/auth.ts +274 -0
- package/connectors/connect-freshdesk/src/utils/bulk.ts +212 -0
- package/connectors/connect-freshdesk/src/utils/config.ts +326 -0
- package/connectors/connect-freshdesk/src/utils/output.ts +175 -0
- package/connectors/connect-freshdesk/src/utils/settings.ts +114 -0
- package/connectors/connect-freshdesk/src/utils/storage.ts +198 -0
- package/connectors/connect-freshdesk/tsconfig.json +16 -0
- package/connectors/connect-gumroad/.env.example +11 -0
- package/connectors/connect-gumroad/CLAUDE.md +272 -0
- package/connectors/connect-gumroad/README.md +193 -0
- package/connectors/connect-gumroad/package.json +51 -0
- package/connectors/connect-gumroad/scripts/release.ts +179 -0
- package/connectors/connect-gumroad/src/api/client.ts +213 -0
- package/connectors/connect-gumroad/src/api/example.ts +48 -0
- package/connectors/connect-gumroad/src/api/index.ts +51 -0
- package/connectors/connect-gumroad/src/cli/index.ts +254 -0
- package/connectors/connect-gumroad/src/index.ts +103 -0
- package/connectors/connect-gumroad/src/types/index.ts +237 -0
- package/connectors/connect-gumroad/src/utils/auth.ts +274 -0
- package/connectors/connect-gumroad/src/utils/bulk.ts +212 -0
- package/connectors/connect-gumroad/src/utils/config.ts +326 -0
- package/connectors/connect-gumroad/src/utils/output.ts +175 -0
- package/connectors/connect-gumroad/src/utils/settings.ts +114 -0
- package/connectors/connect-gumroad/src/utils/storage.ts +198 -0
- package/connectors/connect-gumroad/tsconfig.json +16 -0
- package/connectors/connect-hubspot/.env.example +11 -0
- package/connectors/connect-hubspot/CLAUDE.md +128 -0
- package/connectors/connect-hubspot/README.md +193 -0
- package/connectors/connect-hubspot/package.json +52 -0
- package/connectors/connect-hubspot/src/api/client.ts +136 -0
- package/connectors/connect-hubspot/src/api/index.ts +379 -0
- package/connectors/connect-hubspot/src/cli/index.ts +589 -0
- package/connectors/connect-hubspot/src/index.ts +21 -0
- package/connectors/connect-hubspot/src/types/index.ts +285 -0
- package/connectors/connect-hubspot/src/utils/config.ts +205 -0
- package/connectors/connect-hubspot/src/utils/output.ts +119 -0
- package/connectors/connect-hubspot/tsconfig.json +16 -0
- package/connectors/connect-intercom/.env.example +11 -0
- package/connectors/connect-intercom/CLAUDE.md +131 -0
- package/connectors/connect-intercom/README.md +193 -0
- package/connectors/connect-intercom/package.json +52 -0
- package/connectors/connect-intercom/src/api/client.ts +132 -0
- package/connectors/connect-intercom/src/api/index.ts +366 -0
- package/connectors/connect-intercom/src/cli/index.ts +831 -0
- package/connectors/connect-intercom/src/index.ts +21 -0
- package/connectors/connect-intercom/src/types/index.ts +650 -0
- package/connectors/connect-intercom/src/utils/config.ts +157 -0
- package/connectors/connect-intercom/src/utils/output.ts +119 -0
- package/connectors/connect-intercom/tsconfig.json +16 -0
- package/connectors/connect-lemonsqueezy/.env.example +11 -0
- package/connectors/connect-lemonsqueezy/CLAUDE.md +128 -0
- package/connectors/connect-lemonsqueezy/README.md +193 -0
- package/connectors/connect-lemonsqueezy/package.json +52 -0
- package/connectors/connect-lemonsqueezy/src/api/client.ts +133 -0
- package/connectors/connect-lemonsqueezy/src/api/index.ts +502 -0
- package/connectors/connect-lemonsqueezy/src/cli/index.ts +723 -0
- package/connectors/connect-lemonsqueezy/src/index.ts +21 -0
- package/connectors/connect-lemonsqueezy/src/types/index.ts +353 -0
- package/connectors/connect-lemonsqueezy/src/utils/config.ts +205 -0
- package/connectors/connect-lemonsqueezy/src/utils/output.ts +119 -0
- package/connectors/connect-lemonsqueezy/tsconfig.json +16 -0
- package/connectors/connect-linkedin/.env.example +11 -0
- package/connectors/connect-linkedin/CLAUDE.md +124 -0
- package/connectors/connect-linkedin/README.md +193 -0
- package/connectors/connect-linkedin/package.json +52 -0
- package/connectors/connect-linkedin/src/api/client.ts +132 -0
- package/connectors/connect-linkedin/src/api/index.ts +313 -0
- package/connectors/connect-linkedin/src/cli/index.ts +548 -0
- package/connectors/connect-linkedin/src/index.ts +21 -0
- package/connectors/connect-linkedin/src/types/index.ts +472 -0
- package/connectors/connect-linkedin/src/utils/config.ts +157 -0
- package/connectors/connect-linkedin/src/utils/output.ts +119 -0
- package/connectors/connect-linkedin/tsconfig.json +16 -0
- package/connectors/connect-mailchimp/.env.example +11 -0
- package/connectors/connect-mailchimp/CLAUDE.md +127 -0
- package/connectors/connect-mailchimp/README.md +193 -0
- package/connectors/connect-mailchimp/package.json +52 -0
- package/connectors/connect-mailchimp/src/api/client.ts +162 -0
- package/connectors/connect-mailchimp/src/api/index.ts +580 -0
- package/connectors/connect-mailchimp/src/cli/index.ts +822 -0
- package/connectors/connect-mailchimp/src/index.ts +23 -0
- package/connectors/connect-mailchimp/src/types/index.ts +585 -0
- package/connectors/connect-mailchimp/src/utils/config.ts +208 -0
- package/connectors/connect-mailchimp/src/utils/output.ts +119 -0
- package/connectors/connect-mailchimp/tsconfig.json +16 -0
- package/connectors/connect-mongodb/.env.example +11 -0
- package/connectors/connect-mongodb/CLAUDE.md +272 -0
- package/connectors/connect-mongodb/README.md +193 -0
- package/connectors/connect-mongodb/package.json +51 -0
- package/connectors/connect-mongodb/scripts/release.ts +179 -0
- package/connectors/connect-mongodb/src/api/client.ts +213 -0
- package/connectors/connect-mongodb/src/api/example.ts +48 -0
- package/connectors/connect-mongodb/src/api/index.ts +51 -0
- package/connectors/connect-mongodb/src/cli/index.ts +254 -0
- package/connectors/connect-mongodb/src/index.ts +103 -0
- package/connectors/connect-mongodb/src/types/index.ts +237 -0
- package/connectors/connect-mongodb/src/utils/auth.ts +274 -0
- package/connectors/connect-mongodb/src/utils/bulk.ts +212 -0
- package/connectors/connect-mongodb/src/utils/config.ts +326 -0
- package/connectors/connect-mongodb/src/utils/output.ts +175 -0
- package/connectors/connect-mongodb/src/utils/settings.ts +114 -0
- package/connectors/connect-mongodb/src/utils/storage.ts +198 -0
- package/connectors/connect-mongodb/tsconfig.json +16 -0
- package/connectors/connect-netlify/.env.example +11 -0
- package/connectors/connect-netlify/CLAUDE.md +170 -0
- package/connectors/connect-netlify/README.md +193 -0
- package/connectors/connect-netlify/package.json +53 -0
- package/connectors/connect-netlify/src/api/client.ts +132 -0
- package/connectors/connect-netlify/src/api/index.ts +533 -0
- package/connectors/connect-netlify/src/cli/index.ts +985 -0
- package/connectors/connect-netlify/src/index.ts +22 -0
- package/connectors/connect-netlify/src/types/index.ts +423 -0
- package/connectors/connect-netlify/src/utils/config.ts +171 -0
- package/connectors/connect-netlify/src/utils/output.ts +119 -0
- package/connectors/connect-netlify/tsconfig.json +16 -0
- package/connectors/connect-paypal/.env.example +11 -0
- package/connectors/connect-paypal/CLAUDE.md +128 -0
- package/connectors/connect-paypal/README.md +193 -0
- package/connectors/connect-paypal/package.json +51 -0
- package/connectors/connect-paypal/src/api/client.ts +182 -0
- package/connectors/connect-paypal/src/api/index.ts +235 -0
- package/connectors/connect-paypal/src/cli/index.ts +559 -0
- package/connectors/connect-paypal/src/index.ts +23 -0
- package/connectors/connect-paypal/src/types/index.ts +377 -0
- package/connectors/connect-paypal/src/utils/config.ts +125 -0
- package/connectors/connect-paypal/src/utils/output.ts +119 -0
- package/connectors/connect-paypal/tsconfig.json +16 -0
- package/connectors/connect-pinterest/.env.example +11 -0
- package/connectors/connect-pinterest/CLAUDE.md +115 -0
- package/connectors/connect-pinterest/README.md +193 -0
- package/connectors/connect-pinterest/package.json +52 -0
- package/connectors/connect-pinterest/src/api/client.ts +125 -0
- package/connectors/connect-pinterest/src/api/index.ts +203 -0
- package/connectors/connect-pinterest/src/cli/index.ts +653 -0
- package/connectors/connect-pinterest/src/index.ts +21 -0
- package/connectors/connect-pinterest/src/types/index.ts +268 -0
- package/connectors/connect-pinterest/src/utils/config.ts +157 -0
- package/connectors/connect-pinterest/src/utils/output.ts +119 -0
- package/connectors/connect-pinterest/tsconfig.json +16 -0
- package/connectors/connect-posthog/.env.example +11 -0
- package/connectors/connect-posthog/CLAUDE.md +272 -0
- package/connectors/connect-posthog/README.md +193 -0
- package/connectors/connect-posthog/package.json +51 -0
- package/connectors/connect-posthog/scripts/release.ts +179 -0
- package/connectors/connect-posthog/src/api/client.ts +213 -0
- package/connectors/connect-posthog/src/api/example.ts +48 -0
- package/connectors/connect-posthog/src/api/index.ts +51 -0
- package/connectors/connect-posthog/src/cli/index.ts +254 -0
- package/connectors/connect-posthog/src/index.ts +103 -0
- package/connectors/connect-posthog/src/types/index.ts +237 -0
- package/connectors/connect-posthog/src/utils/auth.ts +274 -0
- package/connectors/connect-posthog/src/utils/bulk.ts +212 -0
- package/connectors/connect-posthog/src/utils/config.ts +326 -0
- package/connectors/connect-posthog/src/utils/output.ts +175 -0
- package/connectors/connect-posthog/src/utils/settings.ts +114 -0
- package/connectors/connect-posthog/src/utils/storage.ts +198 -0
- package/connectors/connect-posthog/tsconfig.json +16 -0
- package/connectors/connect-salesforce/.env.example +11 -0
- package/connectors/connect-salesforce/CLAUDE.md +128 -0
- package/connectors/connect-salesforce/README.md +193 -0
- package/connectors/connect-salesforce/package.json +53 -0
- package/connectors/connect-salesforce/src/api/client.ts +150 -0
- package/connectors/connect-salesforce/src/api/index.ts +367 -0
- package/connectors/connect-salesforce/src/cli/index.ts +594 -0
- package/connectors/connect-salesforce/src/index.ts +21 -0
- package/connectors/connect-salesforce/src/types/index.ts +292 -0
- package/connectors/connect-salesforce/src/utils/config.ts +208 -0
- package/connectors/connect-salesforce/src/utils/output.ts +119 -0
- package/connectors/connect-salesforce/tsconfig.json +16 -0
- package/connectors/connect-segment/.env.example +11 -0
- package/connectors/connect-segment/CLAUDE.md +272 -0
- package/connectors/connect-segment/README.md +193 -0
- package/connectors/connect-segment/package.json +51 -0
- package/connectors/connect-segment/scripts/release.ts +179 -0
- package/connectors/connect-segment/src/api/client.ts +213 -0
- package/connectors/connect-segment/src/api/example.ts +48 -0
- package/connectors/connect-segment/src/api/index.ts +51 -0
- package/connectors/connect-segment/src/cli/index.ts +254 -0
- package/connectors/connect-segment/src/index.ts +103 -0
- package/connectors/connect-segment/src/types/index.ts +237 -0
- package/connectors/connect-segment/src/utils/auth.ts +274 -0
- package/connectors/connect-segment/src/utils/bulk.ts +212 -0
- package/connectors/connect-segment/src/utils/config.ts +326 -0
- package/connectors/connect-segment/src/utils/output.ts +175 -0
- package/connectors/connect-segment/src/utils/settings.ts +114 -0
- package/connectors/connect-segment/src/utils/storage.ts +198 -0
- package/connectors/connect-segment/tsconfig.json +16 -0
- package/connectors/connect-sendgrid/.env.example +11 -0
- package/connectors/connect-sendgrid/CLAUDE.md +125 -0
- package/connectors/connect-sendgrid/README.md +193 -0
- package/connectors/connect-sendgrid/package.json +53 -0
- package/connectors/connect-sendgrid/src/api/client.ts +137 -0
- package/connectors/connect-sendgrid/src/api/index.ts +588 -0
- package/connectors/connect-sendgrid/src/cli/index.ts +764 -0
- package/connectors/connect-sendgrid/src/index.ts +21 -0
- package/connectors/connect-sendgrid/src/types/index.ts +403 -0
- package/connectors/connect-sendgrid/src/utils/config.ts +197 -0
- package/connectors/connect-sendgrid/src/utils/output.ts +119 -0
- package/connectors/connect-sendgrid/tsconfig.json +16 -0
- package/connectors/connect-supabase/.env.example +11 -0
- package/connectors/connect-supabase/CLAUDE.md +132 -0
- package/connectors/connect-supabase/README.md +193 -0
- package/connectors/connect-supabase/package.json +52 -0
- package/connectors/connect-supabase/src/api/client.ts +231 -0
- package/connectors/connect-supabase/src/api/index.ts +439 -0
- package/connectors/connect-supabase/src/cli/index.ts +691 -0
- package/connectors/connect-supabase/src/index.ts +24 -0
- package/connectors/connect-supabase/src/types/index.ts +215 -0
- package/connectors/connect-supabase/src/utils/config.ts +219 -0
- package/connectors/connect-supabase/tsconfig.json +16 -0
- package/connectors/connect-vercel/.env.example +11 -0
- package/connectors/connect-vercel/CLAUDE.md +142 -0
- package/connectors/connect-vercel/README.md +193 -0
- package/connectors/connect-vercel/package.json +52 -0
- package/connectors/connect-vercel/src/api/client.ts +139 -0
- package/connectors/connect-vercel/src/api/index.ts +384 -0
- package/connectors/connect-vercel/src/cli/index.ts +740 -0
- package/connectors/connect-vercel/src/index.ts +24 -0
- package/connectors/connect-vercel/src/types/index.ts +350 -0
- package/connectors/connect-vercel/src/utils/config.ts +182 -0
- package/connectors/connect-vercel/src/utils/output.ts +119 -0
- package/connectors/connect-vercel/tsconfig.json +16 -0
- package/connectors/connect-zendesk/.env.example +10 -0
- package/connectors/connect-zendesk/.github/workflows/deploy.yml +51 -0
- package/connectors/connect-zendesk/CLAUDE.md +78 -0
- package/connectors/connect-zendesk/Makefile +129 -0
- package/connectors/connect-zendesk/README.md +295 -0
- package/connectors/connect-zendesk/SCAFFOLD.md +178 -0
- package/connectors/connect-zendesk/nginx-connector.conf +218 -0
- package/connectors/connect-zendesk/nginx.conf +61 -0
- package/connectors/connect-zendesk/package.json +71 -0
- package/connectors/connect-zendesk/scripts/init.sh +62 -0
- package/connectors/connect-zendesk/scripts/publish.ts +210 -0
- package/connectors/connect-zendesk/server/index.js +142 -0
- package/connectors/connect-zendesk/src/api/automations.ts +95 -0
- package/connectors/connect-zendesk/src/api/brands.ts +80 -0
- package/connectors/connect-zendesk/src/api/bulk.ts +838 -0
- package/connectors/connect-zendesk/src/api/client.test.ts +315 -0
- package/connectors/connect-zendesk/src/api/client.ts +173 -0
- package/connectors/connect-zendesk/src/api/example.ts +59 -0
- package/connectors/connect-zendesk/src/api/groups.ts +60 -0
- package/connectors/connect-zendesk/src/api/index.test.ts +111 -0
- package/connectors/connect-zendesk/src/api/index.ts +131 -0
- package/connectors/connect-zendesk/src/api/macros.ts +124 -0
- package/connectors/connect-zendesk/src/api/organizations.ts +85 -0
- package/connectors/connect-zendesk/src/api/slas.ts +80 -0
- package/connectors/connect-zendesk/src/api/ticket-fields.ts +98 -0
- package/connectors/connect-zendesk/src/api/tickets.test.ts +215 -0
- package/connectors/connect-zendesk/src/api/tickets.ts +103 -0
- package/connectors/connect-zendesk/src/api/triggers.test.ts +204 -0
- package/connectors/connect-zendesk/src/api/triggers.ts +125 -0
- package/connectors/connect-zendesk/src/api/users.test.ts +180 -0
- package/connectors/connect-zendesk/src/api/users.ts +108 -0
- package/connectors/connect-zendesk/src/api/views.test.ts +223 -0
- package/connectors/connect-zendesk/src/api/views.ts +145 -0
- package/connectors/connect-zendesk/src/api/webhooks.test.ts +208 -0
- package/connectors/connect-zendesk/src/api/webhooks.ts +118 -0
- package/connectors/connect-zendesk/src/cli/index.ts +1478 -0
- package/connectors/connect-zendesk/src/index.ts +36 -0
- package/connectors/connect-zendesk/src/server/index.ts +204 -0
- package/connectors/connect-zendesk/src/types/index.ts +910 -0
- package/connectors/connect-zendesk/src/utils/config.test.ts +193 -0
- package/connectors/connect-zendesk/src/utils/config.ts +526 -0
- package/connectors/connect-zendesk/src/utils/export.ts +338 -0
- package/connectors/connect-zendesk/src/utils/logger.ts +105 -0
- package/connectors/connect-zendesk/src/utils/output.test.ts +137 -0
- package/connectors/connect-zendesk/src/utils/output.ts +119 -0
- package/connectors/connect-zendesk/tsconfig.json +31 -0
- package/dist/index.js +175 -0
- package/package.json +1 -1
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
import type { ZendeskClient } from './client';
|
|
2
|
+
import type { TicketsApi } from './tickets';
|
|
3
|
+
import type { UsersApi } from './users';
|
|
4
|
+
import type { TicketFieldsApi } from './ticket-fields';
|
|
5
|
+
import type {
|
|
6
|
+
ZendeskTicket,
|
|
7
|
+
ZendeskUser,
|
|
8
|
+
ZendeskTicketField,
|
|
9
|
+
} from '../types';
|
|
10
|
+
|
|
11
|
+
// ============================================
|
|
12
|
+
// Filter Parser Types
|
|
13
|
+
// ============================================
|
|
14
|
+
|
|
15
|
+
export type FilterOperator =
|
|
16
|
+
| '=' | '!=' | '>' | '<' | '>=' | '<='
|
|
17
|
+
| 'contains' | 'starts_with' | 'ends_with'
|
|
18
|
+
| 'is_empty' | 'is_not_empty';
|
|
19
|
+
|
|
20
|
+
export interface ParsedFilter {
|
|
21
|
+
field: string;
|
|
22
|
+
operator: FilterOperator;
|
|
23
|
+
value: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ParsedUpdate {
|
|
27
|
+
field: string;
|
|
28
|
+
value: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ============================================
|
|
32
|
+
// Bulk Operation Types
|
|
33
|
+
// ============================================
|
|
34
|
+
|
|
35
|
+
export type BulkResourceType = 'tickets' | 'users';
|
|
36
|
+
|
|
37
|
+
export interface BulkUpdateOptions {
|
|
38
|
+
/** Resource type to update */
|
|
39
|
+
resourceType: BulkResourceType;
|
|
40
|
+
/** Simple filter string like "status=open" or "priority=high" */
|
|
41
|
+
where?: string;
|
|
42
|
+
/** Direct IDs to update */
|
|
43
|
+
ids?: number[];
|
|
44
|
+
/** Field updates to apply */
|
|
45
|
+
updates: ParsedUpdate[];
|
|
46
|
+
/** Maximum concurrent API calls (default: 3) */
|
|
47
|
+
concurrency?: number;
|
|
48
|
+
/** Dry run - don't actually update */
|
|
49
|
+
dryRun?: boolean;
|
|
50
|
+
/** Progress callback */
|
|
51
|
+
onProgress?: (current: number, total: number, item: ZendeskTicket | ZendeskUser) => void;
|
|
52
|
+
/** Error callback */
|
|
53
|
+
onError?: (error: Error, item: ZendeskTicket | ZendeskUser) => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface BulkUpdateResult {
|
|
57
|
+
total: number;
|
|
58
|
+
success: number;
|
|
59
|
+
failed: number;
|
|
60
|
+
skipped: number;
|
|
61
|
+
errors: Array<{ id: number; error: string }>;
|
|
62
|
+
jobStatus?: {
|
|
63
|
+
id: string;
|
|
64
|
+
status: string;
|
|
65
|
+
message?: string;
|
|
66
|
+
};
|
|
67
|
+
updatedItems: Array<ZendeskTicket | ZendeskUser>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface BulkPreviewResult {
|
|
71
|
+
items: Array<{
|
|
72
|
+
id: number;
|
|
73
|
+
title: string;
|
|
74
|
+
currentValues: Record<string, unknown>;
|
|
75
|
+
}>;
|
|
76
|
+
updates: ParsedUpdate[];
|
|
77
|
+
count: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================
|
|
81
|
+
// Schema Types
|
|
82
|
+
// ============================================
|
|
83
|
+
|
|
84
|
+
export interface FieldSchema {
|
|
85
|
+
name: string;
|
|
86
|
+
type: string;
|
|
87
|
+
description?: string;
|
|
88
|
+
options?: Array<{ name: string; value: string }>;
|
|
89
|
+
systemOptions?: Array<{ name: string; value: string }>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface BulkSchema {
|
|
93
|
+
tickets: {
|
|
94
|
+
fields: FieldSchema[];
|
|
95
|
+
statuses: string[];
|
|
96
|
+
priorities: string[];
|
|
97
|
+
types: string[];
|
|
98
|
+
};
|
|
99
|
+
users: {
|
|
100
|
+
fields: FieldSchema[];
|
|
101
|
+
roles: string[];
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ============================================
|
|
106
|
+
// Filter Parser
|
|
107
|
+
// ============================================
|
|
108
|
+
|
|
109
|
+
export class FilterParser {
|
|
110
|
+
/**
|
|
111
|
+
* Parse a simple filter string into Zendesk search query format
|
|
112
|
+
* Supports: field=value, field!=value, field>value, etc.
|
|
113
|
+
* Multiple conditions with & (AND) or | (OR)
|
|
114
|
+
*/
|
|
115
|
+
static parse(filterString: string): string {
|
|
116
|
+
// Handle OR conditions
|
|
117
|
+
if (filterString.includes('|')) {
|
|
118
|
+
const parts = filterString.split('|').map(p => p.trim());
|
|
119
|
+
// Zendesk doesn't support OR in the same way, so we'll use multiple queries
|
|
120
|
+
// For simplicity, we'll just take the first condition
|
|
121
|
+
return this.parseSingleCondition(parts[0]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Handle AND conditions
|
|
125
|
+
if (filterString.includes('&')) {
|
|
126
|
+
const parts = filterString.split('&').map(p => p.trim());
|
|
127
|
+
return parts.map(part => this.parseSingleCondition(part)).join(' ');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Single condition
|
|
131
|
+
return this.parseSingleCondition(filterString);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Parse a single filter condition into Zendesk query format
|
|
136
|
+
*/
|
|
137
|
+
private static parseSingleCondition(condition: string): string {
|
|
138
|
+
// Try different operators in order of specificity
|
|
139
|
+
const operators: Array<{ op: string; zendeskOp: string }> = [
|
|
140
|
+
{ op: '!=', zendeskOp: '-' },
|
|
141
|
+
{ op: '>=', zendeskOp: '>=' },
|
|
142
|
+
{ op: '<=', zendeskOp: '<=' },
|
|
143
|
+
{ op: '>', zendeskOp: '>' },
|
|
144
|
+
{ op: '<', zendeskOp: '<' },
|
|
145
|
+
{ op: '=', zendeskOp: ':' },
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
for (const { op, zendeskOp } of operators) {
|
|
149
|
+
const idx = condition.indexOf(op);
|
|
150
|
+
if (idx !== -1) {
|
|
151
|
+
const field = condition.substring(0, idx).trim().toLowerCase();
|
|
152
|
+
let value = condition.substring(idx + op.length).trim();
|
|
153
|
+
|
|
154
|
+
// Handle values with spaces by quoting
|
|
155
|
+
if (value.includes(' ')) {
|
|
156
|
+
value = `"${value}"`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Map common field names to Zendesk search terms
|
|
160
|
+
const mappedField = this.mapFieldName(field);
|
|
161
|
+
|
|
162
|
+
// Handle negation
|
|
163
|
+
if (zendeskOp === '-') {
|
|
164
|
+
return `-${mappedField}:${value}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return `${mappedField}${zendeskOp}${value}`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
throw new Error(`Invalid filter condition: ${condition}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Map common field names to Zendesk search field names
|
|
176
|
+
*/
|
|
177
|
+
private static mapFieldName(field: string): string {
|
|
178
|
+
const fieldMap: Record<string, string> = {
|
|
179
|
+
// Ticket fields
|
|
180
|
+
'status': 'status',
|
|
181
|
+
'priority': 'priority',
|
|
182
|
+
'type': 'ticket_type',
|
|
183
|
+
'ticket_type': 'ticket_type',
|
|
184
|
+
'assignee': 'assignee',
|
|
185
|
+
'assignee_id': 'assignee',
|
|
186
|
+
'requester': 'requester',
|
|
187
|
+
'requester_id': 'requester',
|
|
188
|
+
'group': 'group',
|
|
189
|
+
'group_id': 'group',
|
|
190
|
+
'organization': 'organization',
|
|
191
|
+
'organization_id': 'organization',
|
|
192
|
+
'tag': 'tags',
|
|
193
|
+
'tags': 'tags',
|
|
194
|
+
'subject': 'subject',
|
|
195
|
+
'description': 'description',
|
|
196
|
+
'created': 'created',
|
|
197
|
+
'created_at': 'created',
|
|
198
|
+
'updated': 'updated',
|
|
199
|
+
'updated_at': 'updated',
|
|
200
|
+
'solved': 'solved',
|
|
201
|
+
'due': 'due_date',
|
|
202
|
+
'due_date': 'due_date',
|
|
203
|
+
'brand': 'brand',
|
|
204
|
+
'brand_id': 'brand',
|
|
205
|
+
// User fields
|
|
206
|
+
'role': 'role',
|
|
207
|
+
'name': 'name',
|
|
208
|
+
'email': 'email',
|
|
209
|
+
'phone': 'phone',
|
|
210
|
+
'external_id': 'external_id',
|
|
211
|
+
'suspended': 'suspended',
|
|
212
|
+
'verified': 'verified',
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return fieldMap[field] || field;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Parse a field update string like "status=solved" or "priority=high"
|
|
220
|
+
*/
|
|
221
|
+
static parseUpdate(updateString: string): ParsedUpdate {
|
|
222
|
+
const idx = updateString.indexOf('=');
|
|
223
|
+
if (idx === -1) {
|
|
224
|
+
throw new Error(`Invalid update format: ${updateString}. Expected "field=value"`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
field: updateString.substring(0, idx).trim().toLowerCase(),
|
|
229
|
+
value: updateString.substring(idx + 1).trim(),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Build a Zendesk search query from multiple filter strings
|
|
235
|
+
*/
|
|
236
|
+
static buildSearchQuery(resourceType: BulkResourceType, filters: string[]): string {
|
|
237
|
+
const parts: string[] = [];
|
|
238
|
+
|
|
239
|
+
// Add type prefix for tickets
|
|
240
|
+
if (resourceType === 'tickets') {
|
|
241
|
+
parts.push('type:ticket');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Parse and add each filter
|
|
245
|
+
for (const filter of filters) {
|
|
246
|
+
if (filter.trim()) {
|
|
247
|
+
parts.push(this.parse(filter));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return parts.join(' ');
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ============================================
|
|
256
|
+
// Bulk Operations API
|
|
257
|
+
// ============================================
|
|
258
|
+
|
|
259
|
+
export class BulkApi {
|
|
260
|
+
constructor(
|
|
261
|
+
private readonly client: ZendeskClient,
|
|
262
|
+
private readonly tickets: TicketsApi,
|
|
263
|
+
private readonly users: UsersApi,
|
|
264
|
+
private readonly ticketFields: TicketFieldsApi
|
|
265
|
+
) {}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Bulk update resources using Zendesk's native bulk update endpoints
|
|
269
|
+
* @see https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/#update-many-tickets
|
|
270
|
+
*/
|
|
271
|
+
async update(options: BulkUpdateOptions): Promise<BulkUpdateResult> {
|
|
272
|
+
const {
|
|
273
|
+
resourceType,
|
|
274
|
+
where,
|
|
275
|
+
ids,
|
|
276
|
+
updates,
|
|
277
|
+
concurrency = 3,
|
|
278
|
+
dryRun = false,
|
|
279
|
+
onProgress,
|
|
280
|
+
onError,
|
|
281
|
+
} = options;
|
|
282
|
+
|
|
283
|
+
const result: BulkUpdateResult = {
|
|
284
|
+
total: 0,
|
|
285
|
+
success: 0,
|
|
286
|
+
failed: 0,
|
|
287
|
+
skipped: 0,
|
|
288
|
+
errors: [],
|
|
289
|
+
updatedItems: [],
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// Get items to update
|
|
293
|
+
let itemIds: number[] = [];
|
|
294
|
+
|
|
295
|
+
if (ids && ids.length > 0) {
|
|
296
|
+
itemIds = ids;
|
|
297
|
+
} else if (where) {
|
|
298
|
+
// Search for matching items
|
|
299
|
+
const searchResults = await this.search(resourceType, where);
|
|
300
|
+
itemIds = searchResults.map(item => item.id);
|
|
301
|
+
} else {
|
|
302
|
+
throw new Error('Either "where" filter or "ids" must be provided');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
result.total = itemIds.length;
|
|
306
|
+
|
|
307
|
+
if (itemIds.length === 0) {
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Build the update payload
|
|
312
|
+
const updatePayload = this.buildUpdatePayload(resourceType, updates);
|
|
313
|
+
|
|
314
|
+
if (dryRun) {
|
|
315
|
+
// In dry run mode, just report what would be updated
|
|
316
|
+
result.success = itemIds.length;
|
|
317
|
+
result.skipped = 0;
|
|
318
|
+
|
|
319
|
+
// Fetch items for reporting
|
|
320
|
+
if (resourceType === 'tickets') {
|
|
321
|
+
const tickets = await this.fetchTicketsByIds(itemIds.slice(0, 100)); // Limit for preview
|
|
322
|
+
result.updatedItems = tickets;
|
|
323
|
+
} else if (resourceType === 'users') {
|
|
324
|
+
const users = await this.fetchUsersByIds(itemIds.slice(0, 100));
|
|
325
|
+
result.updatedItems = users;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Use Zendesk's native bulk update for tickets
|
|
332
|
+
if (resourceType === 'tickets') {
|
|
333
|
+
return this.bulkUpdateTickets(itemIds, updatePayload, result, onProgress, onError);
|
|
334
|
+
} else if (resourceType === 'users') {
|
|
335
|
+
return this.bulkUpdateUsers(itemIds, updatePayload, result, concurrency, onProgress, onError);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Bulk update tickets using Zendesk's native update_many endpoint
|
|
343
|
+
*/
|
|
344
|
+
private async bulkUpdateTickets(
|
|
345
|
+
ticketIds: number[],
|
|
346
|
+
updatePayload: Record<string, unknown>,
|
|
347
|
+
result: BulkUpdateResult,
|
|
348
|
+
onProgress?: (current: number, total: number, item: ZendeskTicket | ZendeskUser) => void,
|
|
349
|
+
onError?: (error: Error, item: ZendeskTicket | ZendeskUser) => void
|
|
350
|
+
): Promise<BulkUpdateResult> {
|
|
351
|
+
// Zendesk allows up to 100 tickets per bulk update
|
|
352
|
+
const batchSize = 100;
|
|
353
|
+
const batches = this.chunkArray(ticketIds, batchSize);
|
|
354
|
+
|
|
355
|
+
for (const batch of batches) {
|
|
356
|
+
try {
|
|
357
|
+
// Use PUT /api/v2/tickets/update_many.json?ids=1,2,3
|
|
358
|
+
const response = await this.client.put<{
|
|
359
|
+
job_status: { id: string; status: string; message?: string };
|
|
360
|
+
}>(
|
|
361
|
+
`/tickets/update_many.json`,
|
|
362
|
+
{ ticket: updatePayload },
|
|
363
|
+
{ ids: batch.join(',') }
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
result.jobStatus = {
|
|
367
|
+
id: response.job_status.id,
|
|
368
|
+
status: response.job_status.status,
|
|
369
|
+
message: response.job_status.message,
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
result.success += batch.length;
|
|
373
|
+
|
|
374
|
+
// Report progress
|
|
375
|
+
if (onProgress) {
|
|
376
|
+
const dummyTicket = { id: batch[0] } as ZendeskTicket;
|
|
377
|
+
onProgress(result.success, result.total, dummyTicket);
|
|
378
|
+
}
|
|
379
|
+
} catch (err) {
|
|
380
|
+
result.failed += batch.length;
|
|
381
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
382
|
+
for (const id of batch) {
|
|
383
|
+
result.errors.push({ id, error: errorMessage });
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (onError) {
|
|
387
|
+
const dummyTicket = { id: batch[0] } as ZendeskTicket;
|
|
388
|
+
onError(err instanceof Error ? err : new Error(errorMessage), dummyTicket);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return result;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Bulk update users using Zendesk's native update_many endpoint
|
|
398
|
+
*/
|
|
399
|
+
private async bulkUpdateUsers(
|
|
400
|
+
userIds: number[],
|
|
401
|
+
updatePayload: Record<string, unknown>,
|
|
402
|
+
result: BulkUpdateResult,
|
|
403
|
+
concurrency: number,
|
|
404
|
+
onProgress?: (current: number, total: number, item: ZendeskTicket | ZendeskUser) => void,
|
|
405
|
+
onError?: (error: Error, item: ZendeskTicket | ZendeskUser) => void
|
|
406
|
+
): Promise<BulkUpdateResult> {
|
|
407
|
+
// Zendesk allows up to 100 users per bulk update
|
|
408
|
+
const batchSize = 100;
|
|
409
|
+
const batches = this.chunkArray(userIds, batchSize);
|
|
410
|
+
|
|
411
|
+
for (const batch of batches) {
|
|
412
|
+
try {
|
|
413
|
+
// Build users array for bulk update
|
|
414
|
+
const usersToUpdate = batch.map(id => ({
|
|
415
|
+
id,
|
|
416
|
+
...updatePayload,
|
|
417
|
+
}));
|
|
418
|
+
|
|
419
|
+
// Use PUT /api/v2/users/update_many.json
|
|
420
|
+
const response = await this.client.put<{
|
|
421
|
+
job_status: { id: string; status: string; message?: string };
|
|
422
|
+
}>('/users/update_many.json', { users: usersToUpdate });
|
|
423
|
+
|
|
424
|
+
result.jobStatus = {
|
|
425
|
+
id: response.job_status.id,
|
|
426
|
+
status: response.job_status.status,
|
|
427
|
+
message: response.job_status.message,
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
result.success += batch.length;
|
|
431
|
+
|
|
432
|
+
// Report progress
|
|
433
|
+
if (onProgress) {
|
|
434
|
+
const dummyUser = { id: batch[0] } as ZendeskUser;
|
|
435
|
+
onProgress(result.success, result.total, dummyUser);
|
|
436
|
+
}
|
|
437
|
+
} catch (err) {
|
|
438
|
+
result.failed += batch.length;
|
|
439
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
440
|
+
for (const id of batch) {
|
|
441
|
+
result.errors.push({ id, error: errorMessage });
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (onError) {
|
|
445
|
+
const dummyUser = { id: batch[0] } as ZendeskUser;
|
|
446
|
+
onError(err instanceof Error ? err : new Error(errorMessage), dummyUser);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return result;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Search for resources matching a filter
|
|
456
|
+
*/
|
|
457
|
+
private async search(resourceType: BulkResourceType, where: string): Promise<Array<{ id: number }>> {
|
|
458
|
+
const query = FilterParser.buildSearchQuery(resourceType, [where]);
|
|
459
|
+
|
|
460
|
+
if (resourceType === 'tickets') {
|
|
461
|
+
const response = await this.client.get<{
|
|
462
|
+
results: Array<{ id: number }>;
|
|
463
|
+
next_page?: string;
|
|
464
|
+
}>('/search.json', { query });
|
|
465
|
+
|
|
466
|
+
// Paginate through all results
|
|
467
|
+
let allResults = [...response.results];
|
|
468
|
+
let nextPage = response.next_page;
|
|
469
|
+
|
|
470
|
+
while (nextPage) {
|
|
471
|
+
const pageResponse = await fetch(nextPage, {
|
|
472
|
+
headers: {
|
|
473
|
+
'Authorization': `Basic ${Buffer.from(`${process.env.ZENDESK_EMAIL}/token:${process.env.ZENDESK_API_TOKEN}`).toString('base64')}`,
|
|
474
|
+
'Accept': 'application/json',
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
const pageData = await pageResponse.json() as {
|
|
478
|
+
results: Array<{ id: number }>;
|
|
479
|
+
next_page?: string;
|
|
480
|
+
};
|
|
481
|
+
allResults = [...allResults, ...pageData.results];
|
|
482
|
+
nextPage = pageData.next_page;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return allResults;
|
|
486
|
+
} else if (resourceType === 'users') {
|
|
487
|
+
const response = await this.client.get<{
|
|
488
|
+
users: Array<{ id: number }>;
|
|
489
|
+
next_page?: string;
|
|
490
|
+
}>('/users/search.json', { query: where });
|
|
491
|
+
|
|
492
|
+
return response.users;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return [];
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Build the update payload for Zendesk API
|
|
500
|
+
*/
|
|
501
|
+
private buildUpdatePayload(
|
|
502
|
+
resourceType: BulkResourceType,
|
|
503
|
+
updates: ParsedUpdate[]
|
|
504
|
+
): Record<string, unknown> {
|
|
505
|
+
const payload: Record<string, unknown> = {};
|
|
506
|
+
|
|
507
|
+
for (const update of updates) {
|
|
508
|
+
const field = this.normalizeFieldName(resourceType, update.field);
|
|
509
|
+
payload[field] = this.parseFieldValue(field, update.value);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return payload;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Normalize field names to Zendesk API format
|
|
517
|
+
*/
|
|
518
|
+
private normalizeFieldName(resourceType: BulkResourceType, field: string): string {
|
|
519
|
+
const ticketFieldMap: Record<string, string> = {
|
|
520
|
+
'status': 'status',
|
|
521
|
+
'priority': 'priority',
|
|
522
|
+
'type': 'type',
|
|
523
|
+
'ticket_type': 'type',
|
|
524
|
+
'assignee': 'assignee_id',
|
|
525
|
+
'assignee_id': 'assignee_id',
|
|
526
|
+
'group': 'group_id',
|
|
527
|
+
'group_id': 'group_id',
|
|
528
|
+
'subject': 'subject',
|
|
529
|
+
'tags': 'tags',
|
|
530
|
+
'tag': 'tags',
|
|
531
|
+
'due': 'due_at',
|
|
532
|
+
'due_at': 'due_at',
|
|
533
|
+
'due_date': 'due_at',
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
const userFieldMap: Record<string, string> = {
|
|
537
|
+
'role': 'role',
|
|
538
|
+
'name': 'name',
|
|
539
|
+
'email': 'email',
|
|
540
|
+
'phone': 'phone',
|
|
541
|
+
'suspended': 'suspended',
|
|
542
|
+
'verified': 'verified',
|
|
543
|
+
'notes': 'notes',
|
|
544
|
+
'details': 'details',
|
|
545
|
+
'organization': 'organization_id',
|
|
546
|
+
'organization_id': 'organization_id',
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
const fieldMap = resourceType === 'tickets' ? ticketFieldMap : userFieldMap;
|
|
550
|
+
return fieldMap[field] || field;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Parse field value into appropriate type
|
|
555
|
+
*/
|
|
556
|
+
private parseFieldValue(field: string, value: string): unknown {
|
|
557
|
+
// Boolean fields
|
|
558
|
+
if (['suspended', 'verified'].includes(field)) {
|
|
559
|
+
return value.toLowerCase() === 'true';
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// ID fields
|
|
563
|
+
if (field.endsWith('_id')) {
|
|
564
|
+
return parseInt(value, 10);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Tags (comma-separated)
|
|
568
|
+
if (field === 'tags') {
|
|
569
|
+
return value.split(',').map(t => t.trim());
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return value;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Fetch tickets by IDs
|
|
577
|
+
*/
|
|
578
|
+
private async fetchTicketsByIds(ids: number[]): Promise<ZendeskTicket[]> {
|
|
579
|
+
const response = await this.client.get<{ tickets: ZendeskTicket[] }>(
|
|
580
|
+
'/tickets/show_many.json',
|
|
581
|
+
{ ids: ids.join(',') }
|
|
582
|
+
);
|
|
583
|
+
return response.tickets;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Fetch users by IDs
|
|
588
|
+
*/
|
|
589
|
+
private async fetchUsersByIds(ids: number[]): Promise<ZendeskUser[]> {
|
|
590
|
+
const response = await this.client.get<{ users: ZendeskUser[] }>(
|
|
591
|
+
'/users/show_many.json',
|
|
592
|
+
{ ids: ids.join(',') }
|
|
593
|
+
);
|
|
594
|
+
return response.users;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Preview what a bulk update would affect
|
|
599
|
+
*/
|
|
600
|
+
async preview(options: Omit<BulkUpdateOptions, 'dryRun'>): Promise<BulkPreviewResult> {
|
|
601
|
+
const { resourceType, where, ids, updates } = options;
|
|
602
|
+
|
|
603
|
+
let items: Array<ZendeskTicket | ZendeskUser> = [];
|
|
604
|
+
|
|
605
|
+
if (ids && ids.length > 0) {
|
|
606
|
+
if (resourceType === 'tickets') {
|
|
607
|
+
items = await this.fetchTicketsByIds(ids);
|
|
608
|
+
} else {
|
|
609
|
+
items = await this.fetchUsersByIds(ids);
|
|
610
|
+
}
|
|
611
|
+
} else if (where) {
|
|
612
|
+
const searchResults = await this.search(resourceType, where);
|
|
613
|
+
const resultIds = searchResults.map(r => r.id).slice(0, 100); // Limit preview to 100
|
|
614
|
+
|
|
615
|
+
if (resourceType === 'tickets') {
|
|
616
|
+
items = resultIds.length > 0 ? await this.fetchTicketsByIds(resultIds) : [];
|
|
617
|
+
} else {
|
|
618
|
+
items = resultIds.length > 0 ? await this.fetchUsersByIds(resultIds) : [];
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return {
|
|
623
|
+
items: items.map(item => ({
|
|
624
|
+
id: item.id,
|
|
625
|
+
title: this.getItemTitle(item, resourceType),
|
|
626
|
+
currentValues: this.extractCurrentValues(item, updates),
|
|
627
|
+
})),
|
|
628
|
+
updates,
|
|
629
|
+
count: items.length,
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Get a readable title for an item
|
|
635
|
+
*/
|
|
636
|
+
private getItemTitle(item: ZendeskTicket | ZendeskUser, resourceType: BulkResourceType): string {
|
|
637
|
+
if (resourceType === 'tickets') {
|
|
638
|
+
const ticket = item as ZendeskTicket;
|
|
639
|
+
return ticket.subject || `Ticket #${ticket.id}`;
|
|
640
|
+
} else {
|
|
641
|
+
const user = item as ZendeskUser;
|
|
642
|
+
return user.name || user.email || `User #${user.id}`;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Extract current values for the fields being updated
|
|
648
|
+
*/
|
|
649
|
+
private extractCurrentValues(
|
|
650
|
+
item: ZendeskTicket | ZendeskUser,
|
|
651
|
+
updates: ParsedUpdate[]
|
|
652
|
+
): Record<string, unknown> {
|
|
653
|
+
const values: Record<string, unknown> = {};
|
|
654
|
+
|
|
655
|
+
for (const update of updates) {
|
|
656
|
+
const field = update.field;
|
|
657
|
+
const itemAny = item as Record<string, unknown>;
|
|
658
|
+
|
|
659
|
+
// Handle field name mappings
|
|
660
|
+
const mappings: Record<string, string> = {
|
|
661
|
+
'assignee': 'assignee_id',
|
|
662
|
+
'group': 'group_id',
|
|
663
|
+
'organization': 'organization_id',
|
|
664
|
+
'type': 'type',
|
|
665
|
+
'ticket_type': 'type',
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
const actualField = mappings[field] || field;
|
|
669
|
+
values[field] = itemAny[actualField];
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return values;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Get schema information for bulk operations
|
|
677
|
+
*/
|
|
678
|
+
async getSchema(): Promise<BulkSchema> {
|
|
679
|
+
// Fetch ticket fields from Zendesk
|
|
680
|
+
const ticketFields = await this.ticketFields.list();
|
|
681
|
+
|
|
682
|
+
return {
|
|
683
|
+
tickets: {
|
|
684
|
+
fields: this.mapTicketFieldsToSchema(ticketFields),
|
|
685
|
+
statuses: ['new', 'open', 'pending', 'hold', 'solved', 'closed'],
|
|
686
|
+
priorities: ['urgent', 'high', 'normal', 'low'],
|
|
687
|
+
types: ['problem', 'incident', 'question', 'task'],
|
|
688
|
+
},
|
|
689
|
+
users: {
|
|
690
|
+
fields: [
|
|
691
|
+
{ name: 'name', type: 'string', description: 'User name' },
|
|
692
|
+
{ name: 'email', type: 'string', description: 'User email address' },
|
|
693
|
+
{ name: 'phone', type: 'string', description: 'User phone number' },
|
|
694
|
+
{ name: 'role', type: 'select', description: 'User role', options: [
|
|
695
|
+
{ name: 'End-user', value: 'end-user' },
|
|
696
|
+
{ name: 'Agent', value: 'agent' },
|
|
697
|
+
{ name: 'Admin', value: 'admin' },
|
|
698
|
+
]},
|
|
699
|
+
{ name: 'suspended', type: 'boolean', description: 'Whether user is suspended' },
|
|
700
|
+
{ name: 'verified', type: 'boolean', description: 'Whether user email is verified' },
|
|
701
|
+
{ name: 'notes', type: 'text', description: 'User notes' },
|
|
702
|
+
{ name: 'details', type: 'text', description: 'User details' },
|
|
703
|
+
{ name: 'organization_id', type: 'number', description: 'Organization ID' },
|
|
704
|
+
],
|
|
705
|
+
roles: ['end-user', 'agent', 'admin'],
|
|
706
|
+
},
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Map Zendesk ticket fields to schema format
|
|
712
|
+
*/
|
|
713
|
+
private mapTicketFieldsToSchema(fields: ZendeskTicketField[]): FieldSchema[] {
|
|
714
|
+
const systemFields: FieldSchema[] = [
|
|
715
|
+
{ name: 'status', type: 'select', description: 'Ticket status', options: [
|
|
716
|
+
{ name: 'New', value: 'new' },
|
|
717
|
+
{ name: 'Open', value: 'open' },
|
|
718
|
+
{ name: 'Pending', value: 'pending' },
|
|
719
|
+
{ name: 'Hold', value: 'hold' },
|
|
720
|
+
{ name: 'Solved', value: 'solved' },
|
|
721
|
+
{ name: 'Closed', value: 'closed' },
|
|
722
|
+
]},
|
|
723
|
+
{ name: 'priority', type: 'select', description: 'Ticket priority', options: [
|
|
724
|
+
{ name: 'Urgent', value: 'urgent' },
|
|
725
|
+
{ name: 'High', value: 'high' },
|
|
726
|
+
{ name: 'Normal', value: 'normal' },
|
|
727
|
+
{ name: 'Low', value: 'low' },
|
|
728
|
+
]},
|
|
729
|
+
{ name: 'type', type: 'select', description: 'Ticket type', options: [
|
|
730
|
+
{ name: 'Problem', value: 'problem' },
|
|
731
|
+
{ name: 'Incident', value: 'incident' },
|
|
732
|
+
{ name: 'Question', value: 'question' },
|
|
733
|
+
{ name: 'Task', value: 'task' },
|
|
734
|
+
]},
|
|
735
|
+
{ name: 'subject', type: 'text', description: 'Ticket subject' },
|
|
736
|
+
{ name: 'assignee_id', type: 'number', description: 'Assignee user ID' },
|
|
737
|
+
{ name: 'group_id', type: 'number', description: 'Group ID' },
|
|
738
|
+
{ name: 'tags', type: 'tags', description: 'Ticket tags (comma-separated)' },
|
|
739
|
+
{ name: 'due_at', type: 'date', description: 'Due date (ISO 8601 format)' },
|
|
740
|
+
];
|
|
741
|
+
|
|
742
|
+
// Add custom fields
|
|
743
|
+
const customFields = fields
|
|
744
|
+
.filter(f => f.active && !f.title.startsWith('System'))
|
|
745
|
+
.map(f => ({
|
|
746
|
+
name: `custom_fields.${f.id}`,
|
|
747
|
+
type: f.type,
|
|
748
|
+
description: f.description || f.title,
|
|
749
|
+
options: f.custom_field_options?.map(opt => ({
|
|
750
|
+
name: opt.name,
|
|
751
|
+
value: opt.value,
|
|
752
|
+
})),
|
|
753
|
+
systemOptions: f.system_field_options?.map(opt => ({
|
|
754
|
+
name: opt.name,
|
|
755
|
+
value: opt.value,
|
|
756
|
+
})),
|
|
757
|
+
}));
|
|
758
|
+
|
|
759
|
+
return [...systemFields, ...customFields];
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Split array into chunks
|
|
764
|
+
*/
|
|
765
|
+
private chunkArray<T>(array: T[], size: number): T[][] {
|
|
766
|
+
const chunks: T[][] = [];
|
|
767
|
+
for (let i = 0; i < array.length; i += size) {
|
|
768
|
+
chunks.push(array.slice(i, i + size));
|
|
769
|
+
}
|
|
770
|
+
return chunks;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Check the status of a bulk job
|
|
775
|
+
* @see https://developer.zendesk.com/api-reference/ticketing/job_statuses/job_statuses/
|
|
776
|
+
*/
|
|
777
|
+
async getJobStatus(jobId: string): Promise<{
|
|
778
|
+
id: string;
|
|
779
|
+
status: string;
|
|
780
|
+
progress: number;
|
|
781
|
+
total: number;
|
|
782
|
+
message?: string;
|
|
783
|
+
results?: Array<{ id: number; success: boolean; error?: string }>;
|
|
784
|
+
}> {
|
|
785
|
+
const response = await this.client.get<{
|
|
786
|
+
job_status: {
|
|
787
|
+
id: string;
|
|
788
|
+
status: string;
|
|
789
|
+
progress: number;
|
|
790
|
+
total: number;
|
|
791
|
+
message?: string;
|
|
792
|
+
results?: Array<{ id: number; success: boolean; error?: string }>;
|
|
793
|
+
};
|
|
794
|
+
}>(`/job_statuses/${jobId}.json`);
|
|
795
|
+
|
|
796
|
+
return response.job_status;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Wait for a bulk job to complete
|
|
801
|
+
*/
|
|
802
|
+
async waitForJob(
|
|
803
|
+
jobId: string,
|
|
804
|
+
options: {
|
|
805
|
+
pollInterval?: number;
|
|
806
|
+
timeout?: number;
|
|
807
|
+
onProgress?: (progress: number, total: number) => void;
|
|
808
|
+
} = {}
|
|
809
|
+
): Promise<{
|
|
810
|
+
id: string;
|
|
811
|
+
status: string;
|
|
812
|
+
progress: number;
|
|
813
|
+
total: number;
|
|
814
|
+
message?: string;
|
|
815
|
+
results?: Array<{ id: number; success: boolean; error?: string }>;
|
|
816
|
+
}> {
|
|
817
|
+
const { pollInterval = 1000, timeout = 300000 } = options;
|
|
818
|
+
const startTime = Date.now();
|
|
819
|
+
|
|
820
|
+
while (true) {
|
|
821
|
+
const status = await this.getJobStatus(jobId);
|
|
822
|
+
|
|
823
|
+
if (options.onProgress) {
|
|
824
|
+
options.onProgress(status.progress, status.total);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
if (status.status === 'completed' || status.status === 'failed') {
|
|
828
|
+
return status;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (Date.now() - startTime > timeout) {
|
|
832
|
+
throw new Error(`Job ${jobId} timed out after ${timeout}ms`);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|