@commandable/integration-data 0.0.1 → 0.0.5

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.
Files changed (83) hide show
  1. package/dist/credentials-index.d.ts +4 -21
  2. package/dist/credentials-index.d.ts.map +1 -1
  3. package/dist/credentials-index.js +407 -215
  4. package/dist/credentials-index.js.map +1 -1
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/loader.d.ts +38 -2
  10. package/dist/loader.d.ts.map +1 -1
  11. package/dist/loader.js +70 -16
  12. package/dist/loader.js.map +1 -1
  13. package/integrations/__tests__/liveHarness.ts +84 -0
  14. package/integrations/__tests__/usageParity.ts +54 -0
  15. package/integrations/airtable/__tests__/get_handlers.test.ts +43 -31
  16. package/integrations/airtable/__tests__/usage_parity.test.ts +3 -29
  17. package/integrations/airtable/__tests__/write_and_admin_handlers.test.ts +20 -17
  18. package/integrations/airtable/credentials.json +21 -16
  19. package/integrations/github/__tests__/get_handlers.test.ts +101 -108
  20. package/integrations/github/__tests__/usage_parity.test.ts +15 -27
  21. package/integrations/github/__tests__/write_handlers.test.ts +223 -306
  22. package/integrations/github/credentials.json +40 -15
  23. package/integrations/github/credentials_hint_classic_pat.md +8 -0
  24. package/integrations/github/credentials_hint_fine_grained_pat.md +9 -0
  25. package/integrations/github/handlers/create_commit.js +2 -17
  26. package/integrations/github/manifest.json +2 -2
  27. package/integrations/google-calendar/__tests__/get_handlers.test.ts +21 -13
  28. package/integrations/google-calendar/__tests__/usage_parity.test.ts +3 -28
  29. package/integrations/google-calendar/__tests__/write_and_admin_handlers.test.ts +24 -17
  30. package/integrations/google-calendar/credentials.json +50 -29
  31. package/integrations/google-calendar/credentials_hint_oauth_token.md +8 -0
  32. package/integrations/google-calendar/credentials_hint_service_account.md +10 -0
  33. package/integrations/google-docs/__tests__/get_handlers.test.ts +87 -61
  34. package/integrations/google-docs/__tests__/usage_parity.test.ts +3 -28
  35. package/integrations/google-docs/__tests__/write_handlers.test.ts +251 -245
  36. package/integrations/google-docs/credentials.json +50 -29
  37. package/integrations/google-docs/credentials_hint_oauth_token.md +8 -0
  38. package/integrations/google-docs/credentials_hint_service_account.md +10 -0
  39. package/integrations/google-docs/handlers/insert_inline_image_after_first_match.js +1 -1
  40. package/integrations/google-docs/schemas/insert_inline_image_after_first_match.json +0 -1
  41. package/integrations/google-drive/__tests__/handlers.test.ts +102 -0
  42. package/integrations/google-drive/credentials.json +57 -0
  43. package/integrations/google-drive/credentials_hint_oauth_token.md +8 -0
  44. package/integrations/google-drive/credentials_hint_service_account.md +10 -0
  45. package/integrations/google-drive/handlers/create_file.js +15 -0
  46. package/integrations/google-drive/handlers/create_folder.js +15 -0
  47. package/integrations/google-drive/handlers/delete_file.js +14 -0
  48. package/integrations/google-drive/handlers/get_file.js +7 -0
  49. package/integrations/google-drive/handlers/move_file.js +12 -0
  50. package/integrations/google-drive/manifest.json +42 -0
  51. package/integrations/google-drive/schemas/create_file.json +12 -0
  52. package/integrations/google-drive/schemas/create_folder.json +11 -0
  53. package/integrations/google-drive/schemas/delete_file.json +10 -0
  54. package/integrations/google-drive/schemas/get_file.json +10 -0
  55. package/integrations/google-drive/schemas/move_file.json +12 -0
  56. package/integrations/google-sheet/__tests__/get_handlers.test.ts +48 -55
  57. package/integrations/google-sheet/__tests__/usage_parity.test.ts +3 -29
  58. package/integrations/google-sheet/__tests__/write_handlers.test.ts +65 -63
  59. package/integrations/google-sheet/credentials.json +50 -29
  60. package/integrations/google-sheet/credentials_hint_oauth_token.md +8 -0
  61. package/integrations/google-sheet/credentials_hint_service_account.md +10 -0
  62. package/integrations/google-slides/__tests__/get_handlers.test.ts +38 -36
  63. package/integrations/google-slides/__tests__/usage_parity.test.ts +3 -28
  64. package/integrations/google-slides/__tests__/write_handlers.test.ts +65 -59
  65. package/integrations/google-slides/credentials.json +50 -29
  66. package/integrations/google-slides/credentials_hint_oauth_token.md +8 -0
  67. package/integrations/google-slides/credentials_hint_service_account.md +10 -0
  68. package/integrations/notion/__tests__/get_handlers.test.ts +18 -15
  69. package/integrations/notion/__tests__/usage_parity.test.ts +3 -28
  70. package/integrations/notion/__tests__/write_and_admin_handlers.test.ts +56 -60
  71. package/integrations/notion/credentials.json +22 -17
  72. package/integrations/trello/__tests__/get_handlers.test.ts +58 -73
  73. package/integrations/trello/__tests__/usage_parity.test.ts +3 -28
  74. package/integrations/trello/__tests__/write_and_admin_handlers.test.ts +49 -67
  75. package/integrations/trello/credentials.json +26 -21
  76. package/integrations/trello/handlers/close_board.js +6 -0
  77. package/integrations/trello/handlers/create_board.js +11 -0
  78. package/integrations/trello/handlers/delete_board.js +13 -0
  79. package/integrations/trello/manifest.json +21 -0
  80. package/integrations/trello/schemas/close_board.json +10 -0
  81. package/integrations/trello/schemas/create_board.json +12 -0
  82. package/integrations/trello/schemas/delete_board.json +10 -0
  83. package/package.json +1 -1
@@ -1,21 +1,26 @@
1
1
  {
2
- "schema": {
3
- "type": "object",
4
- "properties": {
5
- "token": {
6
- "type": "string",
7
- "title": "Internal Integration Token",
8
- "description": "Notion internal integration token (starts with \"secret_\")."
2
+ "variants": {
3
+ "internal_integration": {
4
+ "label": "Internal Integration Token",
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "token": {
9
+ "type": "string",
10
+ "title": "Internal Integration Token",
11
+ "description": "Notion internal integration token (starts with \"secret_\")."
12
+ }
13
+ },
14
+ "required": ["token"],
15
+ "additionalProperties": false
16
+ },
17
+ "injection": {
18
+ "headers": {
19
+ "Authorization": "Bearer {{token}}",
20
+ "Notion-Version": "2022-06-28"
21
+ }
9
22
  }
10
- },
11
- "required": ["token"],
12
- "additionalProperties": false
13
- },
14
- "injection": {
15
- "headers": {
16
- "Authorization": "Bearer {{token}}",
17
- "Notion-Version": "2022-06-28"
18
23
  }
19
- }
24
+ },
25
+ "default": "internal_integration"
20
26
  }
21
-
@@ -1,13 +1,10 @@
1
- import { beforeAll, describe, expect, it } from 'vitest'
2
- import { IntegrationProxy } from '../../../../server/src/integrations/proxy.js'
3
- import { loadIntegrationTools } from '../../../../server/src/integrations/dataLoader.js'
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest'
2
+ import { createCredentialStore, createIntegrationNode, createProxy, createToolbox, hasEnv, safeCleanup } from '../../__tests__/liveHarness.js'
4
3
 
5
- // This is a LIVE integration test suite that hits Trello using managed OAuth.
4
+ // This is a LIVE integration test suite that hits Trello using credentials.
6
5
  // Required env vars:
7
- // - COMMANDABLE_MANAGED_OAUTH_BASE_URL
8
- // - COMMANDABLE_MANAGED_OAUTH_SECRET_KEY
9
6
  // - TRELLO_API_KEY
10
- // - TRELLO_TEST_CONNECTION_ID (an existing managed OAuth connectionId for provider 'trello')
7
+ // - TRELLO_API_TOKEN
11
8
 
12
9
  interface Ids {
13
10
  boardId?: string
@@ -16,68 +13,56 @@ interface Ids {
16
13
  orgId?: string
17
14
  }
18
15
 
19
- const env = process.env as Record<string, string>
20
- const hasEnv = (...keys: string[]) => keys.every(k => !!env[k] && env[k].trim().length > 0)
21
16
  const suite = hasEnv(
22
- 'COMMANDABLE_MANAGED_OAUTH_BASE_URL',
23
- 'COMMANDABLE_MANAGED_OAUTH_SECRET_KEY',
24
17
  'TRELLO_API_KEY',
25
- 'TRELLO_TEST_CONNECTION_ID',
18
+ 'TRELLO_API_TOKEN',
26
19
  )
27
20
  ? describe
28
21
  : describe.skip
29
22
 
30
23
  suite('trello read handlers (live)', () => {
31
24
  const ids: Ids = {}
25
+ let boardId: string | undefined
26
+ let listId: string | undefined
32
27
 
33
- let buildHandler: (name: string) => ((input: any) => Promise<any>)
28
+ let trello: ReturnType<typeof createToolbox>
34
29
 
35
30
  beforeAll(async () => {
36
- const { COMMANDABLE_MANAGED_OAUTH_BASE_URL, COMMANDABLE_MANAGED_OAUTH_SECRET_KEY, TRELLO_API_KEY, TRELLO_TEST_CONNECTION_ID } = env
37
-
38
- const proxy = new IntegrationProxy({
39
- managedOAuthBaseUrl: COMMANDABLE_MANAGED_OAUTH_BASE_URL,
40
- managedOAuthSecretKey: COMMANDABLE_MANAGED_OAUTH_SECRET_KEY,
41
- trelloApiKey: TRELLO_API_KEY,
42
- })
43
- const integrationNode = { id: 'node-trello', type: 'trello', label: 'Trello', connectionId: TRELLO_TEST_CONNECTION_ID } as any
44
-
45
- const tools = loadIntegrationTools('trello')
46
- expect(tools).toBeTruthy()
47
-
48
- buildHandler = (name: string) => {
49
- const tool = tools!.read.find(t => t.name === name)
50
- expect(tool, `tool ${name} exists`).toBeTruthy()
51
- const integration = {
52
- fetch: (path: string, init?: RequestInit) => proxy.call(integrationNode, path, init),
53
- }
54
- const build = new Function('integration', `return (${tool!.handlerCode});`)
55
- return build(integration) as (input: any) => Promise<any>
56
- }
57
-
58
- // Discover a board, list, card, and org for subsequent tests
59
- const get_member_boards = buildHandler('get_member_boards')
60
- const boards = await get_member_boards({})
61
- expect(Array.isArray(boards)).toBe(true)
62
- ids.boardId = boards[0]?.id
63
-
64
- if (ids.boardId) {
65
- const get_board_lists = buildHandler('get_board_lists')
66
- const lists = await get_board_lists({ boardId: ids.boardId })
67
- ids.listId = lists[0]?.id
68
-
69
- const get_board_cards = buildHandler('get_board_cards')
70
- const cards = await get_board_cards({ boardId: ids.boardId })
71
- ids.cardId = cards[0]?.id
72
- }
73
-
74
- const get_member_organizations = buildHandler('get_member_organizations')
31
+ const env = process.env as Record<string, string | undefined>
32
+ const credentialStore = createCredentialStore(async () => ({ apiKey: env.TRELLO_API_KEY || '', apiToken: env.TRELLO_API_TOKEN || '' }))
33
+ const proxy = createProxy(credentialStore)
34
+ const node = createIntegrationNode('trello', { label: 'Trello', credentialId: 'trello-creds' })
35
+ trello = createToolbox('trello', proxy, node)
36
+
37
+ // Create an isolated board/list/card for this run so tests don’t touch random user boards.
38
+ const board = await trello.write('create_board')({ name: `CmdTest Trello Read ${Date.now()}`, defaultLists: false })
39
+ boardId = board?.id
40
+ ids.boardId = boardId
41
+ expect(ids.boardId).toBeTruthy()
42
+
43
+ const list = await trello.write('create_list')({ idBoard: boardId, name: 'CmdTest List' })
44
+ listId = list?.id
45
+ ids.listId = listId
46
+ expect(ids.listId).toBeTruthy()
47
+
48
+ const card = await trello.write('create_card')({ idList: listId, name: `CmdTest Card ${Date.now()}` })
49
+ ids.cardId = card?.id
50
+ expect(ids.cardId).toBeTruthy()
51
+
52
+ const get_member_organizations = trello.read('get_member_organizations')
75
53
  const orgs = await get_member_organizations({})
76
54
  ids.orgId = orgs[0]?.id
77
55
  }, 60000)
78
56
 
57
+ afterAll(async () => {
58
+ if (!boardId)
59
+ return
60
+ await safeCleanup(async () => trello.write('close_board')({ boardId }))
61
+ await safeCleanup(async () => trello.write('delete_board')({ boardId }))
62
+ }, 60_000)
63
+
79
64
  it('get_member returns current member', async () => {
80
- const handler = buildHandler('get_member')
65
+ const handler = trello.read('get_member')
81
66
  const result = await handler({})
82
67
  expect(result).toBeTruthy()
83
68
  expect(typeof result.id).toBe('string')
@@ -85,13 +70,13 @@ suite('trello read handlers (live)', () => {
85
70
  }, 30000)
86
71
 
87
72
  it('get_member_boards returns an array', async () => {
88
- const handler = buildHandler('get_member_boards')
73
+ const handler = trello.read('get_member_boards')
89
74
  const result = await handler({})
90
75
  expect(Array.isArray(result)).toBe(true)
91
76
  }, 30000)
92
77
 
93
78
  it('get_member_organizations returns an array', async () => {
94
- const handler = buildHandler('get_member_organizations')
79
+ const handler = trello.read('get_member_organizations')
95
80
  const result = await handler({})
96
81
  expect(Array.isArray(result)).toBe(true)
97
82
  }, 30000)
@@ -99,7 +84,7 @@ suite('trello read handlers (live)', () => {
99
84
  it('get_board works with boardId', async () => {
100
85
  if (!ids.boardId)
101
86
  return expect(true).toBe(true)
102
- const handler = buildHandler('get_board')
87
+ const handler = trello.read('get_board')
103
88
  const result = await handler({ boardId: ids.boardId })
104
89
  expect(result?.id).toBe(ids.boardId)
105
90
  }, 30000)
@@ -107,7 +92,7 @@ suite('trello read handlers (live)', () => {
107
92
  it('get_board_lists returns lists', async () => {
108
93
  if (!ids.boardId)
109
94
  return expect(true).toBe(true)
110
- const handler = buildHandler('get_board_lists')
95
+ const handler = trello.read('get_board_lists')
111
96
  const result = await handler({ boardId: ids.boardId })
112
97
  expect(Array.isArray(result)).toBe(true)
113
98
  }, 30000)
@@ -115,7 +100,7 @@ suite('trello read handlers (live)', () => {
115
100
  it('get_board_cards returns cards', async () => {
116
101
  if (!ids.boardId)
117
102
  return expect(true).toBe(true)
118
- const handler = buildHandler('get_board_cards')
103
+ const handler = trello.read('get_board_cards')
119
104
  const result = await handler({ boardId: ids.boardId })
120
105
  expect(Array.isArray(result)).toBe(true)
121
106
  }, 30000)
@@ -123,7 +108,7 @@ suite('trello read handlers (live)', () => {
123
108
  it('get_board_members returns members', async () => {
124
109
  if (!ids.boardId)
125
110
  return expect(true).toBe(true)
126
- const handler = buildHandler('get_board_members')
111
+ const handler = trello.read('get_board_members')
127
112
  const result = await handler({ boardId: ids.boardId })
128
113
  expect(Array.isArray(result)).toBe(true)
129
114
  }, 30000)
@@ -131,7 +116,7 @@ suite('trello read handlers (live)', () => {
131
116
  it('get_board_labels returns labels', async () => {
132
117
  if (!ids.boardId)
133
118
  return expect(true).toBe(true)
134
- const handler = buildHandler('get_board_labels')
119
+ const handler = trello.read('get_board_labels')
135
120
  const result = await handler({ boardId: ids.boardId })
136
121
  expect(Array.isArray(result)).toBe(true)
137
122
  }, 30000)
@@ -139,7 +124,7 @@ suite('trello read handlers (live)', () => {
139
124
  it('get_board_custom_fields returns custom fields', async () => {
140
125
  if (!ids.boardId)
141
126
  return expect(true).toBe(true)
142
- const handler = buildHandler('get_board_custom_fields')
127
+ const handler = trello.read('get_board_custom_fields')
143
128
  const result = await handler({ boardId: ids.boardId })
144
129
  expect(Array.isArray(result)).toBe(true)
145
130
  }, 30000)
@@ -147,7 +132,7 @@ suite('trello read handlers (live)', () => {
147
132
  it('get_board_memberships returns memberships', async () => {
148
133
  if (!ids.boardId)
149
134
  return expect(true).toBe(true)
150
- const handler = buildHandler('get_board_memberships')
135
+ const handler = trello.read('get_board_memberships')
151
136
  const result = await handler({ boardId: ids.boardId })
152
137
  expect(Array.isArray(result)).toBe(true)
153
138
  }, 30000)
@@ -155,7 +140,7 @@ suite('trello read handlers (live)', () => {
155
140
  it('get_list returns a list', async () => {
156
141
  if (!ids.listId)
157
142
  return expect(true).toBe(true)
158
- const handler = buildHandler('get_list')
143
+ const handler = trello.read('get_list')
159
144
  const result = await handler({ listId: ids.listId })
160
145
  expect(result?.id).toBe(ids.listId)
161
146
  }, 30000)
@@ -163,7 +148,7 @@ suite('trello read handlers (live)', () => {
163
148
  it('get_list_cards returns cards in a list', async () => {
164
149
  if (!ids.listId)
165
150
  return expect(true).toBe(true)
166
- const handler = buildHandler('get_list_cards')
151
+ const handler = trello.read('get_list_cards')
167
152
  const result = await handler({ listId: ids.listId })
168
153
  expect(Array.isArray(result)).toBe(true)
169
154
  }, 30000)
@@ -171,7 +156,7 @@ suite('trello read handlers (live)', () => {
171
156
  it('get_card returns a card', async () => {
172
157
  if (!ids.cardId)
173
158
  return expect(true).toBe(true)
174
- const handler = buildHandler('get_card')
159
+ const handler = trello.read('get_card')
175
160
  const result = await handler({ cardId: ids.cardId })
176
161
  expect(result?.id).toBe(ids.cardId)
177
162
  }, 30000)
@@ -179,7 +164,7 @@ suite('trello read handlers (live)', () => {
179
164
  it('get_card_members returns members for a card', async () => {
180
165
  if (!ids.cardId)
181
166
  return expect(true).toBe(true)
182
- const handler = buildHandler('get_card_members')
167
+ const handler = trello.read('get_card_members')
183
168
  const result = await handler({ cardId: ids.cardId })
184
169
  expect(Array.isArray(result)).toBe(true)
185
170
  }, 30000)
@@ -187,7 +172,7 @@ suite('trello read handlers (live)', () => {
187
172
  it('get_card_attachments returns attachments for a card', async () => {
188
173
  if (!ids.cardId)
189
174
  return expect(true).toBe(true)
190
- const handler = buildHandler('get_card_attachments')
175
+ const handler = trello.read('get_card_attachments')
191
176
  const result = await handler({ cardId: ids.cardId })
192
177
  expect(Array.isArray(result)).toBe(true)
193
178
  }, 30000)
@@ -195,7 +180,7 @@ suite('trello read handlers (live)', () => {
195
180
  it('get_card_actions returns actions for a card', async () => {
196
181
  if (!ids.cardId)
197
182
  return expect(true).toBe(true)
198
- const handler = buildHandler('get_card_actions')
183
+ const handler = trello.read('get_card_actions')
199
184
  const result = await handler({ cardId: ids.cardId })
200
185
  expect(Array.isArray(result)).toBe(true)
201
186
  }, 30000)
@@ -203,7 +188,7 @@ suite('trello read handlers (live)', () => {
203
188
  it('get_card_checklists returns checklists for a card', async () => {
204
189
  if (!ids.cardId)
205
190
  return expect(true).toBe(true)
206
- const handler = buildHandler('get_card_checklists')
191
+ const handler = trello.read('get_card_checklists')
207
192
  const result = await handler({ cardId: ids.cardId })
208
193
  expect(Array.isArray(result)).toBe(true)
209
194
  }, 30000)
@@ -211,7 +196,7 @@ suite('trello read handlers (live)', () => {
211
196
  it('get_card_custom_field_items returns custom field items', async () => {
212
197
  if (!ids.cardId)
213
198
  return expect(true).toBe(true)
214
- const handler = buildHandler('get_card_custom_field_items')
199
+ const handler = trello.read('get_card_custom_field_items')
215
200
  const result = await handler({ cardId: ids.cardId })
216
201
  expect(Array.isArray(result)).toBe(true)
217
202
  }, 30000)
@@ -219,7 +204,7 @@ suite('trello read handlers (live)', () => {
219
204
  it('get_organization returns an organization', async () => {
220
205
  if (!ids.orgId)
221
206
  return expect(true).toBe(true)
222
- const handler = buildHandler('get_organization')
207
+ const handler = trello.read('get_organization')
223
208
  const result = await handler({ orgId: ids.orgId })
224
209
  expect(result?.id).toBe(ids.orgId)
225
210
  }, 30000)
@@ -227,13 +212,13 @@ suite('trello read handlers (live)', () => {
227
212
  it('get_organization_boards returns boards in an org', async () => {
228
213
  if (!ids.orgId)
229
214
  return expect(true).toBe(true)
230
- const handler = buildHandler('get_organization_boards')
215
+ const handler = trello.read('get_organization_boards')
231
216
  const result = await handler({ orgId: ids.orgId })
232
217
  expect(Array.isArray(result)).toBe(true)
233
218
  }, 30000)
234
219
 
235
220
  it('search returns results for a generic query', async () => {
236
- const handler = buildHandler('search')
221
+ const handler = trello.read('search')
237
222
  const result = await handler({ query: 'test' })
238
223
  expect(result).toBeTruthy()
239
224
  }, 30000)
@@ -1,34 +1,9 @@
1
- import { existsSync, readdirSync, readFileSync } from 'node:fs'
2
- import { resolve } from 'node:path'
3
- import { fileURLToPath } from 'node:url'
4
1
  import { describe, expect, it } from 'vitest'
5
- import { loadIntegrationManifest } from '../../../src/integrations/dataLoader.js'
6
-
7
- function escapeRegExp(str: string): string {
8
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
9
- }
2
+ import { getMissingToolUsages } from '../../__tests__/usageParity.js'
10
3
 
11
4
  describe('trello static usage parity', () => {
12
- it('every manifest tool is referenced in tests via build*(name)', () => {
13
- const manifest = loadIntegrationManifest('trello')!
14
- const toolNames = (manifest.tools as any[]).map(t => t.name)
15
-
16
- const testsDir = fileURLToPath(new URL('.', import.meta.url))
17
- expect(existsSync(testsDir)).toBe(true)
18
- const testFiles = readdirSync(testsDir)
19
- .filter(f => /\.test\.(t|j)s$/.test(f) && !f.includes('usage_parity.test'))
20
- .map(f => resolve(testsDir, f))
21
-
22
- const fileContents = testFiles.map(f => readFileSync(f, 'utf8'))
23
-
24
- const missing: string[] = []
25
- for (const name of toolNames) {
26
- const nameRe = new RegExp(`build(?:Read|Write|Admin)?(?:Handler)?\\(\\s*['\"\`]${escapeRegExp(name)}['\"\`]\\s*\\)`, 'm')
27
- const found = fileContents.some(src => nameRe.test(src))
28
- if (!found)
29
- missing.push(name)
30
- }
31
-
5
+ it('every manifest tool is referenced in tests', () => {
6
+ const missing = getMissingToolUsages({ integrationName: 'trello', importMetaUrl: import.meta.url })
32
7
  expect(missing, `Missing handler usages in tests: ${missing.join(', ')}`).toEqual([])
33
8
  })
34
9
  })
@@ -1,6 +1,5 @@
1
- import { beforeAll, describe, expect, it } from 'vitest'
2
- import { IntegrationProxy } from '../../../src/integrations/proxy.js'
3
- import { loadIntegrationTools } from '../../../src/integrations/dataLoader.js'
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest'
2
+ import { createCredentialStore, createIntegrationNode, createProxy, createToolbox, hasEnv, safeCleanup } from '../../__tests__/liveHarness.js'
4
3
 
5
4
  interface Ctx {
6
5
  boardId?: string
@@ -10,86 +9,69 @@ interface Ctx {
10
9
  memberId?: string
11
10
  }
12
11
 
13
- const env = process.env as Record<string, string>
14
- const hasEnv = (...keys: string[]) => keys.every(k => !!env[k] && env[k].trim().length > 0)
15
12
  const suite = hasEnv(
16
- 'COMMANDABLE_MANAGED_OAUTH_BASE_URL',
17
- 'COMMANDABLE_MANAGED_OAUTH_SECRET_KEY',
18
13
  'TRELLO_API_KEY',
19
- 'TRELLO_TEST_CONNECTION_ID',
14
+ 'TRELLO_API_TOKEN',
20
15
  )
21
16
  ? describe
22
17
  : describe.skip
23
18
 
24
19
  suite('trello write handlers (live)', () => {
25
20
  const ctx: Ctx = {}
26
- let buildWrite: (name: string) => ((input: any) => Promise<any>)
27
- let buildRead: (name: string) => ((input: any) => Promise<any>)
21
+ let trello: ReturnType<typeof createToolbox>
28
22
 
29
23
  beforeAll(async () => {
30
- const { COMMANDABLE_MANAGED_OAUTH_BASE_URL, COMMANDABLE_MANAGED_OAUTH_SECRET_KEY, TRELLO_API_KEY, TRELLO_TEST_CONNECTION_ID } = env
31
-
32
- const proxy = new IntegrationProxy({
33
- managedOAuthBaseUrl: COMMANDABLE_MANAGED_OAUTH_BASE_URL,
34
- managedOAuthSecretKey: COMMANDABLE_MANAGED_OAUTH_SECRET_KEY,
35
- trelloApiKey: TRELLO_API_KEY,
36
- })
37
- const integrationNode = { id: 'node-trello', type: 'trello', label: 'Trello', connectionId: TRELLO_TEST_CONNECTION_ID } as any
38
-
39
- const tools = loadIntegrationTools('trello')
40
- expect(tools).toBeTruthy()
41
-
42
- buildWrite = (name: string) => {
43
- const tool = tools!.write.find(t => t.name === name)
44
- expect(tool, `write tool ${name} exists`).toBeTruthy()
45
- const integration = { fetch: (path: string, init?: RequestInit) => proxy.call(integrationNode, path, init) }
46
- const build = new Function('integration', `return (${tool!.handlerCode});`)
47
- return build(integration) as (input: any) => Promise<any>
48
- }
49
-
50
- buildRead = (name: string) => {
51
- const tool = tools!.read.find(t => t.name === name)
52
- expect(tool, `read tool ${name} exists`).toBeTruthy()
53
- const integration = { fetch: (path: string, init?: RequestInit) => proxy.call(integrationNode, path, init) }
54
- const build = new Function('integration', `return (${tool!.handlerCode});`)
55
- return build(integration) as (input: any) => Promise<any>
56
- }
57
-
58
- // Discover a board and list for tests
59
- const get_member_boards = buildRead('get_member_boards')
60
- const boards = await get_member_boards({})
61
- ctx.boardId = boards?.[0]?.id
62
- if (ctx.boardId) {
63
- const get_board_lists = buildRead('get_board_lists')
64
- const lists = await get_board_lists({ boardId: ctx.boardId })
65
- ctx.listId = lists?.[0]?.id
66
- ctx.listId2 = lists?.[1]?.id || ctx.listId
67
- }
24
+ const env = process.env as Record<string, string | undefined>
25
+ const credentialStore = createCredentialStore(async () => ({ apiKey: env.TRELLO_API_KEY || '', apiToken: env.TRELLO_API_TOKEN || '' }))
26
+ const proxy = createProxy(credentialStore)
27
+ const node = createIntegrationNode('trello', { label: 'Trello', credentialId: 'trello-creds' })
28
+ trello = createToolbox('trello', proxy, node)
29
+
30
+ // Create an isolated board + two lists for this test run
31
+ const create_board = trello.write('create_board')
32
+ const board = await create_board({ name: `CmdTest Trello ${Date.now()}`, defaultLists: false })
33
+ ctx.boardId = board?.id
34
+ expect(ctx.boardId).toBeTruthy()
35
+
36
+ const create_list = trello.write('create_list')
37
+ const list1 = await create_list({ idBoard: ctx.boardId, name: 'CmdTest List A' })
38
+ const list2 = await create_list({ idBoard: ctx.boardId, name: 'CmdTest List B' })
39
+ ctx.listId = list1?.id
40
+ ctx.listId2 = list2?.id
41
+ expect(ctx.listId).toBeTruthy()
42
+ expect(ctx.listId2).toBeTruthy()
68
43
 
69
44
  // Discover a member to add to card (self)
70
- const get_member = buildRead('get_member')
45
+ const get_member = trello.read('get_member')
71
46
  const me = await get_member({})
72
47
  ctx.memberId = me?.id
73
48
  }, 60000)
74
49
 
50
+ afterAll(async () => {
51
+ if (!ctx.boardId)
52
+ return
53
+ await safeCleanup(async () => trello.write('close_board')({ boardId: ctx.boardId }))
54
+ await safeCleanup(async () => trello.write('delete_board')({ boardId: ctx.boardId }))
55
+ }, 60_000)
56
+
75
57
  it('create_card -> get_card -> update_card -> move_card_to_list -> delete_card', async () => {
76
58
  if (!ctx.boardId || !ctx.listId || !ctx.memberId)
77
59
  return expect(true).toBe(true)
78
60
 
79
61
  // Create card
80
- const create_card = buildWrite('create_card')
62
+ const create_card = trello.write('create_card')
81
63
  const created = await create_card({ idList: ctx.listId, name: `CmdCard ${Date.now()}`, desc: 'Initial desc' })
82
64
  const cardId = created?.id
83
65
  expect(cardId).toBeTruthy()
84
66
  ctx.cardId = cardId
85
67
 
86
68
  // Read card
87
- const get_card = buildRead('get_card')
69
+ const get_card = trello.read('get_card')
88
70
  const got = await get_card({ cardId })
89
71
  expect(got?.id).toBe(cardId)
90
72
 
91
73
  // Update card
92
- const update_card = buildWrite('update_card')
74
+ const update_card = trello.write('update_card')
93
75
  const updated = await update_card({ cardId, name: 'Updated Name', desc: 'Updated desc' })
94
76
  expect(updated?.id).toBe(cardId)
95
77
 
@@ -99,7 +81,7 @@ suite('trello write handlers (live)', () => {
99
81
 
100
82
  // Move card
101
83
  if (ctx.listId2 && ctx.listId2 !== ctx.listId) {
102
- const move_card_to_list = buildWrite('move_card_to_list')
84
+ const move_card_to_list = trello.write('move_card_to_list')
103
85
  const moved = await move_card_to_list({ cardId, listId: ctx.listId2 })
104
86
  expect(moved?.id).toBe(cardId)
105
87
  const got3 = await get_card({ cardId })
@@ -107,7 +89,7 @@ suite('trello write handlers (live)', () => {
107
89
  }
108
90
 
109
91
  // Delete card
110
- const delete_card = buildWrite('delete_card')
92
+ const delete_card = trello.write('delete_card')
111
93
  const del = await delete_card({ cardId })
112
94
  expect(Boolean(del === '' || del?.limits || del?.id === cardId || (del && typeof del === 'object'))).toBe(true)
113
95
  }, 120000)
@@ -117,26 +99,26 @@ suite('trello write handlers (live)', () => {
117
99
  return expect(true).toBe(true)
118
100
 
119
101
  // Create an isolated card for this test
120
- const create_card = buildWrite('create_card')
102
+ const create_card = trello.write('create_card')
121
103
  const created = await create_card({ idList: ctx.listId, name: `CmdCard Members ${Date.now()}` })
122
104
  const cardId = created?.id
123
105
  expect(cardId).toBeTruthy()
124
106
 
125
- const add_member_to_card = buildWrite('add_member_to_card')
107
+ const add_member_to_card = trello.write('add_member_to_card')
126
108
  const added = await add_member_to_card({ cardId, memberId: ctx.memberId })
127
109
  expect(added).toBeTruthy()
128
110
 
129
- const get_card_members = buildRead('get_card_members')
111
+ const get_card_members = trello.read('get_card_members')
130
112
  const members = await get_card_members({ cardId })
131
113
  const hasMember = (members || []).some((m: any) => m?.id === ctx.memberId)
132
114
  expect(hasMember).toBe(true)
133
115
 
134
- const remove_member_from_card = buildWrite('remove_member_from_card')
116
+ const remove_member_from_card = trello.write('remove_member_from_card')
135
117
  const removed = await remove_member_from_card({ cardId, memberId: ctx.memberId })
136
118
  expect(removed === '' || (removed && typeof removed === 'object')).toBe(true)
137
119
 
138
120
  // Cleanup
139
- const delete_card = buildWrite('delete_card')
121
+ const delete_card = trello.write('delete_card')
140
122
  await delete_card({ cardId })
141
123
  }, 90000)
142
124
 
@@ -145,44 +127,44 @@ suite('trello write handlers (live)', () => {
145
127
  return expect(true).toBe(true)
146
128
 
147
129
  // Create an isolated card for this test
148
- const create_card = buildWrite('create_card')
130
+ const create_card = trello.write('create_card')
149
131
  const createdCard = await create_card({ idList: ctx.listId, name: `CmdCard Checklist ${Date.now()}` })
150
132
  const cardId = createdCard?.id
151
133
  expect(cardId).toBeTruthy()
152
134
 
153
- const add_checklist_to_card = buildWrite('add_checklist_to_card')
135
+ const add_checklist_to_card = trello.write('add_checklist_to_card')
154
136
  const created = await add_checklist_to_card({ cardId, name: `Checklist ${Date.now()}` })
155
137
  expect(created?.id).toBeTruthy()
156
138
 
157
- const get_card_checklists = buildRead('get_card_checklists')
139
+ const get_card_checklists = trello.read('get_card_checklists')
158
140
  const lists = await get_card_checklists({ cardId })
159
141
  expect(Array.isArray(lists)).toBe(true)
160
142
 
161
143
  // Cleanup
162
- const delete_card = buildWrite('delete_card')
144
+ const delete_card = trello.write('delete_card')
163
145
  await delete_card({ cardId })
164
146
  }, 60000)
165
147
 
166
148
  it('create_list -> get_list -> update_list -> archive_list', async () => {
167
149
  if (!ctx.boardId)
168
150
  return expect(true).toBe(true)
169
- const create_list = buildWrite('create_list')
151
+ const create_list = trello.write('create_list')
170
152
  const created = await create_list({ idBoard: ctx.boardId, name: `CmdList ${Date.now()}` })
171
153
  const listId = created?.id
172
154
  expect(listId).toBeTruthy()
173
155
 
174
- const get_list = buildRead('get_list')
156
+ const get_list = trello.read('get_list')
175
157
  const got = await get_list({ listId })
176
158
  expect(got?.id).toBe(listId)
177
159
 
178
- const update_list = buildWrite('update_list')
160
+ const update_list = trello.write('update_list')
179
161
  const updated = await update_list({ listId, name: 'Updated List Name' })
180
162
  expect(updated?.id).toBe(listId)
181
163
 
182
164
  const got2 = await get_list({ listId })
183
165
  expect(got2?.name).toBe('Updated List Name')
184
166
 
185
- const archive_list = buildWrite('archive_list')
167
+ const archive_list = trello.write('archive_list')
186
168
  const archived = await archive_list({ listId })
187
169
  expect(archived?.closed === true).toBe(true)
188
170
  }, 120000)
@@ -1,26 +1,31 @@
1
1
  {
2
- "schema": {
3
- "type": "object",
4
- "properties": {
5
- "apiKey": {
6
- "type": "string",
7
- "title": "API Key",
8
- "description": "Your Trello API key from https://trello.com/power-ups/admin"
2
+ "variants": {
3
+ "api_key_token": {
4
+ "label": "API Key + Token",
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "apiKey": {
9
+ "type": "string",
10
+ "title": "API Key",
11
+ "description": "Your Trello API key from https://trello.com/power-ups/admin"
12
+ },
13
+ "apiToken": {
14
+ "type": "string",
15
+ "title": "API Token",
16
+ "description": "Your Trello API token (\"token\" param). Generate one via Trello's authorize flow."
17
+ }
18
+ },
19
+ "required": ["apiKey", "apiToken"],
20
+ "additionalProperties": false
9
21
  },
10
- "apiToken": {
11
- "type": "string",
12
- "title": "API Token",
13
- "description": "Your Trello API token (\"token\" param). Generate one via Trello's authorize flow."
22
+ "injection": {
23
+ "query": {
24
+ "key": "{{apiKey}}",
25
+ "token": "{{apiToken}}"
26
+ }
14
27
  }
15
- },
16
- "required": ["apiKey", "apiToken"],
17
- "additionalProperties": false
18
- },
19
- "injection": {
20
- "query": {
21
- "key": "{{apiKey}}",
22
- "token": "{{apiToken}}"
23
28
  }
24
- }
29
+ },
30
+ "default": "api_key_token"
25
31
  }
26
-