@commandable/integration-data 0.0.1
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 +30 -0
- package/dist/credentials-index.d.ts.map +1 -0
- package/dist/credentials-index.js +292 -0
- package/dist/credentials-index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +69 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +164 -0
- package/dist/loader.js.map +1 -0
- package/integrations/README.md +52 -0
- package/integrations/airtable/__tests__/get_handlers.test.ts +137 -0
- package/integrations/airtable/__tests__/usage_parity.test.ts +35 -0
- package/integrations/airtable/__tests__/write_and_admin_handlers.test.ts +113 -0
- package/integrations/airtable/credentials.json +20 -0
- package/integrations/airtable/credentials_hint.md +4 -0
- package/integrations/airtable/handlers/create_record.js +12 -0
- package/integrations/airtable/handlers/delete_record.js +7 -0
- package/integrations/airtable/handlers/get_record.js +4 -0
- package/integrations/airtable/handlers/get_table_schema.js +6 -0
- package/integrations/airtable/handlers/list_bases.js +4 -0
- package/integrations/airtable/handlers/list_records.js +25 -0
- package/integrations/airtable/handlers/list_table_fields.js +6 -0
- package/integrations/airtable/handlers/list_tables.js +4 -0
- package/integrations/airtable/handlers/list_views.js +6 -0
- package/integrations/airtable/handlers/search_records.js +6 -0
- package/integrations/airtable/handlers/update_record.js +11 -0
- package/integrations/airtable/manifest.json +83 -0
- package/integrations/airtable/schemas/create_record.json +12 -0
- package/integrations/airtable/schemas/delete_record.json +11 -0
- package/integrations/airtable/schemas/empty.json +6 -0
- package/integrations/airtable/schemas/get_record.json +11 -0
- package/integrations/airtable/schemas/id_base.json +9 -0
- package/integrations/airtable/schemas/id_base_table.json +10 -0
- package/integrations/airtable/schemas/list_records.json +26 -0
- package/integrations/airtable/schemas/search_records.json +12 -0
- package/integrations/airtable/schemas/update_record.json +13 -0
- package/integrations/github/__tests__/get_handlers.test.ts +117 -0
- package/integrations/github/__tests__/usage_parity.test.ts +34 -0
- package/integrations/github/__tests__/write_handlers.test.ts +311 -0
- package/integrations/github/credentials.json +20 -0
- package/integrations/github/credentials_hint.md +7 -0
- package/integrations/github/handlers/add_labels_to_issue.js +12 -0
- package/integrations/github/handlers/close_issue.js +5 -0
- package/integrations/github/handlers/comment_on_issue.js +5 -0
- package/integrations/github/handlers/create_branch.js +33 -0
- package/integrations/github/handlers/create_commit.js +91 -0
- package/integrations/github/handlers/create_issue.js +10 -0
- package/integrations/github/handlers/create_or_update_file.js +21 -0
- package/integrations/github/handlers/create_pull_request.js +16 -0
- package/integrations/github/handlers/create_repo.js +11 -0
- package/integrations/github/handlers/delete_repo.js +6 -0
- package/integrations/github/handlers/get_issue.js +4 -0
- package/integrations/github/handlers/get_repo.js +4 -0
- package/integrations/github/handlers/list_branches.js +4 -0
- package/integrations/github/handlers/list_commits.js +12 -0
- package/integrations/github/handlers/list_issues.js +12 -0
- package/integrations/github/handlers/list_pull_requests.js +8 -0
- package/integrations/github/handlers/list_repos_install.js +4 -0
- package/integrations/github/handlers/list_repos_user.js +4 -0
- package/integrations/github/handlers/merge_pull_request.js +14 -0
- package/integrations/github/handlers/update_issue.js +15 -0
- package/integrations/github/manifest.json +26 -0
- package/integrations/github/schemas/add_labels_to_issue.json +12 -0
- package/integrations/github/schemas/close_issue.json +10 -0
- package/integrations/github/schemas/comment_on_issue.json +11 -0
- package/integrations/github/schemas/create_branch.json +12 -0
- package/integrations/github/schemas/create_commit.json +25 -0
- package/integrations/github/schemas/create_issue.json +13 -0
- package/integrations/github/schemas/create_or_update_file.json +15 -0
- package/integrations/github/schemas/create_pull_request.json +15 -0
- package/integrations/github/schemas/create_repo.json +12 -0
- package/integrations/github/schemas/delete_repo.json +10 -0
- package/integrations/github/schemas/empty.json +5 -0
- package/integrations/github/schemas/get_issue.json +10 -0
- package/integrations/github/schemas/get_repo.json +9 -0
- package/integrations/github/schemas/list_commits.json +12 -0
- package/integrations/github/schemas/list_issues.json +12 -0
- package/integrations/github/schemas/list_pull_requests.json +10 -0
- package/integrations/github/schemas/merge_pull_request.json +14 -0
- package/integrations/github/schemas/owner_repo.json +9 -0
- package/integrations/github/schemas/update_issue.json +15 -0
- package/integrations/google-calendar/__tests__/get_handlers.test.ts +112 -0
- package/integrations/google-calendar/__tests__/usage_parity.test.ts +34 -0
- package/integrations/google-calendar/__tests__/write_and_admin_handlers.test.ts +161 -0
- package/integrations/google-calendar/credentials.json +36 -0
- package/integrations/google-calendar/credentials_hint.md +9 -0
- package/integrations/google-calendar/handlers/create_event.js +6 -0
- package/integrations/google-calendar/handlers/delete_acl.js +6 -0
- package/integrations/google-calendar/handlers/delete_event.js +7 -0
- package/integrations/google-calendar/handlers/freebusy_query.js +4 -0
- package/integrations/google-calendar/handlers/get_acl.js +5 -0
- package/integrations/google-calendar/handlers/get_calendar.js +4 -0
- package/integrations/google-calendar/handlers/get_event.js +5 -0
- package/integrations/google-calendar/handlers/insert_acl.js +6 -0
- package/integrations/google-calendar/handlers/list_acl.js +5 -0
- package/integrations/google-calendar/handlers/list_calendars.js +4 -0
- package/integrations/google-calendar/handlers/list_colors.js +4 -0
- package/integrations/google-calendar/handlers/list_events.js +21 -0
- package/integrations/google-calendar/handlers/list_settings.js +4 -0
- package/integrations/google-calendar/handlers/move_event.js +6 -0
- package/integrations/google-calendar/handlers/patch_event.js +5 -0
- package/integrations/google-calendar/handlers/quick_add.js +6 -0
- package/integrations/google-calendar/handlers/update_acl.js +7 -0
- package/integrations/google-calendar/handlers/update_event.js +5 -0
- package/integrations/google-calendar/manifest.json +26 -0
- package/integrations/google-calendar/schemas/create_event.json +34 -0
- package/integrations/google-calendar/schemas/delete_acl.json +9 -0
- package/integrations/google-calendar/schemas/empty.json +1 -0
- package/integrations/google-calendar/schemas/freebusy_query.json +13 -0
- package/integrations/google-calendar/schemas/get_acl.json +9 -0
- package/integrations/google-calendar/schemas/id_calendar.json +8 -0
- package/integrations/google-calendar/schemas/id_calendar_event.json +9 -0
- package/integrations/google-calendar/schemas/insert_acl.json +18 -0
- package/integrations/google-calendar/schemas/list_events.json +15 -0
- package/integrations/google-calendar/schemas/move_event.json +10 -0
- package/integrations/google-calendar/schemas/patch_event.json +10 -0
- package/integrations/google-calendar/schemas/quick_add.json +9 -0
- package/integrations/google-calendar/schemas/update_acl.json +10 -0
- package/integrations/google-calendar/schemas/update_event.json +10 -0
- package/integrations/google-docs/__tests__/get_handlers.test.ts +72 -0
- package/integrations/google-docs/__tests__/usage_parity.test.ts +34 -0
- package/integrations/google-docs/__tests__/write_handlers.test.ts +249 -0
- package/integrations/google-docs/credentials.json +36 -0
- package/integrations/google-docs/credentials_hint.md +9 -0
- package/integrations/google-docs/handlers/append_text.js +12 -0
- package/integrations/google-docs/handlers/batch_update.js +13 -0
- package/integrations/google-docs/handlers/create_document.js +9 -0
- package/integrations/google-docs/handlers/delete_first_match.js +50 -0
- package/integrations/google-docs/handlers/get_document.js +12 -0
- package/integrations/google-docs/handlers/get_document_structured.js +6 -0
- package/integrations/google-docs/handlers/get_document_text.js +17 -0
- package/integrations/google-docs/handlers/insert_inline_image_after_first_match.js +41 -0
- package/integrations/google-docs/handlers/insert_page_break_after_first_match.js +49 -0
- package/integrations/google-docs/handlers/insert_table_after_first_match.js +49 -0
- package/integrations/google-docs/handlers/insert_text_after_first_match.js +51 -0
- package/integrations/google-docs/handlers/replace_all_text.js +8 -0
- package/integrations/google-docs/handlers/style_first_match.js +42 -0
- package/integrations/google-docs/handlers/update_document_style.js +8 -0
- package/integrations/google-docs/handlers/update_paragraph_style_for_first_match.js +48 -0
- package/integrations/google-docs/manifest.json +58 -0
- package/integrations/google-docs/schemas/append_text.json +10 -0
- package/integrations/google-docs/schemas/apply_text_style.json +13 -0
- package/integrations/google-docs/schemas/batch_update.json +16 -0
- package/integrations/google-docs/schemas/create_document.json +8 -0
- package/integrations/google-docs/schemas/delete_content_range.json +11 -0
- package/integrations/google-docs/schemas/delete_first_match.json +10 -0
- package/integrations/google-docs/schemas/get_document.json +11 -0
- package/integrations/google-docs/schemas/get_document_structured.json +9 -0
- package/integrations/google-docs/schemas/get_document_text.json +9 -0
- package/integrations/google-docs/schemas/insert_inline_image.json +12 -0
- package/integrations/google-docs/schemas/insert_inline_image_after_first_match.json +13 -0
- package/integrations/google-docs/schemas/insert_page_break.json +10 -0
- package/integrations/google-docs/schemas/insert_page_break_after_first_match.json +11 -0
- package/integrations/google-docs/schemas/insert_table.json +12 -0
- package/integrations/google-docs/schemas/insert_table_after_first_match.json +13 -0
- package/integrations/google-docs/schemas/insert_text_after_first_match.json +12 -0
- package/integrations/google-docs/schemas/insert_text_at.json +11 -0
- package/integrations/google-docs/schemas/replace_all_text.json +12 -0
- package/integrations/google-docs/schemas/style_first_match.json +12 -0
- package/integrations/google-docs/schemas/update_document_style.json +11 -0
- package/integrations/google-docs/schemas/update_paragraph_style.json +13 -0
- package/integrations/google-docs/schemas/update_paragraph_style_for_first_match.json +12 -0
- package/integrations/google-sheet/__tests__/get_handlers.test.ts +129 -0
- package/integrations/google-sheet/__tests__/usage_parity.test.ts +35 -0
- package/integrations/google-sheet/__tests__/write_handlers.test.ts +171 -0
- package/integrations/google-sheet/credentials.json +36 -0
- package/integrations/google-sheet/credentials_hint.md +9 -0
- package/integrations/google-sheet/handlers/append_values.js +18 -0
- package/integrations/google-sheet/handlers/batch_clear_values.js +6 -0
- package/integrations/google-sheet/handlers/batch_clear_values_by_data_filter.js +6 -0
- package/integrations/google-sheet/handlers/batch_get_values.js +16 -0
- package/integrations/google-sheet/handlers/batch_update.js +14 -0
- package/integrations/google-sheet/handlers/batch_update_values.js +16 -0
- package/integrations/google-sheet/handlers/batch_update_values_by_data_filter.js +16 -0
- package/integrations/google-sheet/handlers/clear_values.js +6 -0
- package/integrations/google-sheet/handlers/copy_to_spreadsheet.js +6 -0
- package/integrations/google-sheet/handlers/create_spreadsheet.js +5 -0
- package/integrations/google-sheet/handlers/get_developer_metadata.js +6 -0
- package/integrations/google-sheet/handlers/get_spreadsheet.js +12 -0
- package/integrations/google-sheet/handlers/get_spreadsheet_by_data_filter.js +10 -0
- package/integrations/google-sheet/handlers/get_values.js +14 -0
- package/integrations/google-sheet/handlers/get_values_by_data_filter.js +14 -0
- package/integrations/google-sheet/handlers/search_developer_metadata.js +7 -0
- package/integrations/google-sheet/handlers/update_values.js +16 -0
- package/integrations/google-sheet/manifest.json +125 -0
- package/integrations/google-sheet/schemas/append_values.json +16 -0
- package/integrations/google-sheet/schemas/batch_clear_values.json +10 -0
- package/integrations/google-sheet/schemas/batch_clear_values_by_data_filter.json +10 -0
- package/integrations/google-sheet/schemas/batch_get_values.json +13 -0
- package/integrations/google-sheet/schemas/batch_update.json +13 -0
- package/integrations/google-sheet/schemas/batch_update_values.json +25 -0
- package/integrations/google-sheet/schemas/batch_update_values_by_data_filter.json +25 -0
- package/integrations/google-sheet/schemas/clear_values.json +10 -0
- package/integrations/google-sheet/schemas/copy_to_spreadsheet.json +11 -0
- package/integrations/google-sheet/schemas/create_spreadsheet.json +11 -0
- package/integrations/google-sheet/schemas/get_developer_metadata.json +10 -0
- package/integrations/google-sheet/schemas/get_spreadsheet.json +15 -0
- package/integrations/google-sheet/schemas/get_spreadsheet_by_data_filter.json +11 -0
- package/integrations/google-sheet/schemas/get_values.json +13 -0
- package/integrations/google-sheet/schemas/get_values_by_data_filter.json +17 -0
- package/integrations/google-sheet/schemas/search_developer_metadata.json +14 -0
- package/integrations/google-sheet/schemas/update_values.json +15 -0
- package/integrations/google-slides/__tests__/get_handlers.test.ts +69 -0
- package/integrations/google-slides/__tests__/usage_parity.test.ts +34 -0
- package/integrations/google-slides/__tests__/write_handlers.test.ts +125 -0
- package/integrations/google-slides/credentials.json +36 -0
- package/integrations/google-slides/credentials_hint.md +9 -0
- package/integrations/google-slides/handlers/append_text_to_title_of_first_slide.js +17 -0
- package/integrations/google-slides/handlers/batch_update.js +15 -0
- package/integrations/google-slides/handlers/create_presentation.js +8 -0
- package/integrations/google-slides/handlers/create_slide_after_first_match.js +20 -0
- package/integrations/google-slides/handlers/get_page_thumbnail.js +12 -0
- package/integrations/google-slides/handlers/get_presentation.js +6 -0
- package/integrations/google-slides/handlers/insert_image_after_first_match.js +19 -0
- package/integrations/google-slides/handlers/insert_shape_after_first_match.js +21 -0
- package/integrations/google-slides/handlers/replace_text_first_match.js +9 -0
- package/integrations/google-slides/handlers/set_background_color_for_slide_index.js +15 -0
- package/integrations/google-slides/handlers/style_text_first_match.js +48 -0
- package/integrations/google-slides/manifest.json +41 -0
- package/integrations/google-slides/schemas/append_text_to_title_of_first_slide.json +11 -0
- package/integrations/google-slides/schemas/batch_update.json +13 -0
- package/integrations/google-slides/schemas/create_presentation.json +8 -0
- package/integrations/google-slides/schemas/create_slide_after_first_match.json +11 -0
- package/integrations/google-slides/schemas/get_page_thumbnail.json +12 -0
- package/integrations/google-slides/schemas/get_presentation.json +9 -0
- package/integrations/google-slides/schemas/insert_image_after_first_match.json +13 -0
- package/integrations/google-slides/schemas/insert_shape_after_first_match.json +13 -0
- package/integrations/google-slides/schemas/replace_text_first_match.json +12 -0
- package/integrations/google-slides/schemas/set_background_color_for_slide_index.json +11 -0
- package/integrations/google-slides/schemas/style_text_first_match.json +12 -0
- package/integrations/new_integration_prompt.md +41 -0
- package/integrations/notion/__tests__/get_handlers.test.ts +153 -0
- package/integrations/notion/__tests__/usage_parity.test.ts +34 -0
- package/integrations/notion/__tests__/write_and_admin_handlers.test.ts +190 -0
- package/integrations/notion/credentials.json +21 -0
- package/integrations/notion/credentials_hint.md +5 -0
- package/integrations/notion/handlers/append_block_children.js +7 -0
- package/integrations/notion/handlers/create_comment.js +10 -0
- package/integrations/notion/handlers/create_database.js +11 -0
- package/integrations/notion/handlers/create_page.js +13 -0
- package/integrations/notion/handlers/delete_block.js +8 -0
- package/integrations/notion/handlers/get_me.js +4 -0
- package/integrations/notion/handlers/list_block_children.js +10 -0
- package/integrations/notion/handlers/list_comments.js +14 -0
- package/integrations/notion/handlers/list_users.js +10 -0
- package/integrations/notion/handlers/query_database.js +10 -0
- package/integrations/notion/handlers/retrieve_block.js +4 -0
- package/integrations/notion/handlers/retrieve_database.js +4 -0
- package/integrations/notion/handlers/retrieve_page.js +4 -0
- package/integrations/notion/handlers/retrieve_page_property_item.js +10 -0
- package/integrations/notion/handlers/retrieve_user.js +4 -0
- package/integrations/notion/handlers/search.js +11 -0
- package/integrations/notion/handlers/update_block.js +7 -0
- package/integrations/notion/handlers/update_database.js +10 -0
- package/integrations/notion/handlers/update_page_properties.js +10 -0
- package/integrations/notion/manifest.json +139 -0
- package/integrations/notion/prompt.md +26 -0
- package/integrations/notion/schemas/append_block_children.json +10 -0
- package/integrations/notion/schemas/create_comment.json +18 -0
- package/integrations/notion/schemas/create_database.json +18 -0
- package/integrations/notion/schemas/create_page.json +22 -0
- package/integrations/notion/schemas/delete_block.json +9 -0
- package/integrations/notion/schemas/empty.json +6 -0
- package/integrations/notion/schemas/id_block.json +9 -0
- package/integrations/notion/schemas/id_database.json +9 -0
- package/integrations/notion/schemas/id_page.json +9 -0
- package/integrations/notion/schemas/id_user.json +9 -0
- package/integrations/notion/schemas/list_block_children.json +11 -0
- package/integrations/notion/schemas/list_comments.json +15 -0
- package/integrations/notion/schemas/list_users.json +9 -0
- package/integrations/notion/schemas/query_database.json +13 -0
- package/integrations/notion/schemas/retrieve_page_property_item.json +12 -0
- package/integrations/notion/schemas/search.json +27 -0
- package/integrations/notion/schemas/update_block.json +10 -0
- package/integrations/notion/schemas/update_database.json +13 -0
- package/integrations/notion/schemas/update_page_properties.json +13 -0
- package/integrations/trello/__tests__/get_handlers.test.ts +240 -0
- package/integrations/trello/__tests__/usage_parity.test.ts +34 -0
- package/integrations/trello/__tests__/write_and_admin_handlers.test.ts +189 -0
- package/integrations/trello/credentials.json +26 -0
- package/integrations/trello/credentials_hint.md +4 -0
- package/integrations/trello/handlers/add_checklist_to_card.js +5 -0
- package/integrations/trello/handlers/add_member_to_card.js +5 -0
- package/integrations/trello/handlers/archive_list.js +5 -0
- package/integrations/trello/handlers/create_card.js +13 -0
- package/integrations/trello/handlers/create_list.js +7 -0
- package/integrations/trello/handlers/delete_card.js +9 -0
- package/integrations/trello/handlers/get_board.js +4 -0
- package/integrations/trello/handlers/get_board_cards.js +4 -0
- package/integrations/trello/handlers/get_board_custom_fields.js +4 -0
- package/integrations/trello/handlers/get_board_labels.js +4 -0
- package/integrations/trello/handlers/get_board_lists.js +4 -0
- package/integrations/trello/handlers/get_board_members.js +4 -0
- package/integrations/trello/handlers/get_board_memberships.js +4 -0
- package/integrations/trello/handlers/get_card.js +4 -0
- package/integrations/trello/handlers/get_card_actions.js +4 -0
- package/integrations/trello/handlers/get_card_attachments.js +4 -0
- package/integrations/trello/handlers/get_card_checklists.js +4 -0
- package/integrations/trello/handlers/get_card_custom_field_items.js +4 -0
- package/integrations/trello/handlers/get_card_members.js +4 -0
- package/integrations/trello/handlers/get_list.js +4 -0
- package/integrations/trello/handlers/get_list_cards.js +4 -0
- package/integrations/trello/handlers/get_member.js +4 -0
- package/integrations/trello/handlers/get_member_boards.js +4 -0
- package/integrations/trello/handlers/get_member_organizations.js +4 -0
- package/integrations/trello/handlers/get_organization.js +4 -0
- package/integrations/trello/handlers/get_organization_boards.js +4 -0
- package/integrations/trello/handlers/move_card_to_list.js +5 -0
- package/integrations/trello/handlers/remove_member_from_card.js +9 -0
- package/integrations/trello/handlers/search.js +5 -0
- package/integrations/trello/handlers/update_card.js +19 -0
- package/integrations/trello/handlers/update_list.js +11 -0
- package/integrations/trello/manifest.json +231 -0
- package/integrations/trello/schemas/add_checklist_to_card.json +10 -0
- package/integrations/trello/schemas/add_member_to_card.json +10 -0
- package/integrations/trello/schemas/archive_list.json +9 -0
- package/integrations/trello/schemas/create_card.json +13 -0
- package/integrations/trello/schemas/create_list.json +11 -0
- package/integrations/trello/schemas/delete_card.json +9 -0
- package/integrations/trello/schemas/display_trello_cards.json +45 -0
- package/integrations/trello/schemas/empty.json +5 -0
- package/integrations/trello/schemas/get_member.json +5 -0
- package/integrations/trello/schemas/id_board.json +8 -0
- package/integrations/trello/schemas/id_card.json +8 -0
- package/integrations/trello/schemas/id_list.json +8 -0
- package/integrations/trello/schemas/id_org.json +8 -0
- package/integrations/trello/schemas/move_card_to_list.json +10 -0
- package/integrations/trello/schemas/remove_member_from_card.json +10 -0
- package/integrations/trello/schemas/search.json +8 -0
- package/integrations/trello/schemas/update_card.json +16 -0
- package/integrations/trello/schemas/update_list.json +12 -0
- package/package.json +45 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, it } from 'vitest'
|
|
2
|
+
import { IntegrationProxy } from '../../../src/integrations/proxy.js'
|
|
3
|
+
import { loadIntegrationTools } from '../../../src/integrations/dataLoader.js'
|
|
4
|
+
|
|
5
|
+
// LIVE Google Docs read tests using managed OAuth
|
|
6
|
+
// Required env vars:
|
|
7
|
+
// - COMMANDABLE_MANAGED_OAUTH_BASE_URL
|
|
8
|
+
// - COMMANDABLE_MANAGED_OAUTH_SECRET_KEY
|
|
9
|
+
// - GDOCS_TEST_CONNECTION_ID (managed OAuth connection for provider 'google-docs')
|
|
10
|
+
// - GDOCS_TEST_DOCUMENT_ID (an accessible document ID)
|
|
11
|
+
|
|
12
|
+
const env = process.env as Record<string, string>
|
|
13
|
+
const hasEnv = (...keys: string[]) => keys.every(k => !!env[k] && env[k].trim().length > 0)
|
|
14
|
+
const suite = hasEnv(
|
|
15
|
+
'COMMANDABLE_MANAGED_OAUTH_BASE_URL',
|
|
16
|
+
'COMMANDABLE_MANAGED_OAUTH_SECRET_KEY',
|
|
17
|
+
'GDOCS_TEST_CONNECTION_ID',
|
|
18
|
+
)
|
|
19
|
+
? describe
|
|
20
|
+
: describe.skip
|
|
21
|
+
|
|
22
|
+
suite('google-docs read handlers (live)', () => {
|
|
23
|
+
let buildReadHandler: (name: string) => ((input: any) => Promise<any>)
|
|
24
|
+
|
|
25
|
+
beforeAll(async () => {
|
|
26
|
+
const { COMMANDABLE_MANAGED_OAUTH_BASE_URL, COMMANDABLE_MANAGED_OAUTH_SECRET_KEY, GDOCS_TEST_CONNECTION_ID } = env
|
|
27
|
+
|
|
28
|
+
const proxy = new IntegrationProxy({
|
|
29
|
+
managedOAuthBaseUrl: COMMANDABLE_MANAGED_OAUTH_BASE_URL,
|
|
30
|
+
managedOAuthSecretKey: COMMANDABLE_MANAGED_OAUTH_SECRET_KEY,
|
|
31
|
+
})
|
|
32
|
+
const integrationNode = { id: 'node-gdocs', type: 'google-docs', label: 'Google Docs', connectionId: GDOCS_TEST_CONNECTION_ID } as any
|
|
33
|
+
|
|
34
|
+
const tools = loadIntegrationTools('google-docs')
|
|
35
|
+
expect(tools).toBeTruthy()
|
|
36
|
+
|
|
37
|
+
buildReadHandler = (name: string) => {
|
|
38
|
+
const tool = tools!.read.find(t => t.name === name)
|
|
39
|
+
expect(tool, `read tool ${name} exists`).toBeTruthy()
|
|
40
|
+
const integration = { fetch: (path: string, init?: RequestInit) => proxy.call(integrationNode, path, init) }
|
|
41
|
+
const build = new Function('integration', `return (${tool!.handlerCode});`)
|
|
42
|
+
return build(integration) as (input: any) => Promise<any>
|
|
43
|
+
}
|
|
44
|
+
}, 60000)
|
|
45
|
+
|
|
46
|
+
it('get_document returns metadata/content', async () => {
|
|
47
|
+
const documentId = env.GDOCS_TEST_DOCUMENT_ID
|
|
48
|
+
if (!documentId)
|
|
49
|
+
return expect(true).toBe(true)
|
|
50
|
+
const handler = buildReadHandler('get_document')
|
|
51
|
+
const result = await handler({ documentId })
|
|
52
|
+
expect(result?.documentId || result?.body?.content || result?.title).toBeTruthy()
|
|
53
|
+
}, 30000)
|
|
54
|
+
|
|
55
|
+
it('get_document_text returns plain text', async () => {
|
|
56
|
+
const documentId = env.GDOCS_TEST_DOCUMENT_ID
|
|
57
|
+
if (!documentId)
|
|
58
|
+
return expect(true).toBe(true)
|
|
59
|
+
const handler = buildReadHandler('get_document_text')
|
|
60
|
+
const result = await handler({ documentId })
|
|
61
|
+
expect(typeof result?.text === 'string').toBe(true)
|
|
62
|
+
}, 30000)
|
|
63
|
+
|
|
64
|
+
it('get_document_structured returns body JSON', async () => {
|
|
65
|
+
const documentId = env.GDOCS_TEST_DOCUMENT_ID
|
|
66
|
+
if (!documentId)
|
|
67
|
+
return expect(true).toBe(true)
|
|
68
|
+
const handler = buildReadHandler('get_document_structured')
|
|
69
|
+
const result = await handler({ documentId })
|
|
70
|
+
expect(result?.body || result?.documentId).toBeTruthy()
|
|
71
|
+
}, 30000)
|
|
72
|
+
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
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
|
+
}
|
|
10
|
+
|
|
11
|
+
describe('google-docs static usage parity', () => {
|
|
12
|
+
it('every manifest tool is referenced in tests via build*(name)', () => {
|
|
13
|
+
const manifest = loadIntegrationManifest('google-docs')!
|
|
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
|
+
|
|
32
|
+
expect(missing, `Missing handler usages in tests: ${missing.join(', ')}`).toEqual([])
|
|
33
|
+
})
|
|
34
|
+
})
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, it } from 'vitest'
|
|
2
|
+
import { IntegrationProxy } from '../../../src/integrations/proxy.js'
|
|
3
|
+
import { loadIntegrationTools } from '../../../src/integrations/dataLoader.js'
|
|
4
|
+
|
|
5
|
+
// LIVE Google Docs write tests using managed OAuth
|
|
6
|
+
// Required env vars:
|
|
7
|
+
// - COMMANDABLE_MANAGED_OAUTH_BASE_URL
|
|
8
|
+
// - COMMANDABLE_MANAGED_OAUTH_SECRET_KEY
|
|
9
|
+
// - GDOCS_TEST_CONNECTION_ID (managed OAuth connection for provider 'google-docs')
|
|
10
|
+
// - GDOCS_TEST_DOCUMENT_ID (target document ID with write access) OR GDOCS_ALLOW_CREATE to create
|
|
11
|
+
|
|
12
|
+
interface Ctx {
|
|
13
|
+
documentId?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const env = process.env as Record<string, string>
|
|
17
|
+
const hasEnv = (...keys: string[]) => keys.every(k => !!env[k] && env[k].trim().length > 0)
|
|
18
|
+
const suite = hasEnv(
|
|
19
|
+
'COMMANDABLE_MANAGED_OAUTH_BASE_URL',
|
|
20
|
+
'COMMANDABLE_MANAGED_OAUTH_SECRET_KEY',
|
|
21
|
+
'GDOCS_TEST_CONNECTION_ID',
|
|
22
|
+
)
|
|
23
|
+
? describe
|
|
24
|
+
: describe.skip
|
|
25
|
+
|
|
26
|
+
suite('google-docs write handlers (live)', () => {
|
|
27
|
+
const ctx: Ctx = {}
|
|
28
|
+
let buildWriteHandler: (name: string) => ((input: any) => Promise<any>)
|
|
29
|
+
let buildReadHandler: (name: string) => ((input: any) => Promise<any>)
|
|
30
|
+
|
|
31
|
+
beforeAll(async () => {
|
|
32
|
+
const { COMMANDABLE_MANAGED_OAUTH_BASE_URL, COMMANDABLE_MANAGED_OAUTH_SECRET_KEY, GDOCS_TEST_CONNECTION_ID, GDOCS_TEST_DOCUMENT_ID } = env
|
|
33
|
+
|
|
34
|
+
const proxy = new IntegrationProxy({
|
|
35
|
+
managedOAuthBaseUrl: COMMANDABLE_MANAGED_OAUTH_BASE_URL,
|
|
36
|
+
managedOAuthSecretKey: COMMANDABLE_MANAGED_OAUTH_SECRET_KEY,
|
|
37
|
+
})
|
|
38
|
+
const integrationNode = { id: 'node-gdocs', type: 'google-docs', label: 'Google Docs', connectionId: GDOCS_TEST_CONNECTION_ID } as any
|
|
39
|
+
|
|
40
|
+
const tools = loadIntegrationTools('google-docs')
|
|
41
|
+
expect(tools).toBeTruthy()
|
|
42
|
+
|
|
43
|
+
buildWriteHandler = (name: string) => {
|
|
44
|
+
const tool = tools!.write.find(t => t.name === name)
|
|
45
|
+
expect(tool, `write tool ${name} exists`).toBeTruthy()
|
|
46
|
+
const integration = { fetch: (path: string, init?: RequestInit) => proxy.call(integrationNode, path, init) }
|
|
47
|
+
const build = new Function('integration', `return (${tool!.handlerCode});`)
|
|
48
|
+
return build(integration) as (input: any) => Promise<any>
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
buildReadHandler = (name: string) => {
|
|
52
|
+
const tool = tools!.read.find(t => t.name === name)
|
|
53
|
+
expect(tool, `read tool ${name} exists`).toBeTruthy()
|
|
54
|
+
const integration = { fetch: (path: string, init?: RequestInit) => proxy.call(integrationNode, path, init) }
|
|
55
|
+
const build = new Function('integration', `return (${tool!.handlerCode});`)
|
|
56
|
+
return build(integration) as (input: any) => Promise<any>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
ctx.documentId = GDOCS_TEST_DOCUMENT_ID
|
|
60
|
+
}, 60000)
|
|
61
|
+
|
|
62
|
+
it('create_document creates a document when allowed', async () => {
|
|
63
|
+
if (!process.env.GDOCS_ALLOW_CREATE)
|
|
64
|
+
return expect(true).toBe(true)
|
|
65
|
+
const create_document = buildWriteHandler('create_document')
|
|
66
|
+
const res = await create_document({ title: `CmdTest Doc ${Date.now()}` })
|
|
67
|
+
expect(res?.documentId).toBeTruthy()
|
|
68
|
+
}, 60000)
|
|
69
|
+
|
|
70
|
+
it('batch_update can perform a trivial replaceAllText no-op', async () => {
|
|
71
|
+
const documentId = process.env.GDOCS_TEST_DOCUMENT_ID
|
|
72
|
+
if (!documentId)
|
|
73
|
+
return expect(true).toBe(true)
|
|
74
|
+
const batch_update = buildWriteHandler('batch_update')
|
|
75
|
+
const res = await batch_update({ documentId, requests: [
|
|
76
|
+
{ replaceAllText: { containsText: { text: '___unlikely___', matchCase: true }, replaceText: '___unlikely___' } },
|
|
77
|
+
] })
|
|
78
|
+
expect(Array.isArray(res?.replies) || res?.documentId).toBeTruthy()
|
|
79
|
+
}, 60000)
|
|
80
|
+
|
|
81
|
+
it('append_text appends content', async () => {
|
|
82
|
+
const documentId = process.env.GDOCS_TEST_DOCUMENT_ID
|
|
83
|
+
if (!documentId)
|
|
84
|
+
return expect(true).toBe(true)
|
|
85
|
+
const append_text = buildWriteHandler('append_text')
|
|
86
|
+
const marker = `CmdTest ${Date.now()}`
|
|
87
|
+
const res = await append_text({ documentId, text: marker })
|
|
88
|
+
expect(res?.documentId || Array.isArray(res?.replies)).toBeTruthy()
|
|
89
|
+
const get_text = buildReadHandler('get_document_text')
|
|
90
|
+
const after = await get_text({ documentId })
|
|
91
|
+
expect(String(after?.text || '')).toContain(marker)
|
|
92
|
+
}, 60000)
|
|
93
|
+
|
|
94
|
+
it('insert_text_after_first_match inserts text near target', async () => {
|
|
95
|
+
const documentId = process.env.GDOCS_TEST_DOCUMENT_ID
|
|
96
|
+
if (!documentId)
|
|
97
|
+
return expect(true).toBe(true)
|
|
98
|
+
const insert_text_after_first_match = buildWriteHandler('insert_text_after_first_match')
|
|
99
|
+
const get_text = buildReadHandler('get_document_text')
|
|
100
|
+
const anchor = `ANCHOR_${Date.now()}`
|
|
101
|
+
const appended = buildWriteHandler('append_text')
|
|
102
|
+
const before = await get_text({ documentId })
|
|
103
|
+
if (!String(before?.text || '').includes(anchor)) {
|
|
104
|
+
await appended({ documentId, text: `\n${anchor}\n` })
|
|
105
|
+
}
|
|
106
|
+
const insertSnippet = ` CmdTest ${Date.now()} `
|
|
107
|
+
const res = await insert_text_after_first_match({ documentId, findText: anchor, insertText: insertSnippet, position: 'after' })
|
|
108
|
+
expect(res?.applied === true || Array.isArray(res?.replies)).toBeTruthy()
|
|
109
|
+
const after = await get_text({ documentId })
|
|
110
|
+
const text = String(after?.text || '')
|
|
111
|
+
expect(text).toContain(anchor)
|
|
112
|
+
expect(text).toContain(insertSnippet)
|
|
113
|
+
}, 60000)
|
|
114
|
+
|
|
115
|
+
it('replace_all_text replaces occurrences', async () => {
|
|
116
|
+
const documentId = process.env.GDOCS_TEST_DOCUMENT_ID
|
|
117
|
+
if (!documentId)
|
|
118
|
+
return expect(true).toBe(true)
|
|
119
|
+
const replace_all_text = buildWriteHandler('replace_all_text')
|
|
120
|
+
const res = await replace_all_text({ documentId, findText: '___unlikely___', replaceText: '___unlikely___', matchCase: true })
|
|
121
|
+
expect(res?.documentId || Array.isArray(res?.replies)).toBeTruthy()
|
|
122
|
+
}, 60000)
|
|
123
|
+
|
|
124
|
+
it('style_first_match applies style to first match', async () => {
|
|
125
|
+
const documentId = process.env.GDOCS_TEST_DOCUMENT_ID
|
|
126
|
+
if (!documentId)
|
|
127
|
+
return expect(true).toBe(true)
|
|
128
|
+
const style_first_match = buildWriteHandler('style_first_match')
|
|
129
|
+
const get_struct = buildReadHandler('get_document_structured')
|
|
130
|
+
const anchor = `ANCHOR_${Date.now()}`
|
|
131
|
+
const appended = buildWriteHandler('append_text')
|
|
132
|
+
const before = await get_struct({ documentId })
|
|
133
|
+
const hasAnchorBefore = JSON.stringify(before?.body || {}).includes(anchor)
|
|
134
|
+
if (!hasAnchorBefore)
|
|
135
|
+
await appended({ documentId, text: `\n${anchor}\n` })
|
|
136
|
+
const res = await style_first_match({ documentId, findText: anchor, textStyle: { bold: true } })
|
|
137
|
+
expect(res?.applied === true || Array.isArray(res?.replies)).toBeTruthy()
|
|
138
|
+
}, 60000)
|
|
139
|
+
|
|
140
|
+
it('insert_table_after_first_match inserts a table near target', async () => {
|
|
141
|
+
const documentId = process.env.GDOCS_TEST_DOCUMENT_ID
|
|
142
|
+
if (!documentId)
|
|
143
|
+
return expect(true).toBe(true)
|
|
144
|
+
const insert_table_after_first_match = buildWriteHandler('insert_table_after_first_match')
|
|
145
|
+
const get_struct = buildReadHandler('get_document_structured')
|
|
146
|
+
const anchor = `ANCHOR_${Date.now()}`
|
|
147
|
+
const appended = buildWriteHandler('append_text')
|
|
148
|
+
const before = await get_struct({ documentId })
|
|
149
|
+
const hasAnchorBefore = JSON.stringify(before?.body || {}).includes(anchor)
|
|
150
|
+
if (!hasAnchorBefore)
|
|
151
|
+
await appended({ documentId, text: `\n${anchor}\n` })
|
|
152
|
+
const res = await insert_table_after_first_match({ documentId, findText: anchor, rows: 1, columns: 1 })
|
|
153
|
+
expect(res?.applied === true || Array.isArray(res?.replies)).toBeTruthy()
|
|
154
|
+
const after = await get_struct({ documentId })
|
|
155
|
+
const hasTable = (after?.body?.content || []).some((el: any) => Boolean(el.table))
|
|
156
|
+
expect(hasTable).toBe(true)
|
|
157
|
+
}, 60000)
|
|
158
|
+
|
|
159
|
+
it('insert_page_break_after_first_match inserts a break near target', async () => {
|
|
160
|
+
const documentId = process.env.GDOCS_TEST_DOCUMENT_ID
|
|
161
|
+
if (!documentId)
|
|
162
|
+
return expect(true).toBe(true)
|
|
163
|
+
const insert_page_break_after_first_match = buildWriteHandler('insert_page_break_after_first_match')
|
|
164
|
+
const get_struct = buildReadHandler('get_document_structured')
|
|
165
|
+
const anchor = `ANCHOR_${Date.now()}`
|
|
166
|
+
const appended = buildWriteHandler('append_text')
|
|
167
|
+
const before = await get_struct({ documentId })
|
|
168
|
+
const hasAnchorBefore = JSON.stringify(before?.body || {}).includes(anchor)
|
|
169
|
+
if (!hasAnchorBefore)
|
|
170
|
+
await appended({ documentId, text: `\n${anchor}\n` })
|
|
171
|
+
const res = await insert_page_break_after_first_match({ documentId, findText: anchor })
|
|
172
|
+
expect(res?.applied === true || Array.isArray(res?.replies)).toBeTruthy()
|
|
173
|
+
const after = await get_struct({ documentId })
|
|
174
|
+
const hasBreak = (after?.body?.content || []).some((el: any) => Boolean(el.sectionBreak))
|
|
175
|
+
expect(hasBreak).toBe(true)
|
|
176
|
+
}, 60000)
|
|
177
|
+
|
|
178
|
+
it('insert_inline_image_after_first_match inserts an image when allowed', async () => {
|
|
179
|
+
if (!process.env.GDOCS_TEST_DOCUMENT_ID || !process.env.GDOCS_TEST_IMAGE_URI)
|
|
180
|
+
return expect(true).toBe(true)
|
|
181
|
+
const documentId = process.env.GDOCS_TEST_DOCUMENT_ID
|
|
182
|
+
const insert_inline_image_after_first_match = buildWriteHandler('insert_inline_image_after_first_match')
|
|
183
|
+
const anchor = `ANCHOR_${Date.now()}`
|
|
184
|
+
const appended = buildWriteHandler('append_text')
|
|
185
|
+
const get_text = buildReadHandler('get_document_text')
|
|
186
|
+
const before = await get_text({ documentId })
|
|
187
|
+
if (!String(before?.text || '').includes(anchor))
|
|
188
|
+
await appended({ documentId, text: `\n${anchor}\n` })
|
|
189
|
+
const res = await insert_inline_image_after_first_match({ documentId, findText: anchor, uri: process.env.GDOCS_TEST_IMAGE_URI!, altText: 'CmdTest' })
|
|
190
|
+
expect(res?.applied === true || Array.isArray(res?.replies)).toBeTruthy()
|
|
191
|
+
}, 60000)
|
|
192
|
+
|
|
193
|
+
it('delete_first_match deletes a small span (no-op ok)', async () => {
|
|
194
|
+
const documentId = process.env.GDOCS_TEST_DOCUMENT_ID
|
|
195
|
+
if (!documentId)
|
|
196
|
+
return expect(true).toBe(true)
|
|
197
|
+
const delete_first_match = buildWriteHandler('delete_first_match')
|
|
198
|
+
const get_text = buildReadHandler('get_document_text')
|
|
199
|
+
const anchor = `ANCHOR_${Date.now()}`
|
|
200
|
+
const appended = buildWriteHandler('append_text')
|
|
201
|
+
const before = await get_text({ documentId })
|
|
202
|
+
if (!String(before?.text || '').includes(anchor))
|
|
203
|
+
await appended({ documentId, text: `\n${anchor}\n` })
|
|
204
|
+
const res = await delete_first_match({ documentId, findText: anchor })
|
|
205
|
+
expect(res?.applied === true || Array.isArray(res?.replies)).toBeTruthy()
|
|
206
|
+
const after = await get_text({ documentId })
|
|
207
|
+
expect(String(after?.text || '')).not.toContain(anchor)
|
|
208
|
+
}, 60000)
|
|
209
|
+
|
|
210
|
+
it('update_paragraph_style_for_first_match updates paragraph style near target', async () => {
|
|
211
|
+
const documentId = process.env.GDOCS_TEST_DOCUMENT_ID
|
|
212
|
+
if (!documentId)
|
|
213
|
+
return expect(true).toBe(true)
|
|
214
|
+
const update_paragraph_style_for_first_match = buildWriteHandler('update_paragraph_style_for_first_match')
|
|
215
|
+
const get_struct = buildReadHandler('get_document_structured')
|
|
216
|
+
const anchor = `ANCHOR_${Date.now()}`
|
|
217
|
+
const appended = buildWriteHandler('append_text')
|
|
218
|
+
const before = await get_struct({ documentId })
|
|
219
|
+
const hasAnchorBefore = JSON.stringify(before?.body || {}).includes(anchor)
|
|
220
|
+
if (!hasAnchorBefore)
|
|
221
|
+
await appended({ documentId, text: `\n${anchor}\n` })
|
|
222
|
+
const res = await update_paragraph_style_for_first_match({ documentId, findText: anchor, paragraphStyle: { alignment: 'CENTER' } })
|
|
223
|
+
expect(res?.applied === true || Array.isArray(res?.replies)).toBeTruthy()
|
|
224
|
+
const after = await get_struct({ documentId })
|
|
225
|
+
// find the paragraph containing anchor and verify alignment
|
|
226
|
+
let foundAligned = false
|
|
227
|
+
for (const el of (after?.body?.content || [])) {
|
|
228
|
+
if (!el.paragraph)
|
|
229
|
+
continue
|
|
230
|
+
const p = el.paragraph
|
|
231
|
+
const text = (p.elements || []).map((e: any) => e?.textRun?.content || '').join('')
|
|
232
|
+
if (text.includes(anchor)) {
|
|
233
|
+
if (p.paragraphStyle?.alignment === 'CENTER')
|
|
234
|
+
foundAligned = true
|
|
235
|
+
break
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
expect(foundAligned).toBe(true)
|
|
239
|
+
}, 60000)
|
|
240
|
+
|
|
241
|
+
it('update_document_style updates doc style with no-op', async () => {
|
|
242
|
+
const documentId = process.env.GDOCS_TEST_DOCUMENT_ID
|
|
243
|
+
if (!documentId)
|
|
244
|
+
return expect(true).toBe(true)
|
|
245
|
+
const update_document_style = buildWriteHandler('update_document_style')
|
|
246
|
+
const res = await update_document_style({ documentId, documentStyle: { useFirstPageHeaderFooter: false } })
|
|
247
|
+
expect(res?.documentId || Array.isArray(res?.replies)).toBeTruthy()
|
|
248
|
+
}, 60000)
|
|
249
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": {
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"token": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"title": "OAuth Access Token (optional)",
|
|
8
|
+
"description": "Google OAuth access token to use as a Bearer token (typically short-lived)."
|
|
9
|
+
},
|
|
10
|
+
"serviceAccountJson": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"title": "Service Account JSON (recommended)",
|
|
13
|
+
"description": "Full service account key JSON (contents of the downloaded JSON file)."
|
|
14
|
+
},
|
|
15
|
+
"subject": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"title": "Subject / impersonated user (optional)",
|
|
18
|
+
"description": "Optional user email to impersonate when using Google Workspace domain-wide delegation."
|
|
19
|
+
},
|
|
20
|
+
"scopes": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"title": "OAuth scopes (optional)",
|
|
23
|
+
"description": "Optional override for OAuth scopes.",
|
|
24
|
+
"items": { "type": "string" }
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"required": [],
|
|
28
|
+
"additionalProperties": false
|
|
29
|
+
},
|
|
30
|
+
"injection": {
|
|
31
|
+
"headers": {
|
|
32
|
+
"Authorization": "Bearer {{token}}"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Recommended: use a Google service account.
|
|
2
|
+
|
|
3
|
+
- Create a service account in Google Cloud
|
|
4
|
+
- Download a JSON key
|
|
5
|
+
- Paste the JSON into `serviceAccountJson` (or use `env:GOOGLE_SERVICE_ACCOUNT_JSON`)
|
|
6
|
+
- Share your test document with the service account `client_email`
|
|
7
|
+
|
|
8
|
+
You can also paste a short-lived OAuth access token into `token`, but it will expire.
|
|
9
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const { documentId, text } = input
|
|
3
|
+
// Get doc to find end index
|
|
4
|
+
const metaRes = await integration.fetch(`/documents/${encodeURIComponent(documentId)}`)
|
|
5
|
+
const meta = await metaRes.json()
|
|
6
|
+
const endIndex = meta?.body?.content?.[meta.body.content.length - 1]?.endIndex || 1
|
|
7
|
+
const res = await integration.fetch(`/documents/${encodeURIComponent(documentId)}:batchUpdate`, {
|
|
8
|
+
method: 'POST',
|
|
9
|
+
body: { requests: [{ insertText: { text, location: { index: endIndex - 1 } } }] },
|
|
10
|
+
})
|
|
11
|
+
return await res.json()
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const { documentId, requests, writeControl, includeTabStops } = input
|
|
3
|
+
const params = new URLSearchParams()
|
|
4
|
+
if (includeTabStops !== undefined)
|
|
5
|
+
params.set('includeTabStops', String(includeTabStops))
|
|
6
|
+
const qs = params.toString()
|
|
7
|
+
const path = `/documents/${encodeURIComponent(documentId)}:batchUpdate${qs ? `?${qs}` : ''}`
|
|
8
|
+
const body = { requests }
|
|
9
|
+
if (writeControl)
|
|
10
|
+
body.writeControl = writeControl
|
|
11
|
+
const res = await integration.fetch(path, { method: 'POST', body })
|
|
12
|
+
return await res.json()
|
|
13
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const { documentId, findText } = input
|
|
3
|
+
const marker = `__CMD_MARK_${Date.now()}__`
|
|
4
|
+
const replaceRes = await integration.fetch(`/documents/${encodeURIComponent(documentId)}:batchUpdate`, {
|
|
5
|
+
method: 'POST',
|
|
6
|
+
body: { requests: [{ replaceAllText: { containsText: { text: findText, matchCase: false }, replaceText: marker } }] },
|
|
7
|
+
})
|
|
8
|
+
await replaceRes.json()
|
|
9
|
+
|
|
10
|
+
const getRes = await integration.fetch(`/documents/${encodeURIComponent(documentId)}`)
|
|
11
|
+
const doc = await getRes.json()
|
|
12
|
+
let startIndex = -1
|
|
13
|
+
let endIndex = -1
|
|
14
|
+
for (const el of (doc?.body?.content || [])) {
|
|
15
|
+
const p = el.paragraph
|
|
16
|
+
if (!p)
|
|
17
|
+
continue
|
|
18
|
+
for (const e of (p.elements || [])) {
|
|
19
|
+
const t = e?.textRun?.content
|
|
20
|
+
if (!t)
|
|
21
|
+
continue
|
|
22
|
+
const idx = t.indexOf(marker)
|
|
23
|
+
if (idx >= 0) {
|
|
24
|
+
const elStart = e.startIndex || 1
|
|
25
|
+
startIndex = elStart + idx
|
|
26
|
+
endIndex = startIndex + marker.length
|
|
27
|
+
break
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (startIndex >= 0)
|
|
31
|
+
break
|
|
32
|
+
}
|
|
33
|
+
if (startIndex < 0) {
|
|
34
|
+
const confirm = await integration.fetch(`/documents/${encodeURIComponent(documentId)}`)
|
|
35
|
+
const got = await confirm.json()
|
|
36
|
+
return { documentId: got?.documentId || documentId, applied: false, replies: [] }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const requests = []
|
|
40
|
+
requests.push({ deleteContentRange: { range: { startIndex, endIndex } } })
|
|
41
|
+
// also clean any remaining markers
|
|
42
|
+
requests.push({ replaceAllText: { containsText: { text: marker, matchCase: true }, replaceText: '' } })
|
|
43
|
+
const res = await integration.fetch(`/documents/${encodeURIComponent(documentId)}:batchUpdate`, { method: 'POST', body: { requests } })
|
|
44
|
+
const out = await res.json()
|
|
45
|
+
if (out?.documentId || Array.isArray(out?.replies))
|
|
46
|
+
return { ...out, applied: true }
|
|
47
|
+
const confirm = await integration.fetch(`/documents/${encodeURIComponent(documentId)}`)
|
|
48
|
+
const got = await confirm.json()
|
|
49
|
+
return { documentId: got?.documentId || documentId, applied: true, replies: Array.isArray(out?.replies) ? out.replies : [] }
|
|
50
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const { documentId, suggestionsViewMode, includeTabsAndSpaces } = input
|
|
3
|
+
const params = new URLSearchParams()
|
|
4
|
+
if (suggestionsViewMode)
|
|
5
|
+
params.set('suggestionsViewMode', String(suggestionsViewMode))
|
|
6
|
+
if (includeTabsAndSpaces !== undefined)
|
|
7
|
+
params.set('includeTabsAndSpaces', String(includeTabsAndSpaces))
|
|
8
|
+
const qs = params.toString()
|
|
9
|
+
const path = `/documents/${encodeURIComponent(documentId)}${qs ? `?${qs}` : ''}`
|
|
10
|
+
const res = await integration.fetch(path)
|
|
11
|
+
return await res.json()
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const { documentId } = input
|
|
3
|
+
const res = await integration.fetch(`/documents/${encodeURIComponent(documentId)}`)
|
|
4
|
+
const doc = await res.json()
|
|
5
|
+
const content = doc?.body?.content || []
|
|
6
|
+
let text = ''
|
|
7
|
+
for (const el of content) {
|
|
8
|
+
const paragraphs = el.paragraph ? [el.paragraph] : []
|
|
9
|
+
for (const p of paragraphs) {
|
|
10
|
+
for (const e of p.elements || []) {
|
|
11
|
+
text += e.textRun?.content || ''
|
|
12
|
+
}
|
|
13
|
+
text += '\n'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return { documentId: doc?.documentId || documentId, text }
|
|
17
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const { documentId, findText, uri, altText, position } = input
|
|
3
|
+
const marker = `__CMD_MARK_${Date.now()}__`
|
|
4
|
+
const replaceRes = await integration.fetch(`/documents/${encodeURIComponent(documentId)}:batchUpdate`, {
|
|
5
|
+
method: 'POST',
|
|
6
|
+
body: { requests: [{ replaceAllText: { containsText: { text: findText, matchCase: false }, replaceText: marker } }] },
|
|
7
|
+
})
|
|
8
|
+
await replaceRes.json()
|
|
9
|
+
|
|
10
|
+
const getRes = await integration.fetch(`/documents/${encodeURIComponent(documentId)}`)
|
|
11
|
+
const doc = await getRes.json()
|
|
12
|
+
let baseIndex = -1
|
|
13
|
+
for (const el of (doc?.body?.content || [])) {
|
|
14
|
+
const p = el.paragraph
|
|
15
|
+
if (!p)
|
|
16
|
+
continue
|
|
17
|
+
for (const e of (p.elements || [])) {
|
|
18
|
+
const t = e?.textRun?.content
|
|
19
|
+
if (!t)
|
|
20
|
+
continue
|
|
21
|
+
const idx = t.indexOf(marker)
|
|
22
|
+
if (idx >= 0) {
|
|
23
|
+
const elStart = e.startIndex || 1
|
|
24
|
+
const startIndex = elStart + idx
|
|
25
|
+
const endIndex = startIndex + marker.length
|
|
26
|
+
baseIndex = position === 'before' ? startIndex : endIndex
|
|
27
|
+
break
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (baseIndex >= 0)
|
|
31
|
+
break
|
|
32
|
+
}
|
|
33
|
+
if (baseIndex < 0)
|
|
34
|
+
return { ok: true }
|
|
35
|
+
|
|
36
|
+
const requests = []
|
|
37
|
+
requests.push({ insertInlineImage: { location: { index: baseIndex }, uri, altTextTitle: altText } })
|
|
38
|
+
requests.push({ replaceAllText: { containsText: { text: marker, matchCase: true }, replaceText: findText } })
|
|
39
|
+
const res = await integration.fetch(`/documents/${encodeURIComponent(documentId)}:batchUpdate`, { method: 'POST', body: { requests } })
|
|
40
|
+
return await res.json()
|
|
41
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
async (input) => {
|
|
2
|
+
const { documentId, findText, position } = input
|
|
3
|
+
const marker = `__CMD_MARK_${Date.now()}__`
|
|
4
|
+
const replaceRes = await integration.fetch(`/documents/${encodeURIComponent(documentId)}:batchUpdate`, {
|
|
5
|
+
method: 'POST',
|
|
6
|
+
body: { requests: [{ replaceAllText: { containsText: { text: findText, matchCase: false }, replaceText: marker } }] },
|
|
7
|
+
})
|
|
8
|
+
await replaceRes.json()
|
|
9
|
+
|
|
10
|
+
const getRes = await integration.fetch(`/documents/${encodeURIComponent(documentId)}`)
|
|
11
|
+
const doc = await getRes.json()
|
|
12
|
+
let baseIndex = -1
|
|
13
|
+
for (const el of (doc?.body?.content || [])) {
|
|
14
|
+
const p = el.paragraph
|
|
15
|
+
if (!p)
|
|
16
|
+
continue
|
|
17
|
+
for (const e of (p.elements || [])) {
|
|
18
|
+
const t = e?.textRun?.content
|
|
19
|
+
if (!t)
|
|
20
|
+
continue
|
|
21
|
+
const idx = t.indexOf(marker)
|
|
22
|
+
if (idx >= 0) {
|
|
23
|
+
const elStart = e.startIndex || 1
|
|
24
|
+
const startIndex = elStart + idx
|
|
25
|
+
const endIndex = startIndex + marker.length
|
|
26
|
+
baseIndex = position === 'before' ? startIndex : endIndex
|
|
27
|
+
break
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (baseIndex >= 0)
|
|
31
|
+
break
|
|
32
|
+
}
|
|
33
|
+
if (baseIndex < 0) {
|
|
34
|
+
const confirm = await integration.fetch(`/documents/${encodeURIComponent(documentId)}`)
|
|
35
|
+
const got = await confirm.json()
|
|
36
|
+
return { documentId: got?.documentId || documentId, applied: false, replies: [] }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const requests = []
|
|
40
|
+
requests.push({ insertPageBreak: { location: { index: baseIndex } } })
|
|
41
|
+
requests.push({ replaceAllText: { containsText: { text: marker, matchCase: true }, replaceText: findText } })
|
|
42
|
+
const res = await integration.fetch(`/documents/${encodeURIComponent(documentId)}:batchUpdate`, { method: 'POST', body: { requests } })
|
|
43
|
+
const out = await res.json()
|
|
44
|
+
if (out?.documentId || Array.isArray(out?.replies))
|
|
45
|
+
return { ...out, applied: true }
|
|
46
|
+
const confirm = await integration.fetch(`/documents/${encodeURIComponent(documentId)}`)
|
|
47
|
+
const got = await confirm.json()
|
|
48
|
+
return { documentId: got?.documentId || documentId, applied: true, replies: Array.isArray(out?.replies) ? out.replies : [] }
|
|
49
|
+
}
|