Skip to content

Commit e70aaf0

Browse files
committed
fix(knowledge): assert the billing owner before the credential-missing terminal write
1 parent 45117d5 commit e70aaf0

2 files changed

Lines changed: 41 additions & 39 deletions

File tree

‎apps/sim/lib/knowledge/connectors/sync-engine.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3353,6 +3353,7 @@ describe('executeSync hard-delete reconciliation', () => {
33533353
queueTableRows(schemaMock.knowledgeConnector, [
33543354
{ ...CONNECTOR, connectorType: 'oauth', credentialId: null, encryptedApiKey: null },
33553355
])
3356+
queueTableRows(schemaMock.knowledgeBase, [{ id: 'kb-1', userId: 'u-1', workspaceId: 'ws-1' }])
33563357

33573358
const result = await executeSync('c-1', {
33583359
billingAttribution: { workspaceId: 'ws-1' } as never,
@@ -3379,6 +3380,7 @@ describe('executeSync hard-delete reconciliation', () => {
33793380
queueTableRows(schemaMock.knowledgeConnector, [
33803381
{ ...CONNECTOR, connectorType: 'keyed', credentialId: null, encryptedApiKey: null },
33813382
])
3383+
queueTableRows(schemaMock.knowledgeBase, [{ id: 'kb-1', userId: 'u-1', workspaceId: 'ws-1' }])
33823384

33833385
const result = await executeSync('c-1', {
33843386
billingAttribution: { workspaceId: 'ws-1' } as never,

‎apps/sim/lib/knowledge/connectors/sync-engine.ts‎

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -849,45 +849,6 @@ export async function executeSync(
849849
throw new Error(`Unknown connector type: ${connectorBeforeLock.connectorType}`)
850850
}
851851

852-
/**
853-
* A connector with no token source cannot succeed, and each attempt would only walk the
854-
* failure ladder and, at its end, disable a connector that merely needs reconnecting. Left
855-
* unscheduled with the reconnect error instead, the same transition as a deleted knowledge base.
856-
*/
857-
if (!connectorHasAuthSource(connectorConfig.auth, connectorBeforeLock)) {
858-
logger.warn('Skipping sync: connector has no credential to authenticate with', { connectorId })
859-
/**
860-
* Written only while the row is still the credential-less row this run read: a reconnect
861-
* that landed in between keeps its schedule, and a paused or disabled connector a stale task
862-
* reached keeps its status. A row this dispatch marked `pending` is released with it, the
863-
* same token match the lock acquisition below applies.
864-
*/
865-
const observed = (
866-
column: typeof knowledgeConnector.credentialId | typeof knowledgeConnector.encryptedApiKey,
867-
value: string | null
868-
) => (value === null ? isNull(column) : eq(column, value))
869-
await db
870-
.update(knowledgeConnector)
871-
.set(buildSyncUnscheduledUpdate(new Date(), CREDENTIAL_REMOVED_SYNC_ERROR))
872-
.where(
873-
and(
874-
eq(knowledgeConnector.id, connectorId),
875-
observed(knowledgeConnector.credentialId, connectorBeforeLock.credentialId),
876-
observed(knowledgeConnector.encryptedApiKey, connectorBeforeLock.encryptedApiKey),
877-
or(
878-
inArray(knowledgeConnector.status, [...RUNNABLE_CONNECTOR_STATUSES]),
879-
options.dispatchToken
880-
? and(
881-
eq(knowledgeConnector.status, 'pending'),
882-
eq(knowledgeConnector.syncLockToken, options.dispatchToken)
883-
)
884-
: undefined
885-
)
886-
)
887-
)
888-
return { ...result, skipReason: 'credential_missing' }
889-
}
890-
891852
const kbRows = await db
892853
.select({
893854
userId: knowledgeBase.userId,
@@ -928,6 +889,45 @@ export async function executeSync(
928889
)
929890
}
930891
assertBillingAttributionOwner(billingAttribution, kbOwner)
892+
893+
/**
894+
* A connector with no token source cannot succeed, and each attempt would only walk the
895+
* failure ladder and, at its end, disable a connector that merely needs reconnecting. Left
896+
* unscheduled with the reconnect error instead, the same transition as a deleted knowledge base.
897+
*/
898+
if (!connectorHasAuthSource(connectorConfig.auth, connectorBeforeLock)) {
899+
logger.warn('Skipping sync: connector has no credential to authenticate with', { connectorId })
900+
/**
901+
* Written only while the row is still the credential-less row this run read: a reconnect
902+
* that landed in between keeps its schedule, and a paused or disabled connector a stale task
903+
* reached keeps its status. A row this dispatch marked `pending` is released with it, the
904+
* same token match the lock acquisition below applies.
905+
*/
906+
const observed = (
907+
column: typeof knowledgeConnector.credentialId | typeof knowledgeConnector.encryptedApiKey,
908+
value: string | null
909+
) => (value === null ? isNull(column) : eq(column, value))
910+
await db
911+
.update(knowledgeConnector)
912+
.set(buildSyncUnscheduledUpdate(new Date(), CREDENTIAL_REMOVED_SYNC_ERROR))
913+
.where(
914+
and(
915+
eq(knowledgeConnector.id, connectorId),
916+
observed(knowledgeConnector.credentialId, connectorBeforeLock.credentialId),
917+
observed(knowledgeConnector.encryptedApiKey, connectorBeforeLock.encryptedApiKey),
918+
or(
919+
inArray(knowledgeConnector.status, [...RUNNABLE_CONNECTOR_STATUSES]),
920+
options.dispatchToken
921+
? and(
922+
eq(knowledgeConnector.status, 'pending'),
923+
eq(knowledgeConnector.syncLockToken, options.dispatchToken)
924+
)
925+
: undefined
926+
)
927+
)
928+
)
929+
return { ...result, skipReason: 'credential_missing' }
930+
}
931931
return withResourceOutboundScope(kbOwner, async (): Promise<SyncResult> => {
932932
/**
933933
* Identifies this run for the terminal writes. Generated before the CAS and

0 commit comments

Comments
 (0)