Skip to content

feat: connect Gong call sync to customer requests#717

Open
jaeyunha wants to merge 2 commits into
stagingfrom
issue-579-p3-gong-connect-customer-calls-to
Open

feat: connect Gong call sync to customer requests#717
jaeyunha wants to merge 2 commits into
stagingfrom
issue-579-p3-gong-connect-customer-calls-to

Conversation

@jaeyunha

Copy link
Copy Markdown
Member

Summary

  • add a Gong sync worker that schedules provider sync jobs, pages Gong calls/transcripts, ingests customer-facing findings, and records degraded/error provider events for sync failures
  • persist safe Gong integration details for admin health/configuration and send routing guidance / participant mention settings during connect
  • rename the Gong customer-request migration to the next migration prefix after the GitHub integration migration

Verification

  • pnpm --filter @exponential/web exec vitest run tests/integrations-view.test.tsx
  • pnpm exec biome check 'apps/web/src/app/(app)/settings/integrations/page.tsx' apps/web/tests/integrations-view.test.tsx
  • node scripts/check-migrations.mjs
  • node scripts/check-openapi-coverage.mjs

Blocked checks

  • make check: blocked in typecheck by existing missing web deps/types (drizzle-orm, better-auth) after SDK dist build; no changed files involved.
  • make test / focused Go test: blocked because go is not installed on PATH in this worktree environment.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fbdaf6b487

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

values.Set("client_secret", clientSecret)
values.Set("code", code)
values.Set("redirect_uri", redirectURI)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, gongAPIBaseURL()+"/oauth2/token", strings.NewReader(values.Encode()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exchange Gong OAuth codes at the documented token endpoint

Gong's OAuth setup docs show the authorization-code exchange going to /oauth2/generate-customer-token, not /oauth2/token (Gong docs). With the real Gong host, this callback will post the code to a non-existent token endpoint, treat the non-2xx response as an exchange failure, and leave every attempted Gong install in error, so workspaces cannot connect the integration outside mocked tests.

Useful? React with 👍 / 👎.

Comment on lines +50 to +58
type gongOAuthResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
TokenType string `json:"token_type"`
Scope string `json:"scope"`
ExpiresIn int `json:"expires_in"`
TenantID string `json:"tenant_id"`
CompanyID string `json:"company_id"`
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Store the customer API base URL from Gong OAuth

Gong OAuth returns a tenant-specific api_base_url_for_customer for subsequent API calls, but this response type drops that field, so completeGongInstall cannot persist it and the worker later posts all /v2/calls/* requests to the process-wide gongAPIBaseURL() instead. For real OAuth installs whose API host is customer-specific, sync jobs will call the wrong host and either fail or be treated as empty results; capture the returned base URL in the credential/metadata and use it when building Gong API requests.

Useful? React with 👍 / 👎.

@jaeyunha jaeyunha force-pushed the issue-579-p3-gong-connect-customer-calls-to branch from fbdaf6b to 1b54628 Compare June 17, 2026 05:15

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

CREATE TABLE IF NOT EXISTS customer (

P2 Badge Restore the original Gong migration filename

This is a rename of an existing migration (0010_customer_requests_gong.sql) rather than a new forward migration, but packages/proto/migrations/README.md says "Never rename a migration that may have been applied anywhere" because the runner records applied migrations by filename. Any environment that already applied the old filename will treat this identical file as a new migration on deploy and record/run it again, so keep the original migration file and add a separate forward migration only for new schema changes.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +223 to +226
if credential.AccessToken == "" {
return gongCredential{}, fmt.Errorf("active Gong credential is missing access token")
}
return credential, nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Refresh expired Gong access tokens

When Gong's OAuth access token expires, this path still returns the stored accessToken and postGongJSON sends it unchanged; Gong's OAuth docs state the token expires periodically and should be refreshed with the provided refresh token (https://help.gong.io/docs/create-an-app-for-gong). In that scenario the next sync gets a 401/403, failGongJob marks the integration error, and future enqueues skip it, so real installs stop syncing after the first token lifetime unless the user reconnects; refresh and persist the new token before API calls or on authentication failure.

Useful? React with 👍 / 👎.

@jaeyunha

Copy link
Copy Markdown
Member Author

Controller disposition for current head 1b54628: validation-blocked; do not merge as-is.

Evidence:

  • PR feat: connect Gong call sync to customer requests #717 targets staging, GitHub reports mergeable=CONFLICTING/DIRTY, and there are no CI checks.
  • Disposable merge simulation against origin/staging conflicts in apps/api/internal/integrations/handler.go.
  • Diff is backend-only Gong sync/config (apps/api/internal/integrations/* plus migration), but P3 Gong: connect customer calls to customer requests and issue context #579/spec-prep requires the full Gong contract: admin connect/configure/reconnect/disconnect UI/E2E, authorized issue/customer-request source-link visibility, privacy/private-team denial coverage, degraded/provider permission handling, outbound/no-op behavior, and OpenAPI/SDK updates for API contract changes.

Required before merge:

@jaeyunha jaeyunha force-pushed the issue-579-p3-gong-connect-customer-calls-to branch from 1b54628 to e3367fb Compare June 17, 2026 07:14

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3367fb21c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if _, err := tx.Exec(ctx, `update provider_job set status=$2, last_error=$3, next_run_at=$4, completed_at=case when $2='dead' then $5 else completed_at end, updated_at=$5 where id=$1::uuid`, job.ID, status, cause.Error(), nextRunAt, now); err != nil {
return err
}
if _, err := tx.Exec(ctx, `update workspace_integration set status='degraded', last_failure_at=$2, last_failure_message=$3, last_event_at=$2, updated_at=$2 where id=$1::uuid`, job.IntegrationID, now, cause.Error()); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mark exhausted Gong syncs as error

When a Gong sync keeps failing until providerJobFailureStatus returns dead, this statement still leaves the integration degraded. enqueueDueGongSync selects degraded integrations and only suppresses queued/running/failed jobs, so after max attempts the next poll creates a brand-new sync job for the same broken integration and repeats forever (for example with revoked scopes or a bad API host). Set the integration to error or otherwise exclude dead integrations once retries are exhausted.

Useful? React with 👍 / 👎.

}
var id string
err := tx.QueryRow(ctx, `insert into customer (workspace_id, name, domain, source_provider, source_external_id, metadata, created_at, updated_at) values ($1::uuid,$2,$3,'gong',$4,$5::jsonb,now(),now()) on conflict (workspace_id, source_provider, source_external_id) do update set name=excluded.name, domain=coalesce(nullif(excluded.domain,''), customer.domain), metadata=customer.metadata || excluded.metadata, updated_at=now() returning id::text`, workspaceID, name, domain, externalID, metadataRaw).Scan(&id)
err := tx.QueryRow(ctx, `insert into customer (workspace_id, name, domain, source, created_at, updated_at) values ($1::uuid,$2,$3,'gong',now(),now()) returning id::text`, workspaceID, name, nullString(domain)).Scan(&id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Upsert domainless Gong customers by account ID

When Gong supplies an account ID/name but no domain and the speaker email is unavailable, this path skips the domain lookup and performs an unconditional insert with a NULL domain. Every finding for the same Gong account then creates a separate customer row, whereas the previous Gong path used call.Account.ID as a stable external key; preserve a Gong account identifier or another stable key before inserting.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant