@commandable/integration-data 0.0.4 → 0.0.6
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.
- package/dist/credentials-index.d.ts.map +1 -1
- package/dist/credentials-index.js +64 -0
- package/dist/credentials-index.js.map +1 -1
- package/integrations/airtable/__tests__/get_handlers.test.ts +25 -16
- package/integrations/github/__tests__/get_handlers.test.ts +202 -7
- package/integrations/github/__tests__/write_handlers.test.ts +178 -24
- package/integrations/github/handlers/create_commit.js +5 -12
- package/integrations/github/handlers/create_pull_request_review.js +10 -0
- package/integrations/github/handlers/create_release.js +14 -0
- package/integrations/github/handlers/delete_branch.js +8 -0
- package/integrations/github/handlers/delete_file.js +9 -0
- package/integrations/github/handlers/fork_repo.js +10 -0
- package/integrations/github/handlers/get_commit.js +8 -0
- package/integrations/github/handlers/get_file_contents.js +21 -0
- package/integrations/github/handlers/get_job_logs.js +6 -0
- package/integrations/github/handlers/get_latest_release.js +4 -0
- package/integrations/github/handlers/get_me.js +4 -0
- package/integrations/github/handlers/get_pull_request.js +4 -0
- package/integrations/github/handlers/get_pull_request_diff.js +8 -0
- package/integrations/github/handlers/get_repo_tree.js +12 -0
- package/integrations/github/handlers/get_workflow_run.js +4 -0
- package/integrations/github/handlers/list_branches.js +6 -1
- package/integrations/github/handlers/list_commits.js +5 -6
- package/integrations/github/handlers/list_issue_comments.js +8 -0
- package/integrations/github/handlers/list_issues.js +5 -6
- package/integrations/github/handlers/list_labels.js +8 -0
- package/integrations/github/handlers/list_pull_request_comments.js +8 -0
- package/integrations/github/handlers/list_pull_request_files.js +8 -0
- package/integrations/github/handlers/list_pull_requests.js +7 -2
- package/integrations/github/handlers/list_releases.js +8 -0
- package/integrations/github/handlers/list_tags.js +8 -0
- package/integrations/github/handlers/list_workflow_runs.js +11 -0
- package/integrations/github/handlers/request_pull_request_reviewers.js +10 -0
- package/integrations/github/handlers/search_code.js +8 -0
- package/integrations/github/handlers/search_issues.js +8 -0
- package/integrations/github/handlers/search_pull_requests.js +8 -0
- package/integrations/github/handlers/search_repos.js +10 -0
- package/integrations/github/handlers/update_pull_request.js +13 -0
- package/integrations/github/manifest.json +58 -20
- package/integrations/github/schemas/create_pull_request_review.json +17 -0
- package/integrations/github/schemas/create_release.json +16 -0
- package/integrations/github/schemas/delete_branch.json +10 -0
- package/integrations/github/schemas/delete_file.json +13 -0
- package/integrations/github/schemas/fork_repo.json +11 -0
- package/integrations/github/schemas/get_commit.json +12 -0
- package/integrations/github/schemas/get_file_contents.json +11 -0
- package/integrations/github/schemas/get_job_logs.json +10 -0
- package/integrations/github/schemas/get_pull_request.json +10 -0
- package/integrations/github/schemas/get_pull_request_diff.json +10 -0
- package/integrations/github/schemas/get_repo_tree.json +12 -0
- package/integrations/github/schemas/get_workflow_run.json +10 -0
- package/integrations/github/schemas/list_branches.json +12 -0
- package/integrations/github/schemas/list_commits.json +5 -3
- package/integrations/github/schemas/list_issue_comments.json +12 -0
- package/integrations/github/schemas/list_issues.json +4 -2
- package/integrations/github/schemas/list_labels.json +11 -0
- package/integrations/github/schemas/list_pull_request_comments.json +12 -0
- package/integrations/github/schemas/list_pull_request_files.json +12 -0
- package/integrations/github/schemas/list_pull_requests.json +7 -1
- package/integrations/github/schemas/list_releases.json +11 -0
- package/integrations/github/schemas/list_tags.json +11 -0
- package/integrations/github/schemas/list_workflow_runs.json +18 -0
- package/integrations/github/schemas/request_pull_request_reviewers.json +20 -0
- package/integrations/github/schemas/search_code.json +10 -0
- package/integrations/github/schemas/search_issues.json +10 -0
- package/integrations/github/schemas/search_pull_requests.json +10 -0
- package/integrations/github/schemas/search_repos.json +12 -0
- package/integrations/github/schemas/update_pull_request.json +15 -0
- package/integrations/google-calendar/__tests__/write_and_admin_handlers.test.ts +0 -13
- package/integrations/google-calendar/handlers/get_event.js +5 -1
- package/integrations/google-calendar/handlers/list_events.js +2 -0
- package/integrations/google-calendar/manifest.json +17 -18
- package/integrations/google-calendar/prompt.md +68 -0
- package/integrations/google-calendar/schemas/id_calendar_event.json +4 -2
- package/integrations/google-calendar/schemas/list_events.json +10 -8
- package/integrations/google-docs/__tests__/get_handlers.test.ts +5 -20
- package/integrations/google-docs/__tests__/write_handlers.test.ts +38 -52
- package/integrations/google-docs/handlers/insert_inline_image_after_first_match.js +1 -1
- package/integrations/google-docs/handlers/read_document.js +189 -0
- package/integrations/google-docs/manifest.json +16 -31
- package/integrations/google-docs/prompt.md +49 -0
- package/integrations/google-docs/schemas/insert_inline_image_after_first_match.json +0 -1
- package/integrations/google-docs/schemas/{get_document_text.json → read_document.json} +5 -2
- package/integrations/google-docs/todo.md +18 -0
- package/integrations/google-drive/__tests__/handlers.test.ts +145 -0
- package/integrations/google-drive/__tests__/usage_parity.test.ts +9 -0
- package/integrations/google-drive/handlers/get_file.js +2 -4
- package/integrations/google-drive/handlers/get_file_content.js +41 -0
- package/integrations/google-drive/handlers/list_files.js +15 -0
- package/integrations/google-drive/handlers/search_files.js +20 -0
- package/integrations/google-drive/handlers/share_file.js +20 -0
- package/integrations/google-drive/manifest.json +37 -10
- package/integrations/google-drive/prompt.md +59 -0
- package/integrations/google-drive/schemas/get_file.json +2 -2
- package/integrations/google-drive/schemas/get_file_content.json +11 -0
- package/integrations/google-drive/schemas/list_files.json +12 -0
- package/integrations/google-drive/schemas/search_files.json +14 -0
- package/integrations/google-drive/schemas/share_file.json +23 -0
- package/integrations/google-gmail/__tests__/get_handlers.test.ts +134 -0
- package/integrations/google-gmail/__tests__/usage_parity.test.ts +9 -0
- package/integrations/google-gmail/__tests__/write_and_admin_handlers.test.ts +211 -0
- package/integrations/google-gmail/credentials.json +57 -0
- package/integrations/google-gmail/credentials_hint_oauth_token.md +8 -0
- package/integrations/google-gmail/credentials_hint_service_account.md +10 -0
- package/integrations/google-gmail/handlers/create_draft_email.js +27 -0
- package/integrations/google-gmail/handlers/create_label.js +12 -0
- package/integrations/google-gmail/handlers/delete_draft.js +13 -0
- package/integrations/google-gmail/handlers/delete_label.js +13 -0
- package/integrations/google-gmail/handlers/delete_message.js +13 -0
- package/integrations/google-gmail/handlers/delete_thread.js +13 -0
- package/integrations/google-gmail/handlers/get_draft.js +6 -0
- package/integrations/google-gmail/handlers/get_label.js +6 -0
- package/integrations/google-gmail/handlers/get_message.js +14 -0
- package/integrations/google-gmail/handlers/get_profile.js +5 -0
- package/integrations/google-gmail/handlers/get_thread.js +14 -0
- package/integrations/google-gmail/handlers/list_drafts.js +15 -0
- package/integrations/google-gmail/handlers/list_labels.js +5 -0
- package/integrations/google-gmail/handlers/list_messages.js +19 -0
- package/integrations/google-gmail/handlers/list_threads.js +19 -0
- package/integrations/google-gmail/handlers/modify_message.js +11 -0
- package/integrations/google-gmail/handlers/modify_thread.js +11 -0
- package/integrations/google-gmail/handlers/read_email.js +56 -0
- package/integrations/google-gmail/handlers/send_draft.js +15 -0
- package/integrations/google-gmail/handlers/send_email.js +22 -0
- package/integrations/google-gmail/handlers/trash_message.js +6 -0
- package/integrations/google-gmail/handlers/trash_thread.js +6 -0
- package/integrations/google-gmail/handlers/untrash_message.js +6 -0
- package/integrations/google-gmail/handlers/untrash_thread.js +6 -0
- package/integrations/google-gmail/handlers/update_label.js +15 -0
- package/integrations/google-gmail/manifest.json +33 -0
- package/integrations/google-gmail/prompt.md +52 -0
- package/integrations/google-gmail/schemas/create_draft_email.json +16 -0
- package/integrations/google-gmail/schemas/create_label.json +26 -0
- package/integrations/google-gmail/schemas/get_message.json +20 -0
- package/integrations/{google-docs/schemas/get_document_structured.json → google-gmail/schemas/get_profile.json} +4 -2
- package/integrations/google-gmail/schemas/get_thread.json +20 -0
- package/integrations/google-gmail/schemas/id_draft.json +16 -0
- package/integrations/google-gmail/schemas/id_label.json +16 -0
- package/integrations/google-gmail/schemas/id_message.json +16 -0
- package/integrations/google-gmail/schemas/id_thread.json +16 -0
- package/integrations/google-gmail/schemas/list_drafts.json +30 -0
- package/integrations/{google-sheet/schemas/get_developer_metadata.json → google-gmail/schemas/list_labels.json} +4 -3
- package/integrations/google-gmail/schemas/list_messages.json +35 -0
- package/integrations/google-gmail/schemas/list_threads.json +35 -0
- package/integrations/google-gmail/schemas/modify_message.json +24 -0
- package/integrations/google-gmail/schemas/modify_thread.json +24 -0
- package/integrations/google-gmail/schemas/read_email.json +10 -0
- package/integrations/google-gmail/schemas/send_draft.json +29 -0
- package/integrations/google-gmail/schemas/send_email.json +17 -0
- package/integrations/google-gmail/schemas/update_label.json +33 -0
- package/integrations/google-sheet/__tests__/get_handlers.test.ts +7 -52
- package/integrations/google-sheet/__tests__/write_handlers.test.ts +1 -20
- package/integrations/google-sheet/handlers/get_spreadsheet.js +2 -0
- package/integrations/google-sheet/handlers/read_sheet.js +75 -0
- package/integrations/google-sheet/manifest.json +13 -62
- package/integrations/google-sheet/prompt.md +49 -0
- package/integrations/google-sheet/schemas/get_spreadsheet.json +5 -4
- package/integrations/google-sheet/schemas/read_sheet.json +21 -0
- package/integrations/google-slides/__tests__/get_handlers.test.ts +13 -9
- package/integrations/google-slides/__tests__/write_handlers.test.ts +4 -5
- package/integrations/google-slides/handlers/read_presentation.js +51 -0
- package/integrations/google-slides/manifest.json +13 -13
- package/integrations/google-slides/prompt.md +56 -0
- package/integrations/new_integration_prompt.md +5 -1
- package/package.json +1 -1
- package/integrations/google-calendar/handlers/update_event.js +0 -5
- package/integrations/google-calendar/schemas/update_event.json +0 -10
- package/integrations/google-docs/handlers/get_document.js +0 -12
- package/integrations/google-docs/handlers/get_document_structured.js +0 -6
- package/integrations/google-docs/handlers/get_document_text.js +0 -17
- package/integrations/google-docs/schemas/get_document.json +0 -11
- package/integrations/google-sheet/handlers/batch_clear_values_by_data_filter.js +0 -6
- package/integrations/google-sheet/handlers/batch_get_values.js +0 -16
- package/integrations/google-sheet/handlers/batch_update_values_by_data_filter.js +0 -16
- package/integrations/google-sheet/handlers/get_developer_metadata.js +0 -6
- package/integrations/google-sheet/handlers/get_spreadsheet_by_data_filter.js +0 -10
- package/integrations/google-sheet/handlers/get_values.js +0 -14
- package/integrations/google-sheet/handlers/get_values_by_data_filter.js +0 -14
- package/integrations/google-sheet/handlers/search_developer_metadata.js +0 -7
- package/integrations/google-sheet/schemas/batch_clear_values_by_data_filter.json +0 -10
- package/integrations/google-sheet/schemas/batch_get_values.json +0 -13
- package/integrations/google-sheet/schemas/batch_update_values_by_data_filter.json +0 -25
- package/integrations/google-sheet/schemas/get_spreadsheet_by_data_filter.json +0 -11
- package/integrations/google-sheet/schemas/get_values.json +0 -13
- package/integrations/google-sheet/schemas/get_values_by_data_filter.json +0 -17
- package/integrations/google-sheet/schemas/search_developer_metadata.json +0 -14
- package/integrations/google-slides/handlers/get_presentation.js +0 -6
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"query": { "type": "string", "description": "Raw Drive query string using the Drive search syntax. Examples: \"name contains 'budget' and mimeType = 'application/vnd.google-apps.spreadsheet'\", \"modifiedTime > '2024-01-01'\", \"'folderId' in parents\". Combined with name/mimeType shorthand via AND." },
|
|
6
|
+
"name": { "type": "string", "description": "Shorthand for name-based search. Translates to \"name contains 'value'\". Combined with query and mimeType via AND." },
|
|
7
|
+
"mimeType": { "type": "string", "description": "Filter by MIME type. Common values: 'application/vnd.google-apps.document' (Docs), 'application/vnd.google-apps.spreadsheet' (Sheets), 'application/vnd.google-apps.presentation' (Slides), 'application/vnd.google-apps.folder' (folders), 'application/pdf', 'text/plain'." },
|
|
8
|
+
"includeTrashed": { "type": "boolean", "description": "Include trashed files in results. Defaults to false (trashed files excluded)." },
|
|
9
|
+
"pageSize": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 20, "description": "Maximum results per page. Defaults to 20. Use pageToken from the response for the next page." },
|
|
10
|
+
"pageToken": { "type": "string", "description": "Page token from a previous search_files response to retrieve the next page of results." },
|
|
11
|
+
"fields": { "type": "string", "description": "Override the fields returned in each file. Defaults to 'nextPageToken,files(id,name,mimeType,modifiedTime,size,parents)'." }
|
|
12
|
+
},
|
|
13
|
+
"additionalProperties": false
|
|
14
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"required": ["fileId", "role", "type"],
|
|
5
|
+
"properties": {
|
|
6
|
+
"fileId": { "type": "string", "description": "Drive file or folder ID to share." },
|
|
7
|
+
"role": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"enum": ["reader", "commenter", "writer", "organizer", "owner"],
|
|
10
|
+
"description": "Permission level. 'reader' = view only, 'commenter' = view + comment, 'writer' = edit, 'organizer' = organize in shared drives, 'owner' = full ownership transfer."
|
|
11
|
+
},
|
|
12
|
+
"type": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"enum": ["user", "group", "domain", "anyone"],
|
|
15
|
+
"description": "Grantee type. 'user' requires emailAddress, 'group' requires emailAddress, 'domain' requires domain, 'anyone' grants access to all (use role='reader' for public link)."
|
|
16
|
+
},
|
|
17
|
+
"emailAddress": { "type": "string", "description": "Email address of the user or group to share with. Required when type is 'user' or 'group'." },
|
|
18
|
+
"domain": { "type": "string", "description": "Domain to share with (e.g. 'example.com'). Required when type is 'domain'." },
|
|
19
|
+
"sendNotificationEmail": { "type": "boolean", "description": "Whether to send a notification email to the new recipient. Defaults to true. Set to false to share silently." },
|
|
20
|
+
"emailMessage": { "type": "string", "description": "Custom message to include in the notification email." }
|
|
21
|
+
},
|
|
22
|
+
"additionalProperties": false
|
|
23
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
|
2
|
+
import { createCredentialStore, createIntegrationNode, createProxy, createToolbox, safeCleanup } from '../../__tests__/liveHarness.js'
|
|
3
|
+
|
|
4
|
+
const env = process.env as Record<string, string | undefined>
|
|
5
|
+
|
|
6
|
+
interface VariantConfig {
|
|
7
|
+
key: string
|
|
8
|
+
credentials: () => Record<string, string>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const variants: VariantConfig[] = [
|
|
12
|
+
{
|
|
13
|
+
key: 'service_account',
|
|
14
|
+
credentials: () => ({ serviceAccountJson: env.GOOGLE_SERVICE_ACCOUNT_JSON || '', subject: env.GOOGLE_IMPERSONATE_SUBJECT || '' }),
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: 'oauth_token',
|
|
18
|
+
credentials: () => ({ token: env.GOOGLE_TOKEN || '' }),
|
|
19
|
+
},
|
|
20
|
+
].filter(v => Object.values(v.credentials()).some(val => val.trim().length > 0))
|
|
21
|
+
|
|
22
|
+
const suiteOrSkip = variants.length > 0 ? describe : describe.skip
|
|
23
|
+
|
|
24
|
+
suiteOrSkip('google-gmail read handlers (live)', () => {
|
|
25
|
+
for (const variant of variants) {
|
|
26
|
+
describe(`variant: ${variant.key}`, () => {
|
|
27
|
+
const ctx: { email?: string, labelId?: string, messageId?: string, threadId?: string, draftId?: string } = {}
|
|
28
|
+
let gmail: ReturnType<typeof createToolbox>
|
|
29
|
+
|
|
30
|
+
beforeAll(async () => {
|
|
31
|
+
const credentialStore = createCredentialStore(async () => variant.credentials())
|
|
32
|
+
const proxy = createProxy(credentialStore)
|
|
33
|
+
gmail = createToolbox(
|
|
34
|
+
'google-gmail',
|
|
35
|
+
proxy,
|
|
36
|
+
createIntegrationNode('google-gmail', { label: 'Google Gmail', credentialId: 'google-gmail-creds', credentialVariant: variant.key }),
|
|
37
|
+
variant.key,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const profile = await gmail.read('get_profile')({})
|
|
41
|
+
ctx.email = profile?.emailAddress
|
|
42
|
+
|
|
43
|
+
const labels = await gmail.read('list_labels')({})
|
|
44
|
+
ctx.labelId = labels?.labels?.[0]?.id
|
|
45
|
+
|
|
46
|
+
const listedMessages = await gmail.read('list_messages')({ maxResults: 5 })
|
|
47
|
+
ctx.messageId = listedMessages?.messages?.[0]?.id
|
|
48
|
+
if (ctx.messageId) {
|
|
49
|
+
const msg = await gmail.read('get_message')({ messageId: ctx.messageId, format: 'minimal' })
|
|
50
|
+
ctx.threadId = msg?.threadId
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (ctx.email) {
|
|
54
|
+
const draft = await gmail.write('create_draft_email')({
|
|
55
|
+
to: ctx.email,
|
|
56
|
+
subject: `CmdTest Gmail Draft ${Date.now()}`,
|
|
57
|
+
body: 'Draft created by integration live tests.',
|
|
58
|
+
})
|
|
59
|
+
ctx.draftId = draft?.id
|
|
60
|
+
}
|
|
61
|
+
}, 60000)
|
|
62
|
+
|
|
63
|
+
afterAll(async () => {
|
|
64
|
+
await safeCleanup(async () => ctx.draftId ? gmail.write('delete_draft')({ draftId: ctx.draftId }) : Promise.resolve())
|
|
65
|
+
}, 30000)
|
|
66
|
+
|
|
67
|
+
it('get_profile returns mailbox profile', async () => {
|
|
68
|
+
const result = await gmail.read('get_profile')({})
|
|
69
|
+
expect(result?.emailAddress || result?.messagesTotal !== undefined).toBeTruthy()
|
|
70
|
+
}, 30000)
|
|
71
|
+
|
|
72
|
+
it('list_labels returns labels', async () => {
|
|
73
|
+
const result = await gmail.read('list_labels')({})
|
|
74
|
+
expect(Array.isArray(result?.labels)).toBe(true)
|
|
75
|
+
}, 30000)
|
|
76
|
+
|
|
77
|
+
it('get_label returns a label when available', async () => {
|
|
78
|
+
if (!ctx.labelId)
|
|
79
|
+
return expect(true).toBe(true)
|
|
80
|
+
const result = await gmail.read('get_label')({ labelId: ctx.labelId })
|
|
81
|
+
expect(result?.id).toBe(ctx.labelId)
|
|
82
|
+
}, 30000)
|
|
83
|
+
|
|
84
|
+
it('list_messages returns messages list', async () => {
|
|
85
|
+
const result = await gmail.read('list_messages')({ maxResults: 10 })
|
|
86
|
+
expect(result?.resultSizeEstimate !== undefined || Array.isArray(result?.messages)).toBe(true)
|
|
87
|
+
}, 30000)
|
|
88
|
+
|
|
89
|
+
it('get_message returns a message when available', async () => {
|
|
90
|
+
if (!ctx.messageId)
|
|
91
|
+
return expect(true).toBe(true)
|
|
92
|
+
const result = await gmail.read('get_message')({ messageId: ctx.messageId, format: 'full' })
|
|
93
|
+
expect(result?.id).toBe(ctx.messageId)
|
|
94
|
+
}, 30000)
|
|
95
|
+
|
|
96
|
+
it('list_threads returns threads list', async () => {
|
|
97
|
+
const result = await gmail.read('list_threads')({ maxResults: 10 })
|
|
98
|
+
expect(result?.resultSizeEstimate !== undefined || Array.isArray(result?.threads)).toBe(true)
|
|
99
|
+
}, 30000)
|
|
100
|
+
|
|
101
|
+
it('get_thread returns a thread when available', async () => {
|
|
102
|
+
if (!ctx.threadId)
|
|
103
|
+
return expect(true).toBe(true)
|
|
104
|
+
const result = await gmail.read('get_thread')({ threadId: ctx.threadId, format: 'full' })
|
|
105
|
+
expect(result?.id).toBe(ctx.threadId)
|
|
106
|
+
}, 30000)
|
|
107
|
+
|
|
108
|
+
it('list_drafts returns drafts list', async () => {
|
|
109
|
+
const result = await gmail.read('list_drafts')({ maxResults: 10 })
|
|
110
|
+
expect(result?.resultSizeEstimate !== undefined || Array.isArray(result?.drafts)).toBe(true)
|
|
111
|
+
}, 30000)
|
|
112
|
+
|
|
113
|
+
it('get_draft returns draft details when available', async () => {
|
|
114
|
+
if (!ctx.draftId)
|
|
115
|
+
return expect(true).toBe(true)
|
|
116
|
+
const result = await gmail.read('get_draft')({ draftId: ctx.draftId })
|
|
117
|
+
expect(result?.id).toBe(ctx.draftId)
|
|
118
|
+
}, 30000)
|
|
119
|
+
|
|
120
|
+
it('read_email returns flat decoded message when a message is available', async () => {
|
|
121
|
+
if (!ctx.messageId)
|
|
122
|
+
return expect(true).toBe(true)
|
|
123
|
+
const result = await gmail.read('read_email')({ messageId: ctx.messageId })
|
|
124
|
+
expect(result?.id).toBe(ctx.messageId)
|
|
125
|
+
expect(typeof result?.subject).toBe('string')
|
|
126
|
+
expect(typeof result?.from).toBe('string')
|
|
127
|
+
expect(typeof result?.date).toBe('string')
|
|
128
|
+
expect(typeof result?.snippet).toBe('string')
|
|
129
|
+
expect(typeof result?.body).toBe('string')
|
|
130
|
+
expect(Array.isArray(result?.labelIds)).toBe(true)
|
|
131
|
+
}, 30000)
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { getMissingToolUsages } from '../../__tests__/usageParity.js'
|
|
3
|
+
|
|
4
|
+
describe('google-gmail static usage parity', () => {
|
|
5
|
+
it('every manifest tool is referenced in tests', () => {
|
|
6
|
+
const missing = getMissingToolUsages({ integrationName: 'google-gmail', importMetaUrl: import.meta.url })
|
|
7
|
+
expect(missing, `Missing handler usages in tests: ${missing.join(', ')}`).toEqual([])
|
|
8
|
+
})
|
|
9
|
+
})
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer'
|
|
2
|
+
import { beforeAll, describe, expect, it } from 'vitest'
|
|
3
|
+
import { createCredentialStore, createIntegrationNode, createProxy, createToolbox, safeCleanup } from '../../__tests__/liveHarness.js'
|
|
4
|
+
|
|
5
|
+
const env = process.env as Record<string, string | undefined>
|
|
6
|
+
|
|
7
|
+
interface VariantConfig {
|
|
8
|
+
key: string
|
|
9
|
+
credentials: () => Record<string, string>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const variants: VariantConfig[] = [
|
|
13
|
+
{
|
|
14
|
+
key: 'service_account',
|
|
15
|
+
credentials: () => ({ serviceAccountJson: env.GOOGLE_SERVICE_ACCOUNT_JSON || '', subject: env.GOOGLE_IMPERSONATE_SUBJECT || '' }),
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
key: 'oauth_token',
|
|
19
|
+
credentials: () => ({ token: env.GOOGLE_TOKEN || '' }),
|
|
20
|
+
},
|
|
21
|
+
].filter(v => Object.values(v.credentials()).some(val => val.trim().length > 0))
|
|
22
|
+
|
|
23
|
+
const suiteOrSkip = variants.length > 0 ? describe : describe.skip
|
|
24
|
+
|
|
25
|
+
function makeRawMessage(toEmail: string, subject: string, text: string): string {
|
|
26
|
+
const mime = [
|
|
27
|
+
`To: ${toEmail}`,
|
|
28
|
+
`Subject: ${subject}`,
|
|
29
|
+
'MIME-Version: 1.0',
|
|
30
|
+
'Content-Type: text/plain; charset=UTF-8',
|
|
31
|
+
'',
|
|
32
|
+
text,
|
|
33
|
+
].join('\r\n')
|
|
34
|
+
return Buffer.from(mime, 'utf8').toString('base64url')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
suiteOrSkip('google-gmail write/admin handlers (live)', () => {
|
|
38
|
+
for (const variant of variants) {
|
|
39
|
+
describe(`variant: ${variant.key}`, () => {
|
|
40
|
+
const ctx: { email?: string, inboxLabelId?: string, messageId?: string, threadId?: string } = {}
|
|
41
|
+
let gmail: ReturnType<typeof createToolbox>
|
|
42
|
+
|
|
43
|
+
beforeAll(async () => {
|
|
44
|
+
const credentialStore = createCredentialStore(async () => variant.credentials())
|
|
45
|
+
const proxy = createProxy(credentialStore)
|
|
46
|
+
gmail = createToolbox(
|
|
47
|
+
'google-gmail',
|
|
48
|
+
proxy,
|
|
49
|
+
createIntegrationNode('google-gmail', { label: 'Google Gmail', credentialId: 'google-gmail-creds', credentialVariant: variant.key }),
|
|
50
|
+
variant.key,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
const profile = await gmail.read('get_profile')({})
|
|
54
|
+
ctx.email = profile?.emailAddress
|
|
55
|
+
|
|
56
|
+
const labels = await gmail.read('list_labels')({})
|
|
57
|
+
ctx.inboxLabelId = labels?.labels?.find((l: any) => l?.name === 'INBOX')?.id || labels?.labels?.[0]?.id
|
|
58
|
+
|
|
59
|
+
const listedMessages = await gmail.read('list_messages')({ maxResults: 5 })
|
|
60
|
+
ctx.messageId = env.GMAIL_TEST_MESSAGE_ID || listedMessages?.messages?.[0]?.id
|
|
61
|
+
if (ctx.messageId) {
|
|
62
|
+
const msg = await gmail.read('get_message')({ messageId: ctx.messageId, format: 'minimal' })
|
|
63
|
+
ctx.threadId = env.GMAIL_TEST_THREAD_ID || msg?.threadId
|
|
64
|
+
}
|
|
65
|
+
}, 60000)
|
|
66
|
+
|
|
67
|
+
it('create_draft_email -> get_draft -> delete_draft', async () => {
|
|
68
|
+
if (!ctx.email)
|
|
69
|
+
return expect(true).toBe(true)
|
|
70
|
+
const created = await gmail.write('create_draft_email')({
|
|
71
|
+
to: ctx.email,
|
|
72
|
+
subject: `CmdTest Gmail Draft ${Date.now()}`,
|
|
73
|
+
body: 'Draft created by write tests.',
|
|
74
|
+
})
|
|
75
|
+
const draftId = created?.id
|
|
76
|
+
expect(draftId).toBeTruthy()
|
|
77
|
+
const got = await gmail.read('get_draft')({ draftId })
|
|
78
|
+
expect(got?.id).toBe(draftId)
|
|
79
|
+
const deleted = await gmail.write('delete_draft')({ draftId })
|
|
80
|
+
expect(deleted?.success === true || deleted === '').toBe(true)
|
|
81
|
+
}, 60000)
|
|
82
|
+
|
|
83
|
+
it('modify_message -> trash_message -> untrash_message on an existing message', async () => {
|
|
84
|
+
// Create a fresh draft so we own the message — avoids stale/trashed inbox IDs
|
|
85
|
+
const draft = await gmail.write('create_draft_email')({
|
|
86
|
+
to: ctx.email || 'noreply@example.com',
|
|
87
|
+
subject: `CmdTest Trash ${Date.now()}`,
|
|
88
|
+
body: 'Temporary message for trash/untrash test.',
|
|
89
|
+
})
|
|
90
|
+
const messageId = draft?.message?.id
|
|
91
|
+
const draftId = draft?.id
|
|
92
|
+
expect(messageId).toBeTruthy()
|
|
93
|
+
try {
|
|
94
|
+
const modified = await gmail.write('modify_message')({
|
|
95
|
+
messageId,
|
|
96
|
+
addLabelIds: ctx.inboxLabelId ? [ctx.inboxLabelId] : undefined,
|
|
97
|
+
})
|
|
98
|
+
expect(modified?.id).toBe(messageId)
|
|
99
|
+
const trashed = await gmail.write('trash_message')({ messageId })
|
|
100
|
+
expect(trashed?.id).toBe(messageId)
|
|
101
|
+
const untrashed = await gmail.write('untrash_message')({ messageId })
|
|
102
|
+
expect(untrashed?.id).toBe(messageId)
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
await safeCleanup(async () => draftId ? gmail.write('delete_draft')({ draftId }) : Promise.resolve())
|
|
106
|
+
}
|
|
107
|
+
}, 60000)
|
|
108
|
+
|
|
109
|
+
it('modify_thread -> trash_thread -> untrash_thread on an existing thread', async () => {
|
|
110
|
+
// Create a fresh draft so we own the thread — avoids stale inbox IDs
|
|
111
|
+
const draft = await gmail.write('create_draft_email')({
|
|
112
|
+
to: ctx.email || 'noreply@example.com',
|
|
113
|
+
subject: `CmdTest Thread Trash ${Date.now()}`,
|
|
114
|
+
body: 'Temporary message for thread trash/untrash test.',
|
|
115
|
+
})
|
|
116
|
+
const threadId = draft?.message?.threadId
|
|
117
|
+
const draftId = draft?.id
|
|
118
|
+
expect(threadId).toBeTruthy()
|
|
119
|
+
try {
|
|
120
|
+
const modified = await gmail.write('modify_thread')({
|
|
121
|
+
threadId,
|
|
122
|
+
addLabelIds: ctx.inboxLabelId ? [ctx.inboxLabelId] : undefined,
|
|
123
|
+
})
|
|
124
|
+
expect(modified?.id).toBe(threadId)
|
|
125
|
+
const trashed = await gmail.write('trash_thread')({ threadId })
|
|
126
|
+
expect(trashed?.id).toBe(threadId)
|
|
127
|
+
const untrashed = await gmail.write('untrash_thread')({ threadId })
|
|
128
|
+
expect(untrashed?.id).toBe(threadId)
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
await safeCleanup(async () => draftId ? gmail.write('delete_draft')({ draftId }) : Promise.resolve())
|
|
132
|
+
}
|
|
133
|
+
}, 60000)
|
|
134
|
+
|
|
135
|
+
it('create_label -> update_label -> delete_label', async () => {
|
|
136
|
+
const created = await gmail.admin('create_label')({
|
|
137
|
+
name: `CmdTest Label ${Date.now()}`,
|
|
138
|
+
labelListVisibility: 'labelShow',
|
|
139
|
+
messageListVisibility: 'show',
|
|
140
|
+
})
|
|
141
|
+
const labelId = created?.id
|
|
142
|
+
expect(labelId).toBeTruthy()
|
|
143
|
+
const updated = await gmail.admin('update_label')({
|
|
144
|
+
labelId,
|
|
145
|
+
name: `CmdTest Label Updated ${Date.now()}`,
|
|
146
|
+
labelListVisibility: 'labelHide',
|
|
147
|
+
})
|
|
148
|
+
expect(updated?.id).toBe(labelId)
|
|
149
|
+
const deleted = await gmail.admin('delete_label')({ labelId })
|
|
150
|
+
expect(deleted?.success === true || deleted === '').toBe(true)
|
|
151
|
+
}, 60000)
|
|
152
|
+
|
|
153
|
+
it('send_email sends mail when GMAIL_TEST_SEND_TO is set', async () => {
|
|
154
|
+
const to = env.GMAIL_TEST_SEND_TO
|
|
155
|
+
if (!to)
|
|
156
|
+
return expect(true).toBe(true)
|
|
157
|
+
const sent = await gmail.write('send_email')({
|
|
158
|
+
to,
|
|
159
|
+
subject: `CmdTest Gmail send_email ${Date.now()}`,
|
|
160
|
+
body: 'Message sent by integration live test via send_email.',
|
|
161
|
+
})
|
|
162
|
+
expect(sent?.id).toBeTruthy()
|
|
163
|
+
}, 60000)
|
|
164
|
+
|
|
165
|
+
it('send_draft sends mail when GMAIL_TEST_SEND_TO is set', async () => {
|
|
166
|
+
const to = env.GMAIL_TEST_SEND_TO
|
|
167
|
+
if (!to)
|
|
168
|
+
return expect(true).toBe(true)
|
|
169
|
+
const created = await gmail.write('create_draft_email')({
|
|
170
|
+
to,
|
|
171
|
+
subject: `CmdTest Gmail send_draft ${Date.now()}`,
|
|
172
|
+
body: 'Draft sent by integration live test.',
|
|
173
|
+
})
|
|
174
|
+
const draftId = created?.id
|
|
175
|
+
expect(draftId).toBeTruthy()
|
|
176
|
+
const sent = await gmail.write('send_draft')({ draftId })
|
|
177
|
+
expect(sent?.id || sent?.threadId).toBeTruthy()
|
|
178
|
+
}, 60000)
|
|
179
|
+
|
|
180
|
+
it('delete_message deletes message when GMAIL_TEST_DELETE_MESSAGE_ID is set', async () => {
|
|
181
|
+
const deleteMessageId = env.GMAIL_TEST_DELETE_MESSAGE_ID
|
|
182
|
+
if (!deleteMessageId)
|
|
183
|
+
return expect(true).toBe(true)
|
|
184
|
+
const deleted = await gmail.write('delete_message')({ messageId: deleteMessageId })
|
|
185
|
+
expect(deleted?.success === true || deleted === '').toBe(true)
|
|
186
|
+
}, 60000)
|
|
187
|
+
|
|
188
|
+
it('delete_thread deletes thread when GMAIL_TEST_DELETE_THREAD_ID is set', async () => {
|
|
189
|
+
const deleteThreadId = env.GMAIL_TEST_DELETE_THREAD_ID
|
|
190
|
+
if (!deleteThreadId)
|
|
191
|
+
return expect(true).toBe(true)
|
|
192
|
+
const deleted = await gmail.write('delete_thread')({ threadId: deleteThreadId })
|
|
193
|
+
expect(deleted?.success === true || deleted === '').toBe(true)
|
|
194
|
+
}, 60000)
|
|
195
|
+
|
|
196
|
+
it('send_draft supports raw payload mode', async () => {
|
|
197
|
+
const to = env.GMAIL_TEST_SEND_TO
|
|
198
|
+
if (!to)
|
|
199
|
+
return expect(true).toBe(true)
|
|
200
|
+
const raw = makeRawMessage(to, `CmdTest Gmail send_draft raw ${Date.now()}`, 'Raw payload draft-send mode.')
|
|
201
|
+
const sent = await gmail.write('send_draft')({ raw })
|
|
202
|
+
expect(sent?.id || sent?.threadId).toBeTruthy()
|
|
203
|
+
}, 60000)
|
|
204
|
+
|
|
205
|
+
it('cleanup helper remains available for optional future cleanup', async () => {
|
|
206
|
+
await safeCleanup(async () => Promise.resolve())
|
|
207
|
+
expect(true).toBe(true)
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
})
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"variants": {
|
|
3
|
+
"service_account": {
|
|
4
|
+
"label": "Service Account (recommended)",
|
|
5
|
+
"schema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"serviceAccountJson": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"title": "Service Account JSON",
|
|
11
|
+
"description": "Full service account key JSON (contents of the downloaded JSON file from Google Cloud)."
|
|
12
|
+
},
|
|
13
|
+
"subject": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"title": "Subject / impersonated user (optional)",
|
|
16
|
+
"description": "User email to impersonate via Google Workspace domain-wide delegation. Usually required for mailbox access."
|
|
17
|
+
},
|
|
18
|
+
"scopes": {
|
|
19
|
+
"type": "array",
|
|
20
|
+
"title": "OAuth scopes (optional)",
|
|
21
|
+
"description": "Optional override for OAuth scopes. Defaults to full Gmail access.",
|
|
22
|
+
"items": { "type": "string" }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required": ["serviceAccountJson"],
|
|
26
|
+
"additionalProperties": false
|
|
27
|
+
},
|
|
28
|
+
"injection": {
|
|
29
|
+
"headers": {
|
|
30
|
+
"Authorization": "Bearer {{token}}"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"preprocess": "google_service_account"
|
|
34
|
+
},
|
|
35
|
+
"oauth_token": {
|
|
36
|
+
"label": "OAuth Access Token (short-lived)",
|
|
37
|
+
"schema": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"properties": {
|
|
40
|
+
"token": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"title": "OAuth Access Token",
|
|
43
|
+
"description": "Short-lived Google OAuth access token with Gmail scopes."
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"required": ["token"],
|
|
47
|
+
"additionalProperties": false
|
|
48
|
+
},
|
|
49
|
+
"injection": {
|
|
50
|
+
"headers": {
|
|
51
|
+
"Authorization": "Bearer {{token}}"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"default": "service_account"
|
|
57
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Obtain a short-lived Google OAuth access token:
|
|
2
|
+
|
|
3
|
+
1. Use the Google OAuth 2.0 Playground (`https://developers.google.com/oauthplayground/`) or your own OAuth flow
|
|
4
|
+
2. Select scopes such as `https://www.googleapis.com/auth/gmail.modify` (and `https://www.googleapis.com/auth/gmail.send` if sending mail)
|
|
5
|
+
3. Exchange the authorization code for an access token
|
|
6
|
+
4. Paste the access token here
|
|
7
|
+
|
|
8
|
+
Note: OAuth access tokens are short-lived (typically 1 hour). For long-running automation, prefer the Service Account variant with Workspace delegation.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Set up a Google Cloud Service Account:
|
|
2
|
+
|
|
3
|
+
1. Open the [Google Cloud Console](https://console.cloud.google.com/)
|
|
4
|
+
2. Enable the **Gmail API** for your project
|
|
5
|
+
3. Go to **IAM & Admin -> Service Accounts** and create a new service account
|
|
6
|
+
4. Under **Keys**, click **Add Key -> Create new key -> JSON** and download the file
|
|
7
|
+
5. Paste the full contents of the JSON file here
|
|
8
|
+
6. For Google Workspace mailboxes, configure domain-wide delegation and set `subject` to the target user's email
|
|
9
|
+
|
|
10
|
+
For personal Gmail accounts, use the OAuth token variant instead.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const userId = encodeURIComponent(input.userId || 'me')
|
|
3
|
+
const lines = []
|
|
4
|
+
if (input.to) lines.push(`To: ${input.to}`)
|
|
5
|
+
lines.push(`Subject: ${input.subject || ''}`)
|
|
6
|
+
if (input.cc) lines.push(`Cc: ${input.cc}`)
|
|
7
|
+
if (input.bcc) lines.push(`Bcc: ${input.bcc}`)
|
|
8
|
+
if (input.replyToMessageId) lines.push(`In-Reply-To: ${input.replyToMessageId}`)
|
|
9
|
+
lines.push('MIME-Version: 1.0')
|
|
10
|
+
if (input.htmlBody) {
|
|
11
|
+
lines.push('Content-Type: text/html; charset=UTF-8')
|
|
12
|
+
lines.push('', input.htmlBody)
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
lines.push('Content-Type: text/plain; charset=UTF-8')
|
|
16
|
+
lines.push('', input.body || '')
|
|
17
|
+
}
|
|
18
|
+
const raw = Buffer.from(lines.join('\r\n')).toString('base64url')
|
|
19
|
+
const message = { raw }
|
|
20
|
+
if (input.threadId) message.threadId = input.threadId
|
|
21
|
+
|
|
22
|
+
const res = await integration.fetch(`/users/${userId}/drafts`, {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
body: { message },
|
|
25
|
+
})
|
|
26
|
+
return await res.json()
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const userId = encodeURIComponent(input.userId || 'me')
|
|
3
|
+
const body = { name: input.name }
|
|
4
|
+
if (input.messageListVisibility)
|
|
5
|
+
body.messageListVisibility = input.messageListVisibility
|
|
6
|
+
if (input.labelListVisibility)
|
|
7
|
+
body.labelListVisibility = input.labelListVisibility
|
|
8
|
+
if (input.color)
|
|
9
|
+
body.color = input.color
|
|
10
|
+
const res = await integration.fetch(`/users/${userId}/labels`, { method: 'POST', body })
|
|
11
|
+
return await res.json()
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const userId = encodeURIComponent(input.userId || 'me')
|
|
3
|
+
const draftId = encodeURIComponent(input.draftId)
|
|
4
|
+
const res = await integration.fetch(`/users/${userId}/drafts/${draftId}`, { method: 'DELETE' })
|
|
5
|
+
if (res.status === 204)
|
|
6
|
+
return { success: true }
|
|
7
|
+
try {
|
|
8
|
+
return await res.json()
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return { success: true }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const userId = encodeURIComponent(input.userId || 'me')
|
|
3
|
+
const labelId = encodeURIComponent(input.labelId)
|
|
4
|
+
const res = await integration.fetch(`/users/${userId}/labels/${labelId}`, { method: 'DELETE' })
|
|
5
|
+
if (res.status === 204)
|
|
6
|
+
return { success: true }
|
|
7
|
+
try {
|
|
8
|
+
return await res.json()
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return { success: true }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const userId = encodeURIComponent(input.userId || 'me')
|
|
3
|
+
const messageId = encodeURIComponent(input.messageId)
|
|
4
|
+
const res = await integration.fetch(`/users/${userId}/messages/${messageId}`, { method: 'DELETE' })
|
|
5
|
+
if (res.status === 204)
|
|
6
|
+
return { success: true }
|
|
7
|
+
try {
|
|
8
|
+
return await res.json()
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return { success: true }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const userId = encodeURIComponent(input.userId || 'me')
|
|
3
|
+
const threadId = encodeURIComponent(input.threadId)
|
|
4
|
+
const res = await integration.fetch(`/users/${userId}/threads/${threadId}`, { method: 'DELETE' })
|
|
5
|
+
if (res.status === 204)
|
|
6
|
+
return { success: true }
|
|
7
|
+
try {
|
|
8
|
+
return await res.json()
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return { success: true }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const userId = encodeURIComponent(input.userId || 'me')
|
|
3
|
+
const messageId = encodeURIComponent(input.messageId)
|
|
4
|
+
const params = new URLSearchParams()
|
|
5
|
+
if (input.format)
|
|
6
|
+
params.set('format', input.format)
|
|
7
|
+
if (Array.isArray(input.metadataHeaders)) {
|
|
8
|
+
for (const header of input.metadataHeaders)
|
|
9
|
+
params.append('metadataHeaders', String(header))
|
|
10
|
+
}
|
|
11
|
+
const qs = params.toString()
|
|
12
|
+
const res = await integration.fetch(`/users/${userId}/messages/${messageId}${qs ? `?${qs}` : ''}`)
|
|
13
|
+
return await res.json()
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const userId = encodeURIComponent(input.userId || 'me')
|
|
3
|
+
const threadId = encodeURIComponent(input.threadId)
|
|
4
|
+
const params = new URLSearchParams()
|
|
5
|
+
if (input.format)
|
|
6
|
+
params.set('format', input.format)
|
|
7
|
+
if (Array.isArray(input.metadataHeaders)) {
|
|
8
|
+
for (const header of input.metadataHeaders)
|
|
9
|
+
params.append('metadataHeaders', String(header))
|
|
10
|
+
}
|
|
11
|
+
const qs = params.toString()
|
|
12
|
+
const res = await integration.fetch(`/users/${userId}/threads/${threadId}${qs ? `?${qs}` : ''}`)
|
|
13
|
+
return await res.json()
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const userId = encodeURIComponent(input.userId || 'me')
|
|
3
|
+
const params = new URLSearchParams()
|
|
4
|
+
if (input.q)
|
|
5
|
+
params.set('q', input.q)
|
|
6
|
+
if (input.maxResults !== undefined)
|
|
7
|
+
params.set('maxResults', String(input.maxResults))
|
|
8
|
+
if (input.pageToken)
|
|
9
|
+
params.set('pageToken', input.pageToken)
|
|
10
|
+
if (input.includeSpamTrash !== undefined)
|
|
11
|
+
params.set('includeSpamTrash', String(input.includeSpamTrash))
|
|
12
|
+
const qs = params.toString()
|
|
13
|
+
const res = await integration.fetch(`/users/${userId}/drafts${qs ? `?${qs}` : ''}`)
|
|
14
|
+
return await res.json()
|
|
15
|
+
}
|