Skip to content

Commit e149636

Browse files
feat(credentials): complete v2 credential lifecycle
1 parent e4b09dc commit e149636

29 files changed

Lines changed: 2444 additions & 527 deletions

apps/docs/openapi-v2-resources.json

Lines changed: 646 additions & 76 deletions
Large diffs are not rendered by default.

apps/sim/app/api/v2/credential-connections/route.test.ts

Lines changed: 0 additions & 180 deletions
This file was deleted.

apps/sim/app/api/v2/credential-providers/route.test.ts

Lines changed: 0 additions & 124 deletions
This file was deleted.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import {
5+
V2_OPERATION_RATE_LIMIT_ALLOWED,
6+
V2_PREAUTH_RATE_LIMIT_ALLOWED,
7+
v2ApiKeyAuthModuleMock,
8+
v2GateModuleMock,
9+
v2RateLimiterModuleMock,
10+
v2RouteMocks,
11+
} from '@sim/testing'
12+
import { NextRequest } from 'next/server'
13+
import { beforeEach, describe, expect, it, vi } from 'vitest'
14+
15+
const mocks = vi.hoisted(() => ({ execute: vi.fn() }))
16+
17+
vi.mock('@/lib/api/server/routes/v2-api-key-auth', () => v2ApiKeyAuthModuleMock)
18+
vi.mock('@/lib/core/rate-limiter', () => v2RateLimiterModuleMock)
19+
vi.mock('@/app/api/v2/lib/gate', () => v2GateModuleMock)
20+
vi.mock('@/lib/credentials/application/service-account', () => ({
21+
deleteCredentialUseCase: {
22+
operation: { id: 'credentials.delete' },
23+
execute: mocks.execute,
24+
},
25+
}))
26+
27+
import { DELETE } from '@/app/api/v2/credentials/[credentialId]/route'
28+
29+
const WORKSPACE_ID = '11111111-2222-4333-8444-555555555555'
30+
const auth = {
31+
principal: { kind: 'personal_api_key' as const, userId: 'user-1', keyId: 'key-1' },
32+
rolloutUserId: 'user-1',
33+
rateLimitSubjectIds: ['api-key:key-1', 'user:user-1'] as const,
34+
rateLimitSubscription: null,
35+
keyType: 'personal' as const,
36+
}
37+
38+
describe('DELETE /api/v2/credentials/[credentialId]', () => {
39+
beforeEach(() => {
40+
vi.clearAllMocks()
41+
v2RouteMocks.authenticate.mockResolvedValue(auth)
42+
v2RouteMocks.gate.mockResolvedValue(null)
43+
v2RouteMocks.preauthRate.mockResolvedValue(V2_PREAUTH_RATE_LIMIT_ALLOWED)
44+
v2RouteMocks.operationRate.mockResolvedValue(V2_OPERATION_RATE_LIMIT_ALLOWED)
45+
mocks.execute.mockResolvedValue({ credential: { id: 'credential-1' } })
46+
})
47+
48+
it('disconnects a credential through the application operation', async () => {
49+
const request = new NextRequest(
50+
`http://localhost:3000/api/v2/credentials/credential-1?workspaceId=${WORKSPACE_ID}`,
51+
{ method: 'DELETE' }
52+
)
53+
const response = await DELETE(request, {
54+
params: Promise.resolve({ credentialId: 'credential-1' }),
55+
})
56+
57+
expect(response.status).toBe(200)
58+
expect(await response.json()).toEqual({ data: { id: 'credential-1', deleted: true } })
59+
expect(mocks.execute).toHaveBeenCalledWith({
60+
principal: auth.principal,
61+
input: { workspaceId: WORKSPACE_ID, credentialId: 'credential-1' },
62+
request,
63+
})
64+
})
65+
66+
it('requires the asserted workspace scope', async () => {
67+
const response = await DELETE(
68+
new NextRequest('http://localhost:3000/api/v2/credentials/credential-1', {
69+
method: 'DELETE',
70+
}),
71+
{ params: Promise.resolve({ credentialId: 'credential-1' }) }
72+
)
73+
74+
expect(response.status).toBe(400)
75+
expect(mocks.execute).not.toHaveBeenCalled()
76+
})
77+
})

0 commit comments

Comments
 (0)