Skip to content

Commit 470e3c5

Browse files
fix(slack): reconnect surfaces Atlassian error codes and persists name/description edits
- PUT credential route now returns the Atlassian provider code (providerErrorCode -> code) so reconnect failures map to specific token/domain messages, matching create - Google/Atlassian reconnect send + seed displayName/description (parity with Slack); edits are no longer silently discarded, and empty fields don't clobber existing values
1 parent b77dd69 commit 470e3c5

3 files changed

Lines changed: 46 additions & 13 deletions

File tree

apps/sim/app/api/credentials/[id]/route.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,13 @@ export const PUT = withRouteHandler(
9999
: result.errorCode === 'validation'
100100
? 400
101101
: 500
102-
return NextResponse.json({ error: result.error }, { status })
102+
return NextResponse.json(
103+
{
104+
error: result.error,
105+
...(result.providerErrorCode ? { code: result.providerErrorCode } : {}),
106+
},
107+
{ status }
108+
)
103109
}
104110

105111
const access = await getCredentialActorContext(id, session.user.id)

apps/sim/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/connect-service-account-modal.tsx

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ export function ConnectServiceAccountModal({
142142
serviceName={serviceName}
143143
serviceIcon={serviceIcon}
144144
credentialId={credentialId}
145+
initialDisplayName={credentialDisplayName}
146+
initialDescription={credentialDescription}
145147
/>
146148
)
147149
}
@@ -153,6 +155,8 @@ export function ConnectServiceAccountModal({
153155
serviceName={serviceName}
154156
serviceIcon={serviceIcon}
155157
credentialId={credentialId}
158+
initialDisplayName={credentialDisplayName}
159+
initialDescription={credentialDescription}
156160
/>
157161
)
158162
}
@@ -165,6 +169,9 @@ interface ProviderModalProps {
165169
serviceIcon: ComponentType<{ className?: string }>
166170
/** When set, reconnect (rotate secrets on) this credential in place. */
167171
credentialId?: string
172+
/** Existing name/description, seeded into the fields on reconnect. */
173+
initialDisplayName?: string
174+
initialDescription?: string
168175
}
169176

170177
/**
@@ -179,11 +186,13 @@ function GoogleServiceAccountModal({
179186
serviceName,
180187
serviceIcon: ServiceIcon,
181188
credentialId,
189+
initialDisplayName,
190+
initialDescription,
182191
}: ProviderModalProps) {
183192
const [jsonInput, setJsonInput] = useState('')
184193
const [uploadedFileName, setUploadedFileName] = useState<string | null>(null)
185-
const [displayName, setDisplayName] = useState('')
186-
const [description, setDescription] = useState('')
194+
const [displayName, setDisplayName] = useState(initialDisplayName ?? '')
195+
const [description, setDescription] = useState(initialDescription ?? '')
187196
const [error, setError] = useState<string | null>(null)
188197

189198
const createCredential = useCreateWorkspaceCredential()
@@ -193,10 +202,10 @@ function GoogleServiceAccountModal({
193202
if (open) return
194203
setJsonInput('')
195204
setUploadedFileName(null)
196-
setDisplayName('')
197-
setDescription('')
205+
setDisplayName(initialDisplayName ?? '')
206+
setDescription(initialDescription ?? '')
198207
setError(null)
199-
}, [open])
208+
}, [open, initialDisplayName, initialDescription])
200209

201210
/**
202211
* Try to auto-populate display name from the JSON `client_email`. Silent on
@@ -249,7 +258,12 @@ function GoogleServiceAccountModal({
249258
}
250259
try {
251260
if (credentialId) {
252-
await updateCredential.mutateAsync({ credentialId, serviceAccountJson: trimmed })
261+
await updateCredential.mutateAsync({
262+
credentialId,
263+
serviceAccountJson: trimmed,
264+
displayName: displayName.trim() || undefined,
265+
description: description.trim() || undefined,
266+
})
253267
} else {
254268
await createCredential.mutateAsync({
255269
workspaceId,
@@ -359,11 +373,13 @@ function AtlassianServiceAccountModal({
359373
serviceName,
360374
serviceIcon: ServiceIcon,
361375
credentialId,
376+
initialDisplayName,
377+
initialDescription,
362378
}: ProviderModalProps) {
363379
const [apiToken, setApiToken] = useState('')
364380
const [domain, setDomain] = useState('')
365-
const [displayName, setDisplayName] = useState('')
366-
const [description, setDescription] = useState('')
381+
const [displayName, setDisplayName] = useState(initialDisplayName ?? '')
382+
const [description, setDescription] = useState(initialDescription ?? '')
367383
const [error, setError] = useState<string | null>(null)
368384

369385
const createCredential = useCreateWorkspaceCredential()
@@ -373,10 +389,10 @@ function AtlassianServiceAccountModal({
373389
if (open) return
374390
setApiToken('')
375391
setDomain('')
376-
setDisplayName('')
377-
setDescription('')
392+
setDisplayName(initialDisplayName ?? '')
393+
setDescription(initialDescription ?? '')
378394
setError(null)
379-
}, [open])
395+
}, [open, initialDisplayName, initialDescription])
380396

381397
const trimmedToken = apiToken.trim()
382398
const normalizedDomain = normalizeAtlassianDomain(domain)
@@ -395,6 +411,8 @@ function AtlassianServiceAccountModal({
395411
credentialId,
396412
apiToken: trimmedToken,
397413
domain: normalizedDomain,
414+
displayName: displayName.trim() || undefined,
415+
description: description.trim() || undefined,
398416
})
399417
} else {
400418
await createCredential.mutateAsync({

apps/sim/lib/credentials/orchestration/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ export interface PerformCredentialResult {
5454
success: boolean
5555
error?: string
5656
errorCode?: CredentialOrchestrationErrorCode
57+
/** Provider-specific code (e.g. Atlassian `invalid_credentials`) for client message mapping. */
58+
providerErrorCode?: string
5759
workspaceId?: string
5860
updatedFields?: string[]
5961
}
@@ -139,7 +141,14 @@ export async function performUpdateCredential(
139141
return { success: false, error: error.message, errorCode: 'validation' }
140142
}
141143
if (error instanceof AtlassianValidationError) {
142-
return { success: false, error: error.code, errorCode: 'validation' }
144+
// Surface the provider code so the client maps it to the specific
145+
// token/domain message (create returns it too).
146+
return {
147+
success: false,
148+
error: error.code,
149+
errorCode: 'validation',
150+
providerErrorCode: error.code,
151+
}
143152
}
144153
throw error
145154
}

0 commit comments

Comments
 (0)