Compare commits

...

240 Commits
d-x ... AUT-388

Author SHA1 Message Date
Rıdvan Akca
30fadee94d feat(twitch): add new live streams trigger 2023-11-06 14:10:53 +03:00
Rıdvan Akca
e97c7e2e68 feat(twitch): add twitch integration 2023-11-02 13:49:01 +03:00
Ömer Faruk Aydın
5fb48ed54b Merge pull request #1402 from automatisch/tests/get-flow
test: Implement graphQL getFlow query tests
2023-10-31 16:12:26 +01:00
Faruk AYDIN
903e9e6093 test: Implement graphQL getFlow query tests 2023-10-31 15:57:44 +01:00
Faruk AYDIN
d30e491817 test: Delete formattedData from connection factory before persisting 2023-10-31 15:57:22 +01:00
Faruk AYDIN
aa727e3260 test: Adjust step factory to assign correct appKey 2023-10-31 15:56:49 +01:00
Ömer Faruk Aydın
1cad3a7149 Merge pull request #1398 from automatisch/tests/disable-typescript
test: Disable ts check for test files
2023-10-31 12:08:30 +01:00
Ali BARIN
3b7f6740bb chore: exclude test files from eslint 2023-10-31 11:35:53 +01:00
Ali BARIN
2febc5efad fix(mutations/delete-flow): cover incomplete trigger 2023-10-30 15:19:31 +01:00
Faruk AYDIN
903616bef6 test: Disable ts check for test files 2023-10-30 14:25:22 +01:00
Rıdvan Akca
c944193fb4 feat(trello): add create card action 2023-10-30 11:48:19 +01:00
Ömer Faruk Aydın
4f2155ea63 Merge pull request #1394 from automatisch/tests/coverage
test: Show coverage for graphQL queries folder
2023-10-30 10:23:42 +01:00
Faruk AYDIN
4bda1edda7 test: Show coverage for graphQL queries folder 2023-10-29 13:13:01 +01:00
QAComet
1a55cc8604 test: update snackbar with variant and data-test attributes 2023-10-27 19:48:40 +02:00
Ali BARIN
bf7ab475ee feat: unregister webhook upon deleting flow 2023-10-27 19:19:13 +02:00
Ömer Faruk Aydın
2f39efb935 Merge pull request #1381 from automatisch/add-filters-in-get-executions-query
feat(queries/get-executions): add filter support
2023-10-26 17:56:10 +02:00
Ali BARIN
9f8eb985e4 refactor: assert entry dates as string 2023-10-26 15:43:11 +00:00
Ali BARIN
3549fef71c feat(ExecutionRow): use createdAt instead of updatedAt 2023-10-26 15:43:10 +00:00
Ali BARIN
2cfa64c2a3 test(queries/get-executions): use createdAt in filter test cases 2023-10-26 15:43:08 +00:00
Ali BARIN
7245a0a599 refactor(queries/get-executions): use createdAt in filters 2023-10-26 15:29:28 +00:00
Ali BARIN
0633da3244 fix(mutations/duplicate-flow): correct webhook path 2023-10-26 17:21:28 +02:00
Faruk AYDIN
96341976f5 test: Add graphQL query test for getExecutions 2023-10-26 17:19:37 +02:00
Faruk AYDIN
9abfaec4d5 test: Adjust permission factory to pass all values 2023-10-26 17:17:12 +02:00
Faruk AYDIN
945c52dd6b test: Add createdAt and updatedAt defaultst to execution and flow 2023-10-26 17:16:42 +02:00
Faruk AYDIN
6567d24760 test: Adjust app key of step depending on type 2023-10-26 17:15:50 +02:00
Faruk AYDIN
ffaf9b6e0c chore: Add date types to IFlow and IExecution 2023-10-26 17:15:10 +02:00
QAComet
463e6908b1 test: write tests for user management (#1316)
* chore: add data-test attributes

* test: add github connection test, add applications modal

* test: write tests for user management
2023-10-26 15:12:37 +02:00
Ali BARIN
e185ceb385 fix(queries/get-executions): recover flow and steps relations 2023-10-26 13:52:20 +02:00
Ali BARIN
1b21bbe5b7 feat(queries/get-executions): accept timestamp instead of ISO datetime 2023-10-26 13:52:20 +02:00
Ali BARIN
14b7053ed8 feat: add updated_at index in executions 2023-10-26 13:52:20 +02:00
Ali BARIN
2760526def feat: add flow_id index in executions 2023-10-26 13:52:20 +02:00
Ali BARIN
d851db22d0 feat(queries/get-executions): add updatedAt filter support 2023-10-26 13:52:20 +02:00
Ali BARIN
2fa360e400 feat(queries/get-executions): add status filter support 2023-10-26 13:52:20 +02:00
Ali BARIN
e4eb146169 feat(queries/get-executions): add flowId filter support 2023-10-26 13:52:20 +02:00
Moaaz Elsayed
86611453b5 feat(zendesk): add zendesk integration (#1385)
* feat(zendesk): add zendesk integration

* Add Zendesk connection documentation

* docs(zendesk/connection): add missing steps

* feat(zendesk): add more auth scopes for planned triggers/actions

* fix(zendesk): fix instanceUrl

---------

Co-authored-by: Ali BARIN <ali.barin53@gmail.com>
2023-10-26 12:47:13 +02:00
dependabot[bot]
65f9d1b6b9 chore(deps): bump crypto-js from 4.1.1 to 4.2.0
Bumps [crypto-js](https://github.com/brix/crypto-js) from 4.1.1 to 4.2.0.
- [Commits](https://github.com/brix/crypto-js/compare/4.1.1...4.2.0)

---
updated-dependencies:
- dependency-name: crypto-js
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-26 10:00:04 +02:00
QAComet
2fceaf2cf4 test: fix flakiness in GH connection test case (#1383) 2023-10-25 11:04:04 +02:00
Ömer Faruk Aydın
d82b50fcdb Merge pull request #1382 from automatisch/fix/delete-current-user
fix: Guard lowercase email for delete user operation
2023-10-25 01:17:52 +02:00
Faruk AYDIN
ab6e49bf4f fix: Guard lowercase email for delete user operation 2023-10-25 01:00:43 +02:00
Rıdvan Akca
ee90422f56 feat(trello): add trello integration (#1380) 2023-10-24 15:13:08 +02:00
Ömer Faruk Aydın
627184f124 Merge pull request #1379 from automatisch/factories/execution-step
test: Implement execution step factory
2023-10-24 13:45:08 +02:00
Rıdvan Akca
fa02edfefc feat(pushover): add send a pushover notification action (#1373) 2023-10-24 13:40:24 +02:00
Faruk AYDIN
61afebc827 test: Implement execution step factory 2023-10-24 12:35:29 +02:00
Faruk AYDIN
a4c22799e7 fix: Correct executions table name for execution factory 2023-10-24 12:35:29 +02:00
Faruk AYDIN
870a110a75 fix: Fetch lastStep directly on the step factory 2023-10-24 12:35:29 +02:00
kattoczko
8c859f9408 feat: introduce admin app configs page (#1335) 2023-10-24 12:19:37 +02:00
Ali BARIN
0a36101da1 feat: close snackbars upon click (#1378) 2023-10-24 12:03:30 +02:00
Ömer Faruk Aydın
c1bf063b12 Merge pull request #1377 from automatisch/factories/execution
test: Implement factory for the execution model
2023-10-23 23:06:45 +02:00
Ömer Faruk Aydın
0da56a800d Merge pull request #1376 from automatisch/factories/step
test: Add factory file for the step model
2023-10-23 23:00:26 +02:00
Faruk AYDIN
4d2172d153 test: Implement factory for the execution model 2023-10-23 22:52:24 +02:00
Faruk AYDIN
d0fab0e1f1 test: Add factory file for the step model 2023-10-23 22:47:19 +02:00
QAComet
4cedbdbc60 test: add tests for git connection (#1289)
* chore: add data-test attributes

* test: add github connection test, add applications modal

* chore: embed test GITHUB_CLIENT_* environment values

---------

Co-authored-by: Ali BARIN <ali.barin53@gmail.com>
2023-10-23 18:48:23 +02:00
Ömer Faruk Aydın
a82d34cbce Merge pull request #1375 from automatisch/factories/flow
test: Add factory file for the flow model
2023-10-23 18:43:14 +02:00
Faruk AYDIN
16d0c243c7 test: Add factory file for the flow model 2023-10-23 18:26:43 +02:00
Ömer Faruk Aydın
855901bd9e Merge pull request #1372 from automatisch/refactor/tests
refactor: Use named exports for factories
2023-10-23 11:09:05 +02:00
Faruk AYDIN
6be8a581d2 refactor: Use named exports for factories 2023-10-23 10:36:44 +02:00
Faruk AYDIN
655deb12c8 refactor: Rename fixtures as factories to differentiate dynamic data 2023-10-23 10:30:27 +02:00
Ömer Faruk Aydın
31b1b9457b Merge pull request #1364 from automatisch/AUT-348
feat(pushover): add pushover integration
2023-10-22 23:13:01 +02:00
Faruk AYDIN
c4394228f2 fix(placetel): Authenticate in case valid user without device 2023-10-22 23:03:46 +02:00
Rıdvan Akca
98e6dbe141 feat(pushover): add pushover integration 2023-10-22 23:03:46 +02:00
Ömer Faruk Aydın
4fac1ef7c4 Merge pull request #1355 from automatisch/AUT-347
feat(youtube): add new video by search trigger
2023-10-21 10:52:31 +02:00
Rıdvan Akca
034bc6a79e refactor(pipedrive): make filterProvidedFields func reusable 2023-10-20 19:06:34 +02:00
Rıdvan Akca
94e64676af refactor(pipedrive): set baseUrl in a seperate file 2023-10-20 18:36:50 +02:00
Rıdvan Akca
3a638220af feat(pipedrive): add create person action 2023-10-20 17:39:24 +02:00
Rıdvan Akca
0772308bf5 feat(pipedrive): add create organization action 2023-10-20 17:12:51 +02:00
Rıdvan Akca
9f5ea80731 feat(youtube): add new video by search trigger 2023-10-20 18:02:44 +03:00
Ömer Faruk Aydın
fb1f520096 Merge pull request #1367 from automatisch/test/connection-fixture
test: Implement connection fixture
2023-10-20 15:53:04 +02:00
Ömer Faruk Aydın
dd36609443 Merge pull request #1353 from automatisch/AUT-344
feat(youtube): add new video in channel trigger
2023-10-20 15:43:06 +02:00
Rıdvan Akca
4eace3fb7e feat(youtube): add new video in channel trigger 2023-10-20 16:35:13 +03:00
Faruk AYDIN
f13e93e2ce test: Implement connection fixture 2023-10-19 23:04:02 +02:00
Ömer Faruk Aydın
da4f8ab529 Merge pull request #1351 from automatisch/AUT-343
feat(youtube): add youtube integration
2023-10-19 15:05:58 +02:00
Ömer Faruk Aydın
b6ff4ec992 Merge pull request #1363 from automatisch/test/get-trial-status
test: Implement tests for get trial status graphQL query
2023-10-19 14:29:59 +02:00
Ömer Faruk Aydın
25f6cac69a Merge pull request #1362 from automatisch/test/get-automatisch-info
test: Add getAutomatischInfo graphQL query tests
2023-10-19 14:23:51 +02:00
Faruk AYDIN
172a8934e3 test: Add restoreAllMocks to global afterEach for spy and replaceProperty 2023-10-19 14:22:00 +02:00
Ali BARIN
aead014bcf refactor: remove additional mock implentation in tests 2023-10-19 13:43:49 +02:00
Faruk AYDIN
59770c80db test: Implement tests for get trial status graphQL query 2023-10-19 01:06:41 +02:00
Faruk AYDIN
6d6b77148d refactor: User fixture to pass additonal params 2023-10-19 01:06:01 +02:00
Faruk AYDIN
c1e8f5765f chore: Use cloud enabled version for test db 2023-10-19 01:05:23 +02:00
Faruk AYDIN
76e442940b test: Add getAutomatischInfo graphQL query tests 2023-10-18 15:47:09 +02:00
Faruk AYDIN
b2205097da test: Implement the structure of mocking appConfig options 2023-10-18 15:47:09 +02:00
Faruk AYDIN
14886d42e8 test: Clear all jest mocks with after each global hook 2023-10-18 15:47:09 +02:00
Ömer Faruk Aydın
2f35403078 Merge pull request #1359 from automatisch/AUT-350
fix(pipedrive): check if there is no data in dynamic-data
2023-10-18 11:42:12 +02:00
Rıdvan Akca
961d55a1c6 fix(pipedrive): check if there is no data in dynamic-data 2023-10-18 12:20:25 +03:00
Ömer Faruk Aydın
0fca0ef734 Merge pull request #1340 from automatisch/AUT-329
feat(invoice-ninja): add create product action
2023-10-18 00:27:05 +02:00
Ömer Faruk Aydın
cc3acd81bc Merge pull request #1357 from automatisch/pipedrive-create-activity-name-correction
fix(pipedrive/create-activity): correct its key
2023-10-18 00:18:30 +02:00
Ali BARIN
69a691c19e fix(pipedrive/create-activity): correct its key 2023-10-17 21:48:58 +02:00
Ömer Faruk Aydın
e0a4f5c9c9 Merge pull request #1356 from automatisch/formatter/format-phone-number
feat(formatter): implement format phone number transformer
2023-10-17 17:02:22 +02:00
Faruk AYDIN
cabf9b8fb8 feat(formatter): implement format phone number transformer 2023-10-17 16:45:56 +02:00
Rıdvan Akca
61e24da07d feat(invoice-ninja): add create product action 2023-10-17 15:27:25 +03:00
Ömer Faruk Aydın
648511dfad Merge pull request #1354 from automatisch/dependabot/npm_and_yarn/babel/traverse-7.23.2
chore(deps): bump @babel/traverse from 7.16.3 to 7.23.2
2023-10-17 12:52:18 +02:00
dependabot[bot]
dfc9efc31a chore(deps): bump @babel/traverse from 7.16.3 to 7.23.2
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.16.3 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-17 10:32:32 +00:00
Ömer Faruk Aydın
b58a22addc Merge pull request #1339 from automatisch/AUT-328
feat(invoice-ninja): add create payment action
2023-10-17 11:37:44 +02:00
Ömer Faruk Aydın
e07a9eeda2 Merge pull request #1338 from automatisch/AUT-327
feat(invoice-ninja): add create invoice action
2023-10-16 17:36:38 +02:00
Rıdvan Akca
f0de42fa63 feat(invoice-ninja): add create payment action 2023-10-16 18:30:10 +03:00
Rıdvan Akca
0121661ad0 feat(invoice-ninja): add create invoice action 2023-10-16 18:22:57 +03:00
Rıdvan Akca
fb6e46bd69 feat(youtube): add youtube integration 2023-10-16 16:27:17 +03:00
Ömer Faruk Aydın
9bd041799b Merge pull request #1350 from automatisch/test/get-config
test: Implement tests for getConfig graphQL query
2023-10-16 15:12:53 +02:00
Ömer Faruk Aydın
9e191c4ed9 Merge pull request #1349 from automatisch/test/get-roles
test: add tests for graphQL getRoles query
2023-10-16 15:12:43 +02:00
Ömer Faruk Aydın
794b4cf26a Merge pull request #1348 from automatisch/tests/get-role
test: Add getRole graphQL query test
2023-10-16 14:52:28 +02:00
Faruk AYDIN
2d8421943f test: Implement tests for getConfig graphQL query 2023-10-15 20:01:05 +02:00
Faruk AYDIN
48dc2312d9 refactor: Remove redundant context variable from getConfig 2023-10-15 20:00:51 +02:00
Faruk AYDIN
cdf7a1adc4 test: Implement config fixture 2023-10-15 20:00:07 +02:00
Faruk AYDIN
99ebd12081 chore: Add IConfig type 2023-10-15 19:59:26 +02:00
Faruk AYDIN
859337b5c1 test: add tests for graphQL getRoles query 2023-10-15 16:55:25 +02:00
Faruk AYDIN
1e601559a0 test: remove roles and permission with global before all hook 2023-10-15 16:54:51 +02:00
Faruk AYDIN
9314cba724 test: Add getRole graphQL query test 2023-10-15 15:22:08 +02:00
Ömer Faruk Aydın
25224f0308 Merge pull request #1346 from automatisch/test/get-users
test: Implement tests for graphQL getUsers query
2023-10-14 21:35:51 +02:00
Faruk AYDIN
9a981d5373 test: Implement tests for graphQL getUsers query 2023-10-14 21:29:24 +02:00
Faruk AYDIN
c7116361ab refactor: No need to interpolate token for getUser test 2023-10-14 21:21:40 +02:00
Ömer Faruk Aydın
1f17236c4f Merge pull request #1345 from automatisch/refactor/tests
test: Correct get current user test descriptions
2023-10-14 21:21:21 +02:00
Ömer Faruk Aydın
67f415de35 Merge pull request #1344 from automatisch/test/healthcheck-query
test: Add test for healthcheck graphQL query
2023-10-14 21:20:55 +02:00
Faruk AYDIN
b890150817 chore: Add verbose flag to yarn test script 2023-10-14 20:13:13 +02:00
Faruk AYDIN
4de1fc49df test: Add test case for unauthorized user for getUser 2023-10-14 20:12:10 +02:00
Faruk AYDIN
fb80d5d70d test: Correct get current user test descriptions 2023-10-14 20:04:49 +02:00
Faruk AYDIN
eaa25e412a test: Add test for healthcheck graphQL query 2023-10-14 17:45:26 +02:00
Faruk AYDIN
706142f98e chore: Change the description of existing graphQL tests 2023-10-14 17:19:25 +02:00
Ömer Faruk Aydın
36a72d0a32 Merge pull request #1334 from automatisch/AUT-326
feat(invoice-ninja): add create client action
2023-10-14 15:36:24 +02:00
Faruk AYDIN
1d44d387ac feat(invoice-ninja): Add static country codes to create client fields 2023-10-14 15:26:58 +02:00
Faruk AYDIN
f93c41f2d8 feat(invoice-ninja): Set instance URL as base URL 2023-10-14 15:26:58 +02:00
Rıdvan Akca
5b13f880c8 feat(invoice-ninja): add create client action 2023-10-14 15:26:58 +02:00
Ömer Faruk Aydın
c4af873036 Merge pull request #1342 from automatisch/lowercase-email
fix: Lowercase user email before insert and update
2023-10-14 13:42:05 +02:00
Faruk AYDIN
fe820fb4fe fix: use lowercase email for user model findOne method 2023-10-13 13:21:55 +02:00
Faruk AYDIN
82ad0735d2 chore: Convert user emails to lowercase 2023-10-13 11:52:31 +02:00
Faruk AYDIN
8b0a421924 fix: Lowercase user email before insert and update 2023-10-13 11:44:52 +02:00
Ömer Faruk Aydın
d44cb3d92e Merge pull request #1341 from automatisch/test/get-current-user
test: Implement getCurrentUser graphQL test
2023-10-13 11:36:38 +02:00
Ömer Faruk Aydın
5f335ef5b3 refactor: Use token without string interpolation for getCurrentUser test
Co-authored-by: Ali BARIN <ali.barin53@gmail.com>
2023-10-13 11:24:54 +02:00
Faruk AYDIN
5831bf9653 test: Implement getCurrentUser graphQL test 2023-10-13 01:04:35 +02:00
Ömer Faruk Aydın
2480dfbb6d Merge pull request #1331 from automatisch/AUT-323
feat(invoice-ninja): add new quotes trigger
2023-10-12 16:03:28 +02:00
Ömer Faruk Aydın
162b352ea5 Merge pull request #1330 from automatisch/AUT-322
feat(invoice-ninja): add new projects trigger
2023-10-12 15:58:49 +02:00
Ömer Faruk Aydın
9f30d7d7ba Merge pull request #1329 from automatisch/AUT-321
feat(invoice-ninja): add new payments trigger
2023-10-12 15:55:44 +02:00
Rıdvan Akca
27c296bb89 feat(invoice-ninja): add new quotes trigger 2023-10-12 14:44:17 +03:00
Rıdvan Akca
ef67908451 feat(invoice-ninja): add new projects trigger 2023-10-12 14:43:39 +03:00
Rıdvan Akca
3dedc3bfc7 feat(invoice-ninja): add new payments trigger 2023-10-12 14:43:06 +03:00
Ömer Faruk Aydın
65d509c97f Merge pull request #1328 from automatisch/AUT-320
feat(invoice-ninja): add new invoices trigger
2023-10-11 16:00:25 +02:00
Rıdvan Akca
11f38c4d3a feat(invoice-ninja): add new invoices trigger 2023-10-11 15:36:20 +02:00
Ömer Faruk Aydın
3bca6497f7 Merge pull request #1327 from automatisch/AUT-319
feat(invoice-ninja): add new credits trigger
2023-10-11 15:34:17 +02:00
Ömer Faruk Aydın
23d79b9265 Merge pull request #1333 from automatisch/backend-tests-ci
feat: Add CI configuration for backend tests
2023-10-11 14:07:01 +02:00
Faruk AYDIN
12c53a3d4d feat: Add CI configuration for backend tests 2023-10-11 13:41:03 +02:00
Ömer Faruk Aydın
1dfe58ec02 Merge pull request #1337 from automatisch/placetel-webhook
fix(placetel): Parse request body inside of run method
2023-10-11 11:24:55 +02:00
Faruk AYDIN
7615e62bbc fix(placetel): Parse request body inside of run method 2023-10-11 11:17:19 +02:00
Faruk AYDIN
53189a6487 fix: Do not parse all requests with json parser 2023-10-10 18:26:53 +02:00
Rıdvan Akca
027b11c3fb feat(invoice-ninja): add new credits trigger 2023-10-10 16:31:42 +03:00
Ömer Faruk Aydın
453ab7de66 Merge pull request #1325 from automatisch/AUT-318
feat(invoice-ninja): add new clients trigger
2023-10-10 12:51:40 +02:00
Rıdvan Akca
58f8ded161 feat(invoice-ninja): add new clients trigger 2023-10-10 13:41:14 +03:00
Ömer Faruk Aydın
497ce2e84f Merge pull request #1323 from automatisch/AUT-315
feat(invoice-ninja): add invoice ninja integration
2023-10-09 16:41:35 +02:00
Faruk AYDIN
3794c6f508 docs(invoice-ninja): Add warning to see API tokens 2023-10-09 16:30:53 +02:00
Rıdvan Akca
ee6c9fa5d4 feat(invoice-ninja): add invoice ninja integration 2023-10-09 16:06:00 +03:00
Ömer Faruk Aydın
1369bb095f Merge pull request #1324 from automatisch/placetel
feat(placetel): Implement app structure with authentication
2023-10-09 14:19:58 +02:00
Faruk AYDIN
c786d7549a refactor(placetel): No need to stringify call ID 2023-10-09 14:11:39 +02:00
Faruk AYDIN
b59840cb77 feat(placetel): Add types to hungup call trigger 2023-10-09 12:49:59 +02:00
Faruk AYDIN
265d57d8b7 refactor(placetel): Remove redundant header variable 2023-10-09 12:18:55 +02:00
Faruk AYDIN
5eed84f9e5 feat: Add run method to placetel hungup trigger 2023-10-09 12:18:11 +02:00
Faruk AYDIN
0a334dff1d docs(placetel): Add connection and triggers pages 2023-10-09 12:17:23 +02:00
Faruk AYDIN
5fff9bdc02 chore(placetel): Add missing type file 2023-10-09 12:17:23 +02:00
Faruk AYDIN
a9fd261bab refactor: Use json parser when content type is not specified 2023-10-09 12:17:23 +02:00
Faruk AYDIN
ef087be4f0 feat(placetel): Add hungup call trigger 2023-10-09 12:17:23 +02:00
Faruk AYDIN
2099978b8f fix: Add fields from substeps to getApps query 2023-10-09 12:17:23 +02:00
Faruk AYDIN
712bee297a feat(placetel): Implement app structure with authentication 2023-10-09 12:17:23 +02:00
Ömer Faruk Aydın
57bba90091 Merge pull request #1332 from automatisch/webhook-body
feat: Add run method to webhook triggers
2023-10-09 12:12:39 +02:00
Faruk AYDIN
d877f5c764 refactor: Use trigger queue for webhook handler 2023-10-09 12:05:45 +02:00
Faruk AYDIN
4c66cc1e33 feat: Adjust webhook handler to work with run method 2023-10-08 15:51:59 +02:00
Faruk AYDIN
27a3edeb93 feat: Add run method to webhook triggers 2023-10-07 18:46:25 +02:00
Ali BARIN
f79fc29203 chore: add mock license server 2023-10-06 14:39:11 +02:00
Ali BARIN
174240a220 refactor: add useEnqueueSnackbar with data-test attr 2023-10-06 12:28:09 +02:00
Ömer Faruk Aydın
60d8af5c16 Merge pull request #1321 from automatisch/AUT-310
feat(pipedrive): add create lead action
2023-10-06 11:40:04 +02:00
Faruk AYDIN
627a5892f1 chore: Add description to Pipedrive create lead fields 2023-10-06 11:33:52 +02:00
Ömer Faruk Aydın
7767f6d9cc Merge pull request #1322 from automatisch/salesforce/api-request
feat(salesforce): Implement execute query action
2023-10-05 16:12:42 +02:00
Faruk AYDIN
9729fd6b15 feat(salesforce): Implement execute query action 2023-10-05 15:23:48 +02:00
Rıdvan Akca
17916f29f6 feat(pipedrive): add create lead action 2023-10-05 14:12:37 +03:00
kattoczko
584b9323ec feat: introduce admin apps page (#1296)
* feat: introduce admin apps page

* feat: add access restriction and fix incorrectly placed key prop
2023-10-05 10:55:00 +02:00
Ömer Faruk Aydın
82c1aadfa9 Merge pull request #1319 from automatisch/refactor-get-user-tests
refactor: Use fixtures for getUser graphQL tests
2023-10-05 10:25:49 +02:00
Faruk AYDIN
bd497af89b test: Add case to getUser to not return user password 2023-10-04 21:01:16 +02:00
Faruk AYDIN
1683c5630a test: Add types to getUser test file 2023-10-04 21:01:16 +02:00
Faruk AYDIN
b5df1a026a chore: Use snake case test mappers for test env 2023-10-04 21:01:16 +02:00
Faruk AYDIN
b290c32aeb refactor: Use shared requestObject for getUser tests 2023-10-04 21:01:16 +02:00
Faruk AYDIN
a29b3c6db4 refactor: Use fixtures for getUser graphQL tests 2023-10-04 21:01:16 +02:00
Faruk AYDIN
ffb2f4f5db refactor: Use unauthorized user describe block for getUser tests 2023-10-04 21:01:16 +02:00
Faruk AYDIN
24c95f4801 refactor: Remove reduntant global knex instance 2023-10-04 21:01:16 +02:00
Ömer Faruk Aydın
1aaec2d555 Merge pull request #1317 from automatisch/AUT-306
feat(pipedrive): add create activity action
2023-10-04 20:55:43 +02:00
Rıdvan Akca
fedb198ae7 feat(pipedrive): add create activity action 2023-10-04 13:31:39 +03:00
Ömer Faruk Aydın
add654ccac Merge pull request #1314 from automatisch/get-user-test
feat: Implement getUser graphQL query test
2023-10-04 12:19:12 +02:00
Faruk AYDIN
5a578643a6 feat: Implement getUser graphQL query test 2023-10-04 11:52:39 +02:00
Faruk AYDIN
f0712bd213 feat: Adjust global hooks to work with both knex and objection 2023-10-04 11:48:38 +02:00
Faruk AYDIN
f490632722 refactor: Login mutation with create auth token helper 2023-10-04 11:46:18 +02:00
Faruk AYDIN
2610b96762 chore: Install supertest and faker libraries for tests 2023-10-04 11:46:18 +02:00
Ömer Faruk Aydın
8d90cb834d Merge pull request #1312 from automatisch/global-test-hooks
feat: Add global hooks for jest
2023-10-04 11:45:37 +02:00
Faruk AYDIN
9d92509796 fix: Disable eslint no-var while overriding global 2023-10-03 23:40:12 +02:00
Faruk AYDIN
04a78ee0ba feat: Add types knex field of global 2023-10-03 23:40:12 +02:00
Faruk AYDIN
3703390268 chore: Adjust global and knex types for global hooks 2023-10-03 23:40:12 +02:00
Faruk AYDIN
4ab6415f49 feat: Migrate test database within pretest script 2023-10-03 23:40:12 +02:00
Faruk AYDIN
9228722147 chore: Use ts migration files also for test env 2023-10-03 23:40:12 +02:00
Faruk AYDIN
0e5529b4ca feat: Add global hooks for jest 2023-10-03 23:40:12 +02:00
Ömer Faruk Aydın
4d454ec932 Merge pull request #1313 from automatisch/AUT-303
feat(pipedrive): add create note action
2023-10-02 17:50:42 +02:00
Rıdvan Akca
a9282ad118 feat(pipedrive): add create note action 2023-10-02 16:19:07 +03:00
Ömer Faruk Aydın
0e959641af Merge pull request #1310 from automatisch/test-setup
feat: Introduce jest for backend tests
2023-09-30 15:02:01 +02:00
Faruk AYDIN
13263eea76 feat: Introduce jest for backend tests 2023-09-30 14:31:01 +02:00
Faruk AYDIN
1a3418de58 chore: Remove ava from test setup 2023-09-30 14:17:47 +02:00
Ömer Faruk Aydın
4402995132 Merge pull request #1133 from automatisch/sandbox-emails
chore: Allow only automatisch emails for non-prod cloud envs
2023-09-30 13:25:18 +02:00
Ömer Faruk Aydın
4e33f9875b Merge pull request #1307 from automatisch/pipedrive-create-deal
feat(pipedrive): add create deal action
2023-09-30 13:20:43 +02:00
Faruk AYDIN
ed2893e37f fix(pipedrive): Add guard for not having any organizations 2023-09-30 13:13:21 +02:00
Rıdvan Akca
c35be241ca feat(pipedrive): add create deal action 2023-09-30 13:05:09 +02:00
Ömer Faruk Aydın
aad0b4ddfe Merge pull request #1302 from automatisch/miro-create-card-widget
feat(miro): add create card widget action
2023-09-30 10:48:25 +02:00
Rıdvan Akca
52f5c7ddb5 feat(miro): add create card widget action 2023-09-29 21:39:30 +02:00
Kasia
a04b933161 refactor: introduce useApps hook 2023-09-29 17:03:36 +02:00
kattoczko
c77e12edbb fix: consider null as value in step parameters (#1282) 2023-09-29 16:33:45 +02:00
Ömer Faruk Aydın
108bd04cf8 Merge pull request #1301 from automatisch/miro-copy-board
feat(miro): add copy board action
2023-09-29 12:52:25 +02:00
Rıdvan Akca
a95b500e42 feat(miro): add copy board action 2023-09-29 12:46:36 +02:00
Ömer Faruk Aydın
c2744c5569 Merge pull request #1300 from automatisch/miro-integration
feat(miro): add create board action
2023-09-29 12:45:05 +02:00
Faruk AYDIN
a1dfd87bbe docs(miro): Add to available apps 2023-09-29 12:31:33 +02:00
Rıdvan Akca
221aa8687f feat(miro): add create board action 2023-09-29 12:17:21 +02:00
Rıdvan Akca
fa8ac0a8ba feat(miro): add miro integration 2023-09-29 12:17:21 +02:00
Ömer Faruk Aydın
cc1f9873cb Merge pull request #1299 from automatisch/pipedrive-new-lead
feat(pipedrive): add new leads trigger
2023-09-29 12:15:28 +02:00
Rıdvan Akca
3cae9ee5d2 feat(pipedrive): add new leads trigger 2023-09-29 12:06:56 +02:00
Ömer Faruk Aydın
f764914adb Merge pull request #1298 from automatisch/pipedrive-new-activity
feat(pipedrive): add new activities trigger
2023-09-29 12:04:29 +02:00
Rıdvan Akca
991250c73f feat(pipedrive): add new activities trigger 2023-09-29 11:57:11 +02:00
Ömer Faruk Aydın
af46cf5ce8 Merge pull request #1297 from automatisch/pipedrive-new-note
feat(pipedrive): add new notes trigger
2023-09-29 11:53:10 +02:00
Faruk AYDIN
dbb1c42c47 fix(pipedrive): Skip adding auth header for refresh token 2023-09-29 11:44:35 +02:00
Rıdvan Akca
991f593b2e feat(pipedrive): add new notes trigger 2023-09-29 11:44:35 +02:00
Ömer Faruk Aydın
e43c083d50 Merge pull request #1305 from automatisch/fix-user-creation
fix: use default role for fallback user creation
2023-09-29 10:39:29 +02:00
Ömer Faruk Aydın
3cd9bdc1d4 Merge pull request #1304 from automatisch/new-calendars
feat(google-calendar): Add max results option to new calendar trigger
2023-09-28 17:00:51 +02:00
Ali BARIN
c0b8e6178d fix: use default role for fallback user creation 2023-09-28 14:49:49 +00:00
Ömer Faruk Aydın
410f9d0af5 Merge pull request #1291 from automatisch/google-calendar-new-event
feat(google-calendar): add new event trigger
2023-09-28 16:16:32 +02:00
Ömer Faruk Aydın
b1fedf28dc Merge branch 'main' into google-calendar-new-event 2023-09-28 16:06:08 +02:00
Faruk AYDIN
b0df03dcd2 feat(google-calendar): Add max results option to new calendar trigger 2023-09-28 15:10:36 +02:00
Ömer Faruk Aydın
2794e50a19 Merge pull request #1294 from automatisch/pipedrive-integration
feat(pipedrive): add new deals trigger
2023-09-28 15:09:17 +02:00
Faruk AYDIN
365ae656f2 fix(pipedrive): Adjust description of OAuth redirect URL 2023-09-28 15:01:35 +02:00
Faruk AYDIN
98649dcba6 fix(pipedrive): Guard new deals response in case there is none 2023-09-28 15:00:34 +02:00
Faruk AYDIN
213c8096d2 fix(pipedrive): Change the order in available apps 2023-09-28 15:00:34 +02:00
Faruk AYDIN
398938f27e feat(pipedrive): Use also company domain for screen name 2023-09-28 15:00:34 +02:00
Faruk AYDIN
6378e62645 docs(pipedrive): Add to available apps 2023-09-28 15:00:34 +02:00
Rıdvan Akca
251885d4be feat(pipedrive): add new deals trigger 2023-09-28 15:00:34 +02:00
Rıdvan Akca
f53909355f feat(pipedrive): add pipedrive integration 2023-09-28 15:00:34 +02:00
Rıdvan Akca
242b68889a feat(google-calendar): add new event trigger 2023-09-28 15:52:53 +03:00
Ömer Faruk Aydın
6a66b65f2a Merge pull request #1290 from automatisch/google-calendar-new-calendar-trigger
feat(google-calendar): add new calendar trigger
2023-09-28 13:45:41 +02:00
Faruk AYDIN
f30ead6bcb docs(google-calendar): Add to available apps 2023-09-28 13:37:32 +02:00
Faruk AYDIN
237ee72ca6 feat(google-calendar): Use etag as internal id instead of id 2023-09-28 13:29:39 +02:00
Faruk AYDIN
3590d84ad6 fix(google-calendar): Reverse order the response data of new calendars 2023-09-28 13:28:58 +02:00
Rıdvan Akca
2dae8c162d feat(google-calendar): add new calendar trigger 2023-09-18 15:17:05 +03:00
Rıdvan Akca
9a192b708e feat(google-calendar): add google calendar integration 2023-09-18 15:12:29 +03:00
Faruk AYDIN
337d22bbf4 chore: Allow only automatisch emails for non-prod cloud envs 2023-06-08 11:58:14 +02:00
356 changed files with 15731 additions and 3216 deletions

View File

@@ -7,4 +7,12 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
'prettier',
],
overrides: [
{
files: ['**/*.test.ts', '**/test/**/*.ts'],
rules: {
'@typescript-eslint/ban-ts-comment': ['off'],
},
},
],
};

48
.github/workflows/backend.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: Automatisch Backend Tests
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
jobs:
test:
timeout-minutes: 60
runs-on:
- ubuntu-latest
services:
postgres:
image: postgres:14.5-alpine
env:
POSTGRES_DB: automatisch_test
POSTGRES_USER: automatisch_test_user
POSTGRES_PASSWORD: automatisch_test_user_password
options: >-
--health-cmd "pg_isready -U automatisch_test_user -d automatisch_test"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
redis:
image: redis:7.0.4-alpine
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: cd packages/backend && yarn
- name: Copy .env-example.test file to .env.test
run: cd packages/backend && cp .env-example.test .env.test
- name: Run tests
run: cd packages/backend && yarn test

View File

@@ -17,12 +17,13 @@ env:
POSTGRES_PASSWORD: automatisch_password
REDIS_HOST: localhost
APP_ENV: production
LICENSE_KEY: ${{ secrets.E2E_LICENSE_KEY }}
LICENSE_KEY: dummy_license_key
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
runs-on:
- ubuntu-latest
services:
postgres:
image: postgres:14.5-alpine
@@ -67,22 +68,49 @@ jobs:
- name: Seed user
working-directory: ./packages/backend
run: yarn db:seed:user &
- name: Install certutils
run: sudo apt install -y libnss3-tools
- name: Install mkcert
run: |
curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64" \
&& chmod +x mkcert-v*-linux-amd64 \
&& sudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert
- name: Install root certificate via mkcert
run: mkcert -install
- name: Create certificate
run: mkcert automatisch.io "*.automatisch.io" localhost 127.0.0.1 ::1
working-directory: ./packages/e2e-tests
- name: Set CAROOT environment variable
run: echo "NODE_EXTRA_CA_CERTS=$(mkcert -CAROOT)/rootCA.pem" >> "$GITHUB_ENV"
- name: Override license server with local server
run: sudo echo "127.0.0.1 license.automatisch.io" | sudo tee -a /etc/hosts
- name: Run local license server
working-directory: ./packages/e2e-tests
run: sudo yarn start-mock-license-server &
- name: Run Automatisch
run: yarn start &
working-directory: ./packages/backend
- name: Run Automatisch worker
run: node dist/src/worker.js &
working-directory: ./packages/backend
- name: Setup upterm session
if: false
uses: lhotari/action-upterm@v1
with:
limit-access-to-actor: true
limit-access-to-users: barinali
- name: Run Playwright tests
working-directory: ./packages/e2e-tests
env:
LOGIN_EMAIL: user@automatisch.io
LOGIN_PASSWORD: sample
BASE_URL: http://localhost:3000
GITHUB_CLIENT_ID: 1c0417daf898adfbd99a
GITHUB_CLIENT_SECRET: 3328fa814dd582ccd03dbe785cfd683fb8da92b3
run: yarn test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: ./packages/e2e-tests/test-results/**/*
path: packages/e2e-tests/test-results
retention-days: 30

View File

@@ -3,14 +3,13 @@ HOST=localhost
PROTOCOL=http
PORT=3000
LOG_LEVEL=debug
WEBHOOK_SECRET_KEY=secret
ENCRYPTION_KEY=sample_encryption_key
WEBHOOK_SECRET_KEY=sample_webhook_secret_key
APP_SECRET_KEY=sample_app_secret_key
POSTGRES_HOST=localhost
POSTGRES_DATABASE=automatisch_test
POSTGRES_PORT=5432
POSTGRES_HOST=localhost
POSTGRES_USERNAME=automatisch_test_user
POSTGRES_PASSWORD=
POSTGRES_ENABLE_SSL=false
ENCRYPTION_KEY=secret
APP_SECRET_KEY=secret
REDIS_PORT=6379
REDIS_HOST=127.0.0.1
POSTGRES_PASSWORD=automatisch_test_user_password
REDIS_HOST=localhost
AUTOMATISCH_CLOUD=true

View File

@@ -1,5 +0,0 @@
export default {
require: ['ts-node/register', './src/config/app.ts'],
files: ['**/*.test.ts'],
extensions: ['ts'],
};

View File

@@ -0,0 +1,9 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
setupFilesAfterEnv: ['./test/setup/global-hooks.ts'],
globalTeardown: './test/setup/global-teardown.ts',
collectCoverage: true,
collectCoverageFrom: ['src/graphql/queries/*.ts'],
};

View File

@@ -1,6 +1,7 @@
import { knexSnakeCaseMappers } from 'objection';
import appConfig from './src/config/app';
const fileExtension = appConfig.isDev ? 'ts' : 'js';
const fileExtension = appConfig.isDev || appConfig.isTest ? 'ts' : 'js';
const knexConfig = {
client: 'pg',
@@ -23,6 +24,7 @@ const knexConfig = {
seeds: {
directory: __dirname + '/src/db/seeds',
},
...(appConfig.isTest ? knexSnakeCaseMappers() : {}),
};
export default knexConfig;

View File

@@ -10,7 +10,7 @@
"build:watch": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec yarn build --ext ts",
"start": "node dist/src/server.js",
"pretest": "APP_ENV=test ts-node ./test/setup/prepare-test-env.ts",
"test": "APP_ENV=test ava",
"test": "APP_ENV=test jest --verbose",
"lint": "eslint . --ignore-path ../../.eslintignore",
"db:create": "ts-node ./bin/database/create.ts",
"db:seed:user": "ts-node ./bin/database/seed-user.ts",
@@ -60,6 +60,7 @@
"https-proxy-agent": "^7.0.1",
"jsonwebtoken": "^9.0.0",
"knex": "^2.4.0",
"libphonenumber-js": "^1.10.48",
"lodash.get": "^4.4.2",
"luxon": "2.5.2",
"memory-cache": "^0.2.0",
@@ -73,6 +74,7 @@
"pg": "^8.7.1",
"php-serialize": "^4.0.2",
"pluralize": "^8.0.0",
"raw-body": "^2.5.2",
"showdown": "^2.1.0",
"stripe": "^11.13.0",
"winston": "^3.7.1",
@@ -115,12 +117,14 @@
},
"devDependencies": {
"@automatisch/types": "^0.9.3",
"@faker-js/faker": "^8.1.0",
"@types/bcrypt": "^5.0.0",
"@types/bull": "^3.15.8",
"@types/cors": "^2.8.12",
"@types/crypto-js": "^4.0.2",
"@types/express": "^4.17.15",
"@types/http-errors": "^1.8.1",
"@types/jest": "^29.5.5",
"@types/jsonwebtoken": "^8.5.8",
"@types/lodash.get": "^4.4.6",
"@types/memory-cache": "^0.2.2",
@@ -132,9 +136,12 @@
"@types/pino": "^7.0.5",
"@types/pluralize": "^0.0.30",
"@types/showdown": "^2.0.1",
"ava": "^5.3.1",
"@types/supertest": "^2.0.14",
"jest": "^29.7.0",
"nodemon": "^2.0.13",
"sinon": "^11.1.2",
"supertest": "^6.3.3",
"ts-jest": "^29.1.1",
"ts-node": "^10.2.1",
"ts-node-dev": "^1.1.8"
},

View File

@@ -33,6 +33,7 @@ injectBullBoardHandler(app, serverAdapter);
appAssetsHandler(app);
app.use(morgan);
app.use(
express.json({
limit: appConfig.requestBodySizeLimit,

View File

@@ -1,3 +1,4 @@
import Crypto from 'crypto';
import isEmpty from 'lodash/isEmpty';
import defineTrigger from '../../../../helpers/define-trigger';
import webhookFilters from '../../common/webhook-filters';
@@ -19,6 +20,17 @@ export default defineTrigger({
},
],
async run($) {
const dataItem = {
raw: $.request.body,
meta: {
internalId: Crypto.randomUUID(),
},
};
$.pushTriggerItem(dataItem);
},
async testRun($) {
const lastExecutionStep = await $.getLastExecutionStep();

View File

@@ -3,11 +3,13 @@ import defineAction from '../../../../helpers/define-action';
import performMathOperation from './transformers/perform-math-operation';
import randomNumber from './transformers/random-number';
import formatNumber from './transformers/format-number';
import formatPhoneNumber from './transformers/format-phone-number';
const transformers = {
performMathOperation,
randomNumber,
formatNumber,
formatPhoneNumber,
};
export default defineAction({
@@ -26,6 +28,7 @@ export default defineAction({
{ label: 'Perform Math Operation', value: 'performMathOperation' },
{ label: 'Random Number', value: 'randomNumber' },
{ label: 'Format Number', value: 'formatNumber' },
{ label: 'Format Phone Number', value: 'formatPhoneNumber' },
],
additionalFields: {
type: 'query',

View File

@@ -0,0 +1,24 @@
import { IGlobalVariable } from '@automatisch/types';
import parsePhoneNumber, { CountryCode } from 'libphonenumber-js';
const formatPhoneNumber = ($: IGlobalVariable) => {
const phoneNumber = $.step.parameters.phoneNumber as string;
const toFormat = $.step.parameters.toFormat as string;
const phoneNumberCountryCode = ($.step.parameters.phoneNumberCountryCode ||
'US') as CountryCode;
const parsedPhoneNumber = parsePhoneNumber(
phoneNumber,
phoneNumberCountryCode
);
if (toFormat === 'e164') {
return parsedPhoneNumber.format('E.164');
} else if (toFormat === 'international') {
return parsedPhoneNumber.formatInternational();
} else if (toFormat === 'national') {
return parsedPhoneNumber.formatNational();
}
};
export default formatPhoneNumber;

View File

@@ -0,0 +1,249 @@
const phoneNumberCountryCodes = [
{ label: 'Ascension Island', value: 'AC' },
{ label: 'Andorra', value: 'AD' },
{ label: 'United Arab Emirates', value: 'AE' },
{ label: 'Afghanistan', value: 'AF' },
{ label: 'Antigua & Barbuda', value: 'AG' },
{ label: 'Anguilla', value: 'AI' },
{ label: 'Albania', value: 'AL' },
{ label: 'Armenia', value: 'AM' },
{ label: 'Angola', value: 'AO' },
{ label: 'Argentina', value: 'AR' },
{ label: 'American Samoa', value: 'AS' },
{ label: 'Austria', value: 'AT' },
{ label: 'Australia', value: 'AU' },
{ label: 'Aruba', value: 'AW' },
{ label: 'Åland Islands', value: 'AX' },
{ label: 'Azerbaijan', value: 'AZ' },
{ label: 'Bosnia & Herzegovina', value: 'BA' },
{ label: 'Barbados', value: 'BB' },
{ label: 'Bangladesh', value: 'BD' },
{ label: 'Belgium', value: 'BE' },
{ label: 'Burkina Faso', value: 'BF' },
{ label: 'Bulgaria', value: 'BG' },
{ label: 'Bahrain', value: 'BH' },
{ label: 'Burundi', value: 'BI' },
{ label: 'Benin', value: 'BJ' },
{ label: 'St. Barthélemy', value: 'BL' },
{ label: 'Bermuda', value: 'BM' },
{ label: 'Brunei', value: 'BN' },
{ label: 'Bolivia', value: 'BO' },
{ label: 'Caribbean Netherlands', value: 'BQ' },
{ label: 'Brazil', value: 'BR' },
{ label: 'Bahamas', value: 'BS' },
{ label: 'Bhutan', value: 'BT' },
{ label: 'Botswana', value: 'BW' },
{ label: 'Belarus', value: 'BY' },
{ label: 'Belize', value: 'BZ' },
{ label: 'Canada', value: 'CA' },
{ label: 'Cocos (Keeling) Islands', value: 'CC' },
{ label: 'Congo - Kinshasa', value: 'CD' },
{ label: 'Central African Republic', value: 'CF' },
{ label: 'Congo - Brazzaville', value: 'CG' },
{ label: 'Switzerland', value: 'CH' },
{ label: 'Côte dIvoire', value: 'CI' },
{ label: 'Cook Islands', value: 'CK' },
{ label: 'Chile', value: 'CL' },
{ label: 'Cameroon', value: 'CM' },
{ label: 'China', value: 'CN' },
{ label: 'Colombia', value: 'CO' },
{ label: 'Costa Rica', value: 'CR' },
{ label: 'Cuba', value: 'CU' },
{ label: 'Cape Verde', value: 'CV' },
{ label: 'Curaçao', value: 'CW' },
{ label: 'Christmas Island', value: 'CX' },
{ label: 'Cyprus', value: 'CY' },
{ label: 'Czechia', value: 'CZ' },
{ label: 'Germany', value: 'DE' },
{ label: 'Djibouti', value: 'DJ' },
{ label: 'Denmark', value: 'DK' },
{ label: 'Dominica', value: 'DM' },
{ label: 'Dominican Republic', value: 'DO' },
{ label: 'Algeria', value: 'DZ' },
{ label: 'Ecuador', value: 'EC' },
{ label: 'Estonia', value: 'EE' },
{ label: 'Egypt', value: 'EG' },
{ label: 'Western Sahara', value: 'EH' },
{ label: 'Eritrea', value: 'ER' },
{ label: 'Spain', value: 'ES' },
{ label: 'Ethiopia', value: 'ET' },
{ label: 'Finland', value: 'FI' },
{ label: 'Fiji', value: 'FJ' },
{ label: 'Falkland Islands (Islas Malvinas)', value: 'FK' },
{ label: 'Micronesia', value: 'FM' },
{ label: 'Faroe Islands', value: 'FO' },
{ label: 'France', value: 'FR' },
{ label: 'Gabon', value: 'GA' },
{ label: 'United Kingdom', value: 'GB' },
{ label: 'Grenada', value: 'GD' },
{ label: 'Georgia', value: 'GE' },
{ label: 'French Guiana', value: 'GF' },
{ label: 'Guernsey', value: 'GG' },
{ label: 'Ghana', value: 'GH' },
{ label: 'Gibraltar', value: 'GI' },
{ label: 'Greenland', value: 'GL' },
{ label: 'Gambia', value: 'GM' },
{ label: 'Guinea', value: 'GN' },
{ label: 'Guadeloupe', value: 'GP' },
{ label: 'Equatorial Guinea', value: 'GQ' },
{ label: 'Greece', value: 'GR' },
{ label: 'Guatemala', value: 'GT' },
{ label: 'Guam', value: 'GU' },
{ label: 'Guinea-Bissau', value: 'GW' },
{ label: 'Guyana', value: 'GY' },
{ label: 'Hong Kong', value: 'HK' },
{ label: 'Honduras', value: 'HN' },
{ label: 'Croatia', value: 'HR' },
{ label: 'Haiti', value: 'HT' },
{ label: 'Hungary', value: 'HU' },
{ label: 'Indonesia', value: 'ID' },
{ label: 'Ireland', value: 'IE' },
{ label: 'Israel', value: 'IL' },
{ label: 'Isle of Man', value: 'IM' },
{ label: 'India', value: 'IN' },
{ label: 'British Indian Ocean Territory', value: 'IO' },
{ label: 'Iraq', value: 'IQ' },
{ label: 'Iran', value: 'IR' },
{ label: 'Iceland', value: 'IS' },
{ label: 'Italy', value: 'IT' },
{ label: 'Jersey', value: 'JE' },
{ label: 'Jamaica', value: 'JM' },
{ label: 'Jordan', value: 'JO' },
{ label: 'Japan', value: 'JP' },
{ label: 'Kenya', value: 'KE' },
{ label: 'Kyrgyzstan', value: 'KG' },
{ label: 'Cambodia', value: 'KH' },
{ label: 'Kiribati', value: 'KI' },
{ label: 'Comoros', value: 'KM' },
{ label: 'St. Kitts & Nevis', value: 'KN' },
{ label: 'North Korea', value: 'KP' },
{ label: 'South Korea', value: 'KR' },
{ label: 'Kuwait', value: 'KW' },
{ label: 'Cayman Islands', value: 'KY' },
{ label: 'Kazakhstan', value: 'KZ' },
{ label: 'Laos', value: 'LA' },
{ label: 'Lebanon', value: 'LB' },
{ label: 'St. Lucia', value: 'LC' },
{ label: 'Liechtenstein', value: 'LI' },
{ label: 'Sri Lanka', value: 'LK' },
{ label: 'Liberia', value: 'LR' },
{ label: 'Lesotho', value: 'LS' },
{ label: 'Lithuania', value: 'LT' },
{ label: 'Luxembourg', value: 'LU' },
{ label: 'Latvia', value: 'LV' },
{ label: 'Libya', value: 'LY' },
{ label: 'Morocco', value: 'MA' },
{ label: 'Monaco', value: 'MC' },
{ label: 'Moldova', value: 'MD' },
{ label: 'Montenegro', value: 'ME' },
{ label: 'St. Martin', value: 'MF' },
{ label: 'Madagascar', value: 'MG' },
{ label: 'Marshall Islands', value: 'MH' },
{ label: 'North Macedonia', value: 'MK' },
{ label: 'Mali', value: 'ML' },
{ label: 'Myanmar (Burma)', value: 'MM' },
{ label: 'Mongolia', value: 'MN' },
{ label: 'Macao', value: 'MO' },
{ label: 'Northern Mariana Islands', value: 'MP' },
{ label: 'Martinique', value: 'MQ' },
{ label: 'Mauritania', value: 'MR' },
{ label: 'Montserrat', value: 'MS' },
{ label: 'Malta', value: 'MT' },
{ label: 'Mauritius', value: 'MU' },
{ label: 'Maldives', value: 'MV' },
{ label: 'Malawi', value: 'MW' },
{ label: 'Mexico', value: 'MX' },
{ label: 'Malaysia', value: 'MY' },
{ label: 'Mozambique', value: 'MZ' },
{ label: 'Namibia', value: 'NA' },
{ label: 'New Caledonia', value: 'NC' },
{ label: 'Niger', value: 'NE' },
{ label: 'Norfolk Island', value: 'NF' },
{ label: 'Nigeria', value: 'NG' },
{ label: 'Nicaragua', value: 'NI' },
{ label: 'Netherlands', value: 'NL' },
{ label: 'Norway', value: 'NO' },
{ label: 'Nepal', value: 'NP' },
{ label: 'Nauru', value: 'NR' },
{ label: 'Niue', value: 'NU' },
{ label: 'New Zealand', value: 'NZ' },
{ label: 'Oman', value: 'OM' },
{ label: 'Panama', value: 'PA' },
{ label: 'Peru', value: 'PE' },
{ label: 'French Polynesia', value: 'PF' },
{ label: 'Papua New Guinea', value: 'PG' },
{ label: 'Philippines', value: 'PH' },
{ label: 'Pakistan', value: 'PK' },
{ label: 'Poland', value: 'PL' },
{ label: 'St. Pierre & Miquelon', value: 'PM' },
{ label: 'Puerto Rico', value: 'PR' },
{ label: 'Palestine', value: 'PS' },
{ label: 'Portugal', value: 'PT' },
{ label: 'Palau', value: 'PW' },
{ label: 'Paraguay', value: 'PY' },
{ label: 'Qatar', value: 'QA' },
{ label: 'Réunion', value: 'RE' },
{ label: 'Romania', value: 'RO' },
{ label: 'Serbia', value: 'RS' },
{ label: 'Russia', value: 'RU' },
{ label: 'Rwanda', value: 'RW' },
{ label: 'Saudi Arabia', value: 'SA' },
{ label: 'Solomon Islands', value: 'SB' },
{ label: 'Seychelles', value: 'SC' },
{ label: 'Sudan', value: 'SD' },
{ label: 'Sweden', value: 'SE' },
{ label: 'Singapore', value: 'SG' },
{ label: 'St. Helena', value: 'SH' },
{ label: 'Slovenia', value: 'SI' },
{ label: 'Svalbard & Jan Mayen', value: 'SJ' },
{ label: 'Slovakia', value: 'SK' },
{ label: 'Sierra Leone', value: 'SL' },
{ label: 'San Marino', value: 'SM' },
{ label: 'Senegal', value: 'SN' },
{ label: 'Somalia', value: 'SO' },
{ label: 'Suriname', value: 'SR' },
{ label: 'South Sudan', value: 'SS' },
{ label: 'São Tomé & Príncipe', value: 'ST' },
{ label: 'El Salvador', value: 'SV' },
{ label: 'Sint Maarten', value: 'SX' },
{ label: 'Syria', value: 'SY' },
{ label: 'Eswatini', value: 'SZ' },
{ label: 'Tristan da Cunha', value: 'TA' },
{ label: 'Turks & Caicos Islands', value: 'TC' },
{ label: 'Chad', value: 'TD' },
{ label: 'Togo', value: 'TG' },
{ label: 'Thailand', value: 'TH' },
{ label: 'Tajikistan', value: 'TJ' },
{ label: 'Tokelau', value: 'TK' },
{ label: 'Timor-Leste', value: 'TL' },
{ label: 'Turkmenistan', value: 'TM' },
{ label: 'Tunisia', value: 'TN' },
{ label: 'Tonga', value: 'TO' },
{ label: 'Türkiye', value: 'TR' },
{ label: 'Trinidad & Tobago', value: 'TT' },
{ label: 'Tuvalu', value: 'TV' },
{ label: 'Taiwan', value: 'TW' },
{ label: 'Tanzania', value: 'TZ' },
{ label: 'Ukraine', value: 'UA' },
{ label: 'Uganda', value: 'UG' },
{ label: 'United States', value: 'US' },
{ label: 'Uruguay', value: 'UY' },
{ label: 'Uzbekistan', value: 'UZ' },
{ label: 'Vatican City', value: 'VA' },
{ label: 'St. Vincent & Grenadines', value: 'VC' },
{ label: 'Venezuela', value: 'VE' },
{ label: 'British Virgin Islands', value: 'VG' },
{ label: 'U.S. Virgin Islands', value: 'VI' },
{ label: 'Vietnam', value: 'VN' },
{ label: 'Vanuatu', value: 'VU' },
{ label: 'Wallis & Futuna', value: 'WF' },
{ label: 'Samoa', value: 'WS' },
{ label: 'Kosovo', value: 'XK' },
{ label: 'Yemen', value: 'YE' },
{ label: 'Mayotte', value: 'YT' },
{ label: 'South Africa', value: 'ZA' },
{ label: 'Zambia', value: 'ZM' },
{ label: 'Zimbabwe', value: 'ZW' },
];
export default phoneNumberCountryCodes;

View File

@@ -12,6 +12,7 @@ import useDefaultValue from './text/use-default-value';
import performMathOperation from './numbers/perform-math-operation';
import randomNumber from './numbers/random-number';
import formatNumber from './numbers/format-number';
import formatPhoneNumber from './numbers/format-phone-number';
import formatDateTime from './date-time/format-date-time';
const options: IJSONObject = {
@@ -28,6 +29,7 @@ const options: IJSONObject = {
performMathOperation,
randomNumber,
formatNumber,
formatPhoneNumber,
formatDateTime,
};

View File

@@ -0,0 +1,36 @@
import phoneNumberCountryCodes from '../../../common/phone-number-country-codes';
const formatPhoneNumber = [
{
label: 'Phone Number',
key: 'phoneNumber',
type: 'string' as const,
required: true,
description: 'The phone number you want to format.',
variables: true,
},
{
label: 'To Format',
key: 'toFormat',
type: 'dropdown' as const,
required: true,
description: 'The format you want to convert the number to.',
variables: true,
options: [
{ label: '+491632223344 (E164)', value: 'e164' },
{ label: '+49 163 2223344 (International)', value: 'international' },
{ label: '0163 2223344 (National)', value: 'national' },
],
},
{
label: 'Phone Number Country Code',
key: 'phoneNumberCountryCode',
type: 'dropdown' as const,
required: true,
description: 'The country code of the phone number. The default is US.',
variables: true,
options: phoneNumberCountryCodes,
},
];
export default formatPhoneNumber;

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -19,6 +20,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.confidential_issues_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.confidential_issues_events),
unregisterHook,

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -19,6 +20,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.confidential_note_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.confidential_note_events),
unregisterHook,

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -18,6 +19,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.deployment_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.deployment_events),
unregisterHook,

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -18,6 +19,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.feature_flag_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.feature_flag_events),
unregisterHook,

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -18,6 +19,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.issues_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.issues_events),
unregisterHook,

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -17,6 +18,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.job_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.job_events),
unregisterHook,

View File

@@ -22,6 +22,17 @@ export const projectArgumentDescriptor = {
},
};
export const getRunFn = async ($: IGlobalVariable) => {
const dataItem = {
raw: $.request.body,
meta: {
internalId: Crypto.randomUUID(),
},
};
$.pushTriggerItem(dataItem);
};
export const getTestRunFn =
(eventData: IJSONObject) => ($: IGlobalVariable) => {
/*

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -18,6 +19,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.merge_requests_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.merge_requests_events),
unregisterHook,

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -18,6 +19,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.note_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.note_events),
unregisterHook,

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -18,6 +19,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.pipeline_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.pipeline_events),
unregisterHook,

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -54,6 +55,7 @@ export const triggerDescriptor: IRawTrigger = {
branchFilterStrategyArgumentDescriptor,
pushEventsBranchFilterArgumentDescriptor,
],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.push_events),
unregisterHook,

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -17,6 +18,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.releases_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.releases_events),
unregisterHook,

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -18,6 +19,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.tag_push_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.tag_push_events),
unregisterHook,

View File

@@ -3,6 +3,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getRunFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
@@ -18,6 +19,7 @@ export const triggerDescriptor: IRawTrigger = {
key: GITLAB_EVENT_TYPE.wiki_page_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
run: ($) => getRunFn($),
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.wiki_page_events),
unregisterHook,

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Livello_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 200 200" enable-background="new 0 0 200 200" xml:space="preserve">
<g>
<g transform="translate(3.75 3.75)">
<path fill="#FFFFFF" d="M148.882,43.618l-47.368-5.263l-57.895,5.263L38.355,96.25l5.263,52.632l52.632,6.579l52.632-6.579
l5.263-53.947L148.882,43.618z"/>
<path fill="#1A73E8" d="M65.211,125.276c-3.934-2.658-6.658-6.539-8.145-11.671l9.132-3.763c0.829,3.158,2.276,5.605,4.342,7.342
c2.053,1.737,4.553,2.592,7.474,2.592c2.987,0,5.553-0.908,7.697-2.724s3.224-4.132,3.224-6.934c0-2.868-1.132-5.211-3.395-7.026
s-5.105-2.724-8.5-2.724h-5.276v-9.039H76.5c2.921,0,5.382-0.789,7.382-2.368c2-1.579,3-3.737,3-6.487
c0-2.447-0.895-4.395-2.684-5.855s-4.053-2.197-6.803-2.197c-2.684,0-4.816,0.711-6.395,2.145s-2.724,3.197-3.447,5.276
l-9.039-3.763c1.197-3.395,3.395-6.395,6.618-8.987c3.224-2.592,7.342-3.895,12.342-3.895c3.697,0,7.026,0.711,9.974,2.145
c2.947,1.434,5.263,3.421,6.934,5.947c1.671,2.539,2.5,5.382,2.5,8.539c0,3.224-0.776,5.947-2.329,8.184
c-1.553,2.237-3.461,3.947-5.724,5.145v0.539c2.987,1.25,5.421,3.158,7.342,5.724c1.908,2.566,2.868,5.632,2.868,9.211
s-0.908,6.776-2.724,9.579c-1.816,2.803-4.329,5.013-7.513,6.618c-3.197,1.605-6.789,2.421-10.776,2.421
C73.408,129.263,69.145,127.934,65.211,125.276z"/>
<path fill="#1A73E8" d="M121.25,79.961l-9.974,7.25l-5.013-7.605l17.987-12.974h6.895v61.197h-9.895L121.25,79.961z"/>
<path fill="#EA4335" d="M148.882,196.25l47.368-47.368l-23.684-10.526l-23.684,10.526l-10.526,23.684L148.882,196.25z"/>
<path fill="#34A853" d="M33.092,172.566l10.526,23.684h105.263v-47.368H43.618L33.092,172.566z"/>
<path fill="#4285F4" d="M12.039-3.75C3.316-3.75-3.75,3.316-3.75,12.039v136.842l23.684,10.526l23.684-10.526V43.618h105.263
l10.526-23.684L148.882-3.75H12.039z"/>
<path fill="#188038" d="M-3.75,148.882v31.579c0,8.724,7.066,15.789,15.789,15.789h31.579v-47.368H-3.75z"/>
<path fill="#FBBC04" d="M148.882,43.618v105.263h47.368V43.618l-23.684-10.526L148.882,43.618z"/>
<path fill="#1967D2" d="M196.25,43.618V12.039c0-8.724-7.066-15.789-15.789-15.789h-31.579v47.368H196.25z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,24 @@
import { IField, IGlobalVariable } from '@automatisch/types';
import { URLSearchParams } from 'url';
import authScope from '../common/auth-scope';
export default async function generateAuthUrl($: IGlobalVariable) {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const searchParams = new URLSearchParams({
client_id: $.auth.data.clientId as string,
redirect_uri: redirectUri,
prompt: 'select_account',
scope: authScope.join(' '),
response_type: 'code',
access_type: 'offline',
});
const url = `https://accounts.google.com/o/oauth2/v2/auth?${searchParams.toString()}`;
await $.auth.set({
url,
});
}

View File

@@ -0,0 +1,48 @@
import generateAuthUrl from './generate-auth-url';
import verifyCredentials from './verify-credentials';
import refreshToken from './refresh-token';
import isStillVerified from './is-still-verified';
export default {
fields: [
{
key: 'oAuthRedirectUrl',
label: 'OAuth Redirect URL',
type: 'string' as const,
required: true,
readOnly: true,
value: '{WEB_APP_URL}/app/google-calendar/connections/add',
placeholder: null,
description:
'When asked to input a redirect URL in Google Cloud, enter the URL above.',
clickToCopy: true,
},
{
key: 'clientId',
label: 'Client ID',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
{
key: 'clientSecret',
label: 'Client Secret',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
],
generateAuthUrl,
verifyCredentials,
isStillVerified,
refreshToken,
};

View File

@@ -0,0 +1,9 @@
import { IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
const isStillVerified = async ($: IGlobalVariable) => {
const currentUser = await getCurrentUser($);
return !!currentUser.resourceName;
};
export default isStillVerified;

View File

@@ -0,0 +1,26 @@
import { URLSearchParams } from 'node:url';
import { IGlobalVariable } from '@automatisch/types';
import authScope from '../common/auth-scope';
const refreshToken = async ($: IGlobalVariable) => {
const params = new URLSearchParams({
client_id: $.auth.data.clientId as string,
client_secret: $.auth.data.clientSecret as string,
grant_type: 'refresh_token',
refresh_token: $.auth.data.refreshToken as string,
});
const { data } = await $.http.post(
'https://oauth2.googleapis.com/token',
params.toString()
);
await $.auth.set({
accessToken: data.access_token,
expiresIn: data.expires_in,
scope: authScope.join(' '),
tokenType: data.token_type,
});
};
export default refreshToken;

View File

@@ -0,0 +1,57 @@
import { IField, IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
type TUser = {
displayName: string;
metadata: {
primary: boolean;
};
};
type TEmailAddress = {
value: string;
metadata: {
primary: boolean;
};
};
const verifyCredentials = async ($: IGlobalVariable) => {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const { data } = await $.http.post(`https://oauth2.googleapis.com/token`, {
client_id: $.auth.data.clientId,
client_secret: $.auth.data.clientSecret,
code: $.auth.data.code,
grant_type: 'authorization_code',
redirect_uri: redirectUri,
});
await $.auth.set({
accessToken: data.access_token,
tokenType: data.token_type,
});
const currentUser = await getCurrentUser($);
const { displayName } = currentUser.names.find(
(name: TUser) => name.metadata.primary
);
const { value: email } = currentUser.emailAddresses.find(
(emailAddress: TEmailAddress) => emailAddress.metadata.primary
);
await $.auth.set({
clientId: $.auth.data.clientId,
clientSecret: $.auth.data.clientSecret,
scope: $.auth.data.scope,
idToken: data.id_token,
expiresIn: data.expires_in,
refreshToken: data.refresh_token,
resourceName: currentUser.resourceName,
screenName: `${displayName} - ${email}`,
});
};
export default verifyCredentials;

View File

@@ -0,0 +1,11 @@
import { TBeforeRequest } from '@automatisch/types';
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
if ($.auth.data?.accessToken) {
requestConfig.headers.Authorization = `${$.auth.data.tokenType} ${$.auth.data.accessToken}`;
}
return requestConfig;
};
export default addAuthHeader;

View File

@@ -0,0 +1,7 @@
const authScope: string[] = [
'https://www.googleapis.com/auth/calendar',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
];
export default authScope;

View File

@@ -0,0 +1,10 @@
import { IGlobalVariable } from '@automatisch/types';
const getCurrentUser = async ($: IGlobalVariable) => {
const { data: currentUser } = await $.http.get(
'https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses'
);
return currentUser;
};
export default getCurrentUser;

View File

@@ -0,0 +1,3 @@
import listCalendars from './list-calendars';
export default [listCalendars];

View File

@@ -0,0 +1,36 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List calendars',
key: 'listCalendars',
async run($: IGlobalVariable) {
const drives: {
data: IJSONObject[];
} = {
data: [],
};
const params = {
pageToken: undefined as unknown as string,
};
do {
const { data } = await $.http.get(`/v3/users/me/calendarList`, {
params,
});
params.pageToken = data.nextPageToken;
if (data.items) {
for (const calendar of data.items) {
drives.data.push({
value: calendar.id,
name: calendar.summary,
});
}
}
} while (params.pageToken);
return drives;
},
};

View File

View File

@@ -0,0 +1,20 @@
import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-header';
import auth from './auth';
import triggers from './triggers';
import dynamicData from './dynamic-data';
export default defineApp({
name: 'Google Calendar',
key: 'google-calendar',
baseUrl: 'https://calendar.google.com',
apiBaseUrl: 'https://www.googleapis.com/calendar',
iconUrl: '{BASE_URL}/apps/google-calendar/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/google-calendar/connection',
primaryColor: '448AFF',
supportsConnections: true,
beforeRequest: [addAuthHeader],
auth,
triggers,
dynamicData,
});

View File

@@ -0,0 +1,4 @@
import newCalendar from './new-calendar';
import newEvent from './new-event';
export default [newCalendar, newEvent];

View File

@@ -0,0 +1,34 @@
import defineTrigger from '../../../../helpers/define-trigger';
export default defineTrigger({
name: 'New calendar',
key: 'newCalendar',
pollInterval: 15,
description: 'Triggers when a new calendar is created.',
arguments: [],
async run($) {
const params: Record<string, unknown> = {
pageToken: undefined as unknown as string,
maxResults: 250,
};
do {
const { data } = await $.http.get('/v3/users/me/calendarList', {
params,
});
params.pageToken = data.nextPageToken;
if (data.items?.length) {
for (const calendar of data.items.reverse()) {
$.pushTriggerItem({
raw: calendar,
meta: {
internalId: calendar.etag,
},
});
}
}
} while (params.pageToken);
},
});

View File

@@ -0,0 +1,55 @@
import defineTrigger from '../../../../helpers/define-trigger';
export default defineTrigger({
name: 'New event',
key: 'newEvent',
pollInterval: 15,
description: 'Triggers when a new event is created.',
arguments: [
{
label: 'Calendar',
key: 'calendarId',
type: 'dropdown' as const,
required: true,
description: '',
variables: false,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listCalendars',
},
],
},
},
],
async run($) {
const calendarId = $.step.parameters.calendarId;
const params: Record<string, unknown> = {
pageToken: undefined as unknown as string,
orderBy: 'updated',
};
do {
const { data } = await $.http.get(`/v3/calendars/${calendarId}/events`, {
params,
});
params.pageToken = data.nextPageToken;
if (data.items?.length) {
for (const event of data.items.reverse()) {
$.pushTriggerItem({
raw: event,
meta: {
internalId: event.etag,
},
});
}
}
} while (params.pageToken);
},
});

View File

@@ -0,0 +1,639 @@
export const fields = [
{
label: 'Client Name',
key: 'clientName',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Contact First Name',
key: 'contactFirstName',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Contact Last Name',
key: 'contactLastName',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Contact Email',
key: 'contactEmail',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Contact Phone',
key: 'contactPhone',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Language Code',
key: 'languageCode',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
options: [
{ value: 1, label: 'English - United States' },
{ value: 2, label: 'Italian' },
{ value: 3, label: 'German' },
{ value: 4, label: 'French' },
{ value: 5, label: 'Portuguese - Brazilian' },
{ value: 6, label: 'Dutch' },
{ value: 7, label: 'Spanish' },
{ value: 8, label: 'Norwegian' },
{ value: 9, label: 'Danish' },
{ value: 10, label: 'Japanese' },
{ value: 11, label: 'Swedish' },
{ value: 12, label: 'Spanish - Spain' },
{ value: 13, label: 'French - Canada' },
{ value: 14, label: 'Lithuanian' },
{ value: 15, label: 'Polish' },
{ value: 16, label: 'Czech' },
{ value: 17, label: 'Croatian' },
{ value: 18, label: 'Albanian' },
{ value: 19, label: 'Greek' },
{ value: 20, label: 'English - United Kingdom' },
{ value: 21, label: 'Portuguese - Portugal' },
{ value: 22, label: 'Slovenian' },
{ value: 23, label: 'Finnish' },
{ value: 24, label: 'Romanian' },
{ value: 25, label: 'Turkish - Turkey' },
{ value: 26, label: 'Thai' },
{ value: 27, label: 'Macedonian' },
{ value: 28, label: 'Chinese - Taiwan' },
{ value: 29, label: 'Russian (Russia)' },
{ value: 30, label: 'Arabic' },
{ value: 31, label: 'Persian' },
{ value: 32, label: 'Latvian' },
{ value: 33, label: 'Serbian' },
{ value: 34, label: 'Slovak' },
{ value: 35, label: 'Estonian' },
{ value: 36, label: 'Bulgarian' },
{ value: 37, label: 'Hebrew' },
{ value: 38, label: 'Khmer' },
{ value: 39, label: 'Hungarian' },
{ value: 40, label: 'French - Swiss' },
],
},
{
label: 'Currency Code',
key: 'currencyCode',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
options: [
{ value: 1, label: 'US Dollar' },
{ value: 2, label: 'British Pound' },
{ value: 3, label: 'Euro' },
{ value: 4, label: 'South African Rand' },
{ value: 5, label: 'Danish Krone' },
{ value: 6, label: 'Israeli Shekel' },
{ value: 7, label: 'Swedish Krona' },
{ value: 8, label: 'Kenyan Shilling' },
{ value: 9, label: 'Canadian Dollar' },
{ value: 10, label: 'Philippine Peso' },
{ value: 11, label: 'Indian Rupee' },
{ value: 12, label: 'Australian Dollar' },
{ value: 13, label: 'Singapore Dollar' },
{ value: 14, label: 'Norske Kroner' },
{ value: 15, label: 'New Zealand Dollar' },
{ value: 16, label: 'Vietnamese Dong' },
{ value: 17, label: 'Swiss Franc' },
{ value: 18, label: 'Guatemalan Quetzal' },
{ value: 19, label: 'Malaysian Ringgit' },
{ value: 20, label: 'Brazilian Real' },
{ value: 21, label: 'Thai Baht' },
{ value: 22, label: 'Nigerian Naira' },
{ value: 23, label: 'Argentine Peso' },
{ value: 24, label: 'Bangladeshi Taka' },
{ value: 25, label: 'United Arab Emirates Dirham' },
{ value: 26, label: 'Hong Kong Dollar' },
{ value: 27, label: 'Indonesian Rupiah' },
{ value: 28, label: 'Mexican Peso' },
{ value: 29, label: 'Egyptian Pound' },
{ value: 30, label: 'Colombian Peso' },
{ value: 31, label: 'West African Franc' },
{ value: 32, label: 'Chinese Renminbi' },
{ value: 33, label: 'Rwandan Franc' },
{ value: 34, label: 'Tanzanian Shilling' },
{ value: 35, label: 'Netherlands Antillean Guilder' },
{ value: 36, label: 'Trinidad and Tobago Dollar' },
{ value: 37, label: 'East Caribbean Dollar' },
{ value: 38, label: 'Ghanaian Cedi' },
{ value: 39, label: 'Bulgarian Lev' },
{ value: 40, label: 'Aruban Florin' },
{ value: 41, label: 'Turkish Lira' },
{ value: 42, label: 'Romanian New Leu' },
{ value: 43, label: 'Croatian Kuna' },
{ value: 44, label: 'Saudi Riyal' },
{ value: 45, label: 'Japanese Yen' },
{ value: 46, label: 'Maldivian Rufiyaa' },
{ value: 47, label: 'Costa Rican Colón' },
{ value: 48, label: 'Pakistani Rupee' },
{ value: 49, label: 'Polish Zloty' },
{ value: 50, label: 'Sri Lankan Rupee' },
{ value: 51, label: 'Czech Koruna' },
{ value: 52, label: 'Uruguayan Peso' },
{ value: 53, label: 'Namibian Dollar' },
{ value: 54, label: 'Tunisian Dinar' },
{ value: 55, label: 'Russian Ruble' },
{ value: 56, label: 'Mozambican Metical' },
{ value: 57, label: 'Omani Rial' },
{ value: 58, label: 'Ukrainian Hryvnia' },
{ value: 59, label: 'Macanese Pataca' },
{ value: 60, label: 'Taiwan New Dollar' },
{ value: 61, label: 'Dominican Peso' },
{ value: 62, label: 'Chilean Peso' },
{ value: 63, label: 'Icelandic Króna' },
{ value: 64, label: 'Papua New Guinean Kina' },
{ value: 65, label: 'Jordanian Dinar' },
{ value: 66, label: 'Myanmar Kyat' },
{ value: 67, label: 'Peruvian Sol' },
{ value: 68, label: 'Botswana Pula' },
{ value: 69, label: 'Hungarian Forint' },
{ value: 70, label: 'Ugandan Shilling' },
{ value: 71, label: 'Barbadian Dollar' },
{ value: 72, label: 'Brunei Dollar' },
{ value: 73, label: 'Georgian Lari' },
{ value: 74, label: 'Qatari Riyal' },
{ value: 75, label: 'Honduran Lempira' },
{ value: 76, label: 'Surinamese Dollar' },
{ value: 77, label: 'Bahraini Dinar' },
{ value: 78, label: 'Venezuelan Bolivars' },
{ value: 79, label: 'South Korean Won' },
{ value: 80, label: 'Moroccan Dirham' },
{ value: 81, label: 'Jamaican Dollar' },
{ value: 82, label: 'Angolan Kwanza' },
{ value: 83, label: 'Haitian Gourde' },
{ value: 84, label: 'Zambian Kwacha' },
{ value: 85, label: 'Nepalese Rupee' },
{ value: 86, label: 'CFP Franc' },
{ value: 87, label: 'Mauritian Rupee' },
{ value: 88, label: 'Cape Verdean Escudo' },
{ value: 89, label: 'Kuwaiti Dinar' },
{ value: 90, label: 'Algerian Dinar' },
{ value: 91, label: 'Macedonian Denar' },
{ value: 92, label: 'Fijian Dollar' },
{ value: 93, label: 'Bolivian Boliviano' },
{ value: 94, label: 'Albanian Lek' },
{ value: 95, label: 'Serbian Dinar' },
{ value: 96, label: 'Lebanese Pound' },
{ value: 97, label: 'Armenian Dram' },
{ value: 98, label: 'Azerbaijan Manat' },
{ value: 99, label: 'Bosnia and Herzegovina Convertible Mark' },
{ value: 100, label: 'Belarusian Ruble' },
{ value: 101, label: 'Gibraltar Pound' },
{ value: 102, label: 'Moldovan Leu' },
{ value: 103, label: 'Kazakhstani Tenge' },
{ value: 104, label: 'Ethiopian Birr' },
{ value: 105, label: 'Gambia Dalasi' },
{ value: 106, label: 'Paraguayan Guarani' },
{ value: 107, label: 'Malawi Kwacha' },
{ value: 108, label: 'Zimbabwean Dollar' },
{ value: 109, label: 'Cambodian Riel' },
{ value: 110, label: 'Vanuatu Vatu' },
{ value: 111, label: 'Cuban Peso' },
{ value: 112, label: 'Cayman Island Dollar' },
{ value: 113, label: 'Swazi lilangeni' },
{ value: 114, label: 'BZ Dollar' },
{ value: 115, label: 'Libyan Dinar' },
{ value: 116, label: 'Silver Troy Ounce' },
{ value: 117, label: 'Gold Troy Ounce' },
{ value: 118, label: 'Nicaraguan Córdoba' },
],
},
{
label: 'Id Number',
key: 'idNumber',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Vat Number',
key: 'vatNumber',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Street Address',
key: 'streetAddress',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Apt/Suite',
key: 'aptSuite',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'City',
key: 'city',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'State/Province',
key: 'stateProvince',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Postal Code',
key: 'postalCode',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Country Code',
key: 'countryCode',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
options: [
{ value: 4, label: 'Afghanistan' },
{ value: 8, label: 'Albania' },
{ value: 12, label: 'Algeria' },
{ value: 16, label: 'American Samoa' },
{ value: 20, label: 'Andorra' },
{ value: 24, label: 'Angola' },
{ value: 660, label: 'Anguilla' },
{ value: 10, label: 'Antarctica' },
{ value: 28, label: 'Antigua and Barbuda' },
{ value: 32, label: 'Argentina' },
{ value: 51, label: 'Armenia' },
{ value: 533, label: 'Aruba' },
{ value: 36, label: 'Australia' },
{ value: 40, label: 'Austria' },
{ value: 31, label: 'Azerbaijan' },
{ value: 44, label: 'Bahamas' },
{ value: 48, label: 'Bahrain' },
{ value: 50, label: 'Bangladesh' },
{ value: 52, label: 'Barbados' },
{ value: 112, label: 'Belarus' },
{ value: 56, label: 'Belgium' },
{ value: 84, label: 'Belize' },
{ value: 204, label: 'Benin' },
{ value: 60, label: 'Bermuda' },
{ value: 64, label: 'Bhutan' },
{ value: 68, label: 'Bolivia, Plurinational State of' },
{ value: 535, label: 'Bonaire, Sint Eustatius and Saba' },
{ value: 70, label: 'Bosnia and Herzegovina' },
{ value: 72, label: 'Botswana' },
{ value: 74, label: 'Bouvet Island' },
{ value: 76, label: 'Brazil' },
{ value: 86, label: 'British Indian Ocean Territory' },
{ value: 96, label: 'Brunei Darussalam' },
{ value: 100, label: 'Bulgaria' },
{ value: 854, label: 'Burkina Faso' },
{ value: 108, label: 'Burundi' },
{ value: 116, label: 'Cambodia' },
{ value: 120, label: 'Cameroon' },
{ value: 124, label: 'Canada' },
{ value: 132, label: 'Cape Verde' },
{ value: 136, label: 'Cayman Islands' },
{ value: 140, label: 'Central African Republic' },
{ value: 148, label: 'Chad' },
{ value: 152, label: 'Chile' },
{ value: 156, label: 'China' },
{ value: 162, label: 'Christmas Island' },
{ value: 166, label: 'Cocos (Keeling) Islands' },
{ value: 170, label: 'Colombia' },
{ value: 174, label: 'Comoros' },
{ value: 178, label: 'Congo' },
{ value: 180, label: 'Congo, the Democratic Republic of the' },
{ value: 184, label: 'Cook Islands' },
{ value: 188, label: 'Costa Rica' },
{ value: 191, label: 'Croatia' },
{ value: 192, label: 'Cuba' },
{ value: 531, label: 'Curaçao' },
{ value: 196, label: 'Cyprus' },
{ value: 203, label: 'Czech Republic' },
{ value: 384, label: "Côte d'Ivoire" },
{ value: 208, label: 'Denmark' },
{ value: 262, label: 'Djibouti' },
{ value: 212, label: 'Dominica' },
{ value: 214, label: 'Dominican Republic' },
{ value: 218, label: 'Ecuador' },
{ value: 818, label: 'Egypt' },
{ value: 222, label: 'El Salvador' },
{ value: 226, label: 'Equatorial Guinea' },
{ value: 232, label: 'Eritrea' },
{ value: 233, label: 'Estonia' },
{ value: 231, label: 'Ethiopia' },
{ value: 238, label: 'Falkland Islands (Malvinas)' },
{ value: 234, label: 'Faroe Islands' },
{ value: 242, label: 'Fiji' },
{ value: 246, label: 'Finland' },
{ value: 250, label: 'France' },
{ value: 254, label: 'French Guiana' },
{ value: 258, label: 'French Polynesia' },
{ value: 260, label: 'French Southern Territories' },
{ value: 266, label: 'Gabon' },
{ value: 270, label: 'Gambia' },
{ value: 268, label: 'Georgia' },
{ value: 276, label: 'Germany' },
{ value: 288, label: 'Ghana' },
{ value: 292, label: 'Gibraltar' },
{ value: 300, label: 'Greece' },
{ value: 304, label: 'Greenland' },
{ value: 308, label: 'Grenada' },
{ value: 312, label: 'Guadeloupe' },
{ value: 316, label: 'Guam' },
{ value: 320, label: 'Guatemala' },
{ value: 831, label: 'Guernsey' },
{ value: 324, label: 'Guinea' },
{ value: 624, label: 'Guinea-Bissau' },
{ value: 328, label: 'Guyana' },
{ value: 332, label: 'Haiti' },
{ value: 334, label: 'Heard Island and McDonald Islands' },
{ value: 336, label: 'Holy See (Vatican City State)' },
{ value: 340, label: 'Honduras' },
{ value: 344, label: 'Hong Kong' },
{ value: 348, label: 'Hungary' },
{ value: 352, label: 'Iceland' },
{ value: 356, label: 'India' },
{ value: 360, label: 'Indonesia' },
{ value: 364, label: 'Iran, Islamic Republic of' },
{ value: 368, label: 'Iraq' },
{ value: 372, label: 'Ireland' },
{ value: 833, label: 'Isle of Man' },
{ value: 376, label: 'Israel' },
{ value: 380, label: 'Italy' },
{ value: 388, label: 'Jamaica' },
{ value: 392, label: 'Japan' },
{ value: 832, label: 'Jersey' },
{ value: 400, label: 'Jordan' },
{ value: 398, label: 'Kazakhstan' },
{ value: 404, label: 'Kenya' },
{ value: 296, label: 'Kiribati' },
{ value: 408, label: "Korea, Democratic People's Republic of" },
{ value: 410, label: 'Korea, Republic of' },
{ value: 414, label: 'Kuwait' },
{ value: 417, label: 'Kyrgyzstan' },
{ value: 418, label: "Lao People's Democratic Republic" },
{ value: 428, label: 'Latvia' },
{ value: 422, label: 'Lebanon' },
{ value: 426, label: 'Lesotho' },
{ value: 430, label: 'Liberia' },
{ value: 434, label: 'Libya' },
{ value: 438, label: 'Liechtenstein' },
{ value: 440, label: 'Lithuania' },
{ value: 442, label: 'Luxembourg' },
{ value: 446, label: 'Macao' },
{ value: 807, label: 'Macedonia, the former Yugoslav Republic of' },
{ value: 450, label: 'Madagascar' },
{ value: 454, label: 'Malawi' },
{ value: 458, label: 'Malaysia' },
{ value: 462, label: 'Maldives' },
{ value: 466, label: 'Mali' },
{ value: 470, label: 'Malta' },
{ value: 584, label: 'Marshall Islands' },
{ value: 474, label: 'Martinique' },
{ value: 478, label: 'Mauritania' },
{ value: 480, label: 'Mauritius' },
{ value: 175, label: 'Mayotte' },
{ value: 484, label: 'Mexico' },
{ value: 583, label: 'Micronesia, Federated States of' },
{ value: 498, label: 'Moldova, Republic of' },
{ value: 492, label: 'Monaco' },
{ value: 496, label: 'Mongolia' },
{ value: 499, label: 'Montenegro' },
{ value: 500, label: 'Montserrat' },
{ value: 504, label: 'Morocco' },
{ value: 508, label: 'Mozambique' },
{ value: 104, label: 'Myanmar' },
{ value: 516, label: 'Namibia' },
{ value: 520, label: 'Nauru' },
{ value: 524, label: 'Nepal' },
{ value: 528, label: 'Netherlands' },
{ value: 540, label: 'New Caledonia' },
{ value: 554, label: 'New Zealand' },
{ value: 558, label: 'Nicaragua' },
{ value: 562, label: 'Niger' },
{ value: 566, label: 'Nigeria' },
{ value: 570, label: 'Niue' },
{ value: 574, label: 'Norfolk Island' },
{ value: 580, label: 'Northern Mariana Islands' },
{ value: 578, label: 'Norway' },
{ value: 512, label: 'Oman' },
{ value: 586, label: 'Pakistan' },
{ value: 585, label: 'Palau' },
{ value: 275, label: 'Palestine' },
{ value: 591, label: 'Panama' },
{ value: 598, label: 'Papua New Guinea' },
{ value: 600, label: 'Paraguay' },
{ value: 604, label: 'Peru' },
{ value: 608, label: 'Philippines' },
{ value: 612, label: 'Pitcairn' },
{ value: 616, label: 'Poland' },
{ value: 620, label: 'Portugal' },
{ value: 630, label: 'Puerto Rico' },
{ value: 634, label: 'Qatar' },
{ value: 642, label: 'Romania' },
{ value: 643, label: 'Russian Federation' },
{ value: 646, label: 'Rwanda' },
{ value: 638, label: 'Réunion' },
{ value: 652, label: 'Saint Barthélemy' },
{ value: 654, label: 'Saint Helena, Ascension and Tristan da Cunha' },
{ value: 659, label: 'Saint Kitts and Nevis' },
{ value: 662, label: 'Saint Lucia' },
{ value: 663, label: 'Saint Martin (French part)' },
{ value: 666, label: 'Saint Pierre and Miquelon' },
{ value: 670, label: 'Saint Vincent and the Grenadines' },
{ value: 882, label: 'Samoa' },
{ value: 674, label: 'San Marino' },
{ value: 678, label: 'Sao Tome and Principe' },
{ value: 682, label: 'Saudi Arabia' },
{ value: 686, label: 'Senegal' },
{ value: 688, label: 'Serbia' },
{ value: 690, label: 'Seychelles' },
{ value: 694, label: 'Sierra Leone' },
{ value: 702, label: 'Singapore' },
{ value: 534, label: 'Sint Maarten (Dutch part)' },
{ value: 703, label: 'Slovakia' },
{ value: 705, label: 'Slovenia' },
{ value: 90, label: 'Solomon Islands' },
{ value: 706, label: 'Somalia' },
{ value: 710, label: 'South Africa' },
{ value: 239, label: 'South Georgia and the South Sandwich Islands' },
{ value: 728, label: 'South Sudan' },
{ value: 724, label: 'Spain' },
{ value: 144, label: 'Sri Lanka' },
{ value: 729, label: 'Sudan' },
{ value: 740, label: 'Suriname' },
{ value: 744, label: 'Svalbard and Jan Mayen' },
{ value: 748, label: 'Swaziland' },
{ value: 752, label: 'Sweden' },
{ value: 756, label: 'Switzerland' },
{ value: 760, label: 'Syrian Arab Republic' },
{ value: 158, label: 'Taiwan, Province of China' },
{ value: 762, label: 'Tajikistan' },
{ value: 834, label: 'Tanzania, United Republic of' },
{ value: 764, label: 'Thailand' },
{ value: 626, label: 'Timor-Leste' },
{ value: 768, label: 'Togo' },
{ value: 772, label: 'Tokelau' },
{ value: 776, label: 'Tonga' },
{ value: 780, label: 'Trinidad and Tobago' },
{ value: 788, label: 'Tunisia' },
{ value: 792, label: 'Turkey' },
{ value: 795, label: 'Turkmenistan' },
{ value: 796, label: 'Turks and Caicos Islands' },
{ value: 798, label: 'Tuvalu' },
{ value: 800, label: 'Uganda' },
{ value: 804, label: 'Ukraine' },
{ value: 784, label: 'United Arab Emirates' },
{ value: 826, label: 'United Kingdom' },
{ value: 840, label: 'United States' },
{ value: 581, label: 'United States Minor Outlying Islands' },
{ value: 858, label: 'Uruguay' },
{ value: 860, label: 'Uzbekistan' },
{ value: 548, label: 'Vanuatu' },
{ value: 862, label: 'Venezuela, Bolivarian Republic of' },
{ value: 704, label: 'Viet Nam' },
{ value: 92, label: 'Virgin Islands, British' },
{ value: 850, label: 'Virgin Islands, U.S.' },
{ value: 876, label: 'Wallis and Futuna' },
{ value: 732, label: 'Western Sahara' },
{ value: 887, label: 'Yemen' },
{ value: 894, label: 'Zambia' },
{ value: 716, label: 'Zimbabwe' },
{ value: 248, label: 'Åland Islands' },
],
},
{
label: 'Shipping Street Address',
key: 'shippingStreetAddress',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Shipping Apt/Suite',
key: 'shippingAptSuite',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Shipping City',
key: 'shippingCity',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Shipping State/Province',
key: 'shippingStateProvince',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Shipping Postal Code',
key: 'shippingPostalCode',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Shipping Country Code',
key: 'shippingCountryCode',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Private Notes',
key: 'privateNotes',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Public Notes',
key: 'publicNotes',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Website',
key: 'website',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Value 1',
key: 'customValue1',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Value 2',
key: 'customValue2',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Value 3',
key: 'customValue3',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Value 4',
key: 'customValue4',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
];

View File

@@ -0,0 +1,84 @@
import defineAction from '../../../../helpers/define-action';
import { filterProvidedFields } from '../../common/filter-provided-fields';
import { fields } from './fields';
export default defineAction({
name: 'Create client',
key: 'createClient',
description: 'Creates a new client.',
arguments: fields,
async run($) {
const {
clientName,
contactFirstName,
contactLastName,
contactEmail,
contactPhone,
languageCode,
currencyCode,
idNumber,
vatNumber,
streetAddress,
aptSuite,
city,
stateProvince,
postalCode,
countryCode,
shippingStreetAddress,
shippingAptSuite,
shippingCity,
shippingStateProvince,
shippingPostalCode,
shippingCountryCode,
privateNotes,
publicNotes,
website,
customValue1,
customValue2,
customValue3,
customValue4,
} = $.step.parameters;
const bodyFields = {
name: clientName,
contacts: {
first_name: contactFirstName,
last_name: contactLastName,
email: contactEmail,
phone: contactPhone,
},
settings: {
language_id: languageCode,
currency_id: currencyCode,
},
id_number: idNumber,
vat_number: vatNumber,
address1: streetAddress,
address2: aptSuite,
city: city,
state: stateProvince,
postal_code: postalCode,
country_id: countryCode,
shipping_address1: shippingStreetAddress,
shipping_address2: shippingAptSuite,
shipping_city: shippingCity,
shipping_state: shippingStateProvince,
shipping_postal_code: shippingPostalCode,
shipping_country_id: shippingCountryCode,
private_notes: privateNotes,
public_notes: publicNotes,
website: website,
custom_value1: customValue1,
custom_value2: customValue2,
custom_value3: customValue3,
custom_value4: customValue4,
};
const body = filterProvidedFields(bodyFields);
const response = await $.http.post('/v1/clients', body);
$.setActionItem({ raw: response.data.data });
},
});

View File

@@ -0,0 +1,407 @@
export const fields = [
{
label: 'Client ID',
key: 'clientId',
type: 'dropdown' as const,
required: true,
description: 'The ID of the client, not the name or email address.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listClients',
},
],
},
},
{
label: 'Send Email',
key: 'sendEmail',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
options: [
{ label: 'False', value: 'false' },
{ label: 'True', value: 'true' },
],
},
{
label: 'Mark Sent',
key: 'markSent',
type: 'dropdown' as const,
required: false,
description: 'Setting this to true creates the invoice as sent.',
variables: true,
options: [
{ label: 'False', value: 'false' },
{ label: 'True', value: 'true' },
],
},
{
label: 'Paid',
key: 'paid',
type: 'dropdown' as const,
required: false,
description: 'Setting this to true creates the invoice as paid.',
variables: true,
options: [
{ label: 'False', value: 'false' },
{ label: 'True', value: 'true' },
],
},
{
label: 'Amount Paid',
key: 'amountPaid',
type: 'string' as const,
required: false,
description:
'If this value is greater than zero a payment will be created along with the invoice.',
variables: true,
},
{
label: 'Number',
key: 'number',
type: 'string' as const,
required: false,
description:
'The invoice number - is a unique alpha numeric number per invoice per company',
variables: true,
},
{
label: 'Discount',
key: 'discount',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'PO Number',
key: 'poNumber',
type: 'string' as const,
required: false,
description: 'The purchase order associated with this invoice',
variables: true,
},
{
label: 'Date',
key: 'date',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Due Date',
key: 'dueDate',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Rate 1',
key: 'taxRate1',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Name 1',
key: 'taxName1',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Rate 2',
key: 'taxRate2',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Name 2',
key: 'taxName2',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Rate 3',
key: 'taxRate3',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Name 3',
key: 'taxName3',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Field 1',
key: 'customField1',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Field 2',
key: 'customField2',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Field 3',
key: 'customField3',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Field 4',
key: 'customField4',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Surcharge 1',
key: 'customSurcharge1',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Surcharge 2',
key: 'customSurcharge2',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Surcharge 3',
key: 'customSurcharge3',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Surcharge 4',
key: 'customSurcharge4',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Is Amount Discount',
key: 'isAmountDiscount',
type: 'dropdown' as const,
required: false,
description:
'By default the discount is applied as a percentage, enabling this applies the discount as a fixed amount.',
variables: true,
options: [
{ label: 'False', value: 'false' },
{ label: 'True', value: 'true' },
],
},
{
label: 'Partial/Deposit',
key: 'partialDeposit',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Partial Due Date',
key: 'partialDueDate',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Cost',
key: 'lineItemCost',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Quatity',
key: 'lineItemQuantity',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Product',
key: 'lineItemProduct',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Discount',
key: 'lineItemDiscount',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Description',
key: 'lineItemDescription',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Tax Rate 1',
key: 'lineItemTaxRate1',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Tax Name 1',
key: 'lineItemTaxName1',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Tax Rate 2',
key: 'lineItemTaxRate2',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Tax Name 2',
key: 'lineItemTaxName2',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Tax Rate 3',
key: 'lineItemTaxRate3',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Tax Name 3',
key: 'lineItemTaxName3',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Custom Field 1',
key: 'lineItemCustomField1',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Custom Field 2',
key: 'lineItemCustomField2',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Custom Field 3',
key: 'lineItemCustomField3',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Custom Field 4',
key: 'lineItemCustomField4',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Line Item Product Cost',
key: 'lineItemProductCost',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Public Notes',
key: 'publicNotes',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Private Notes',
key: 'privateNotes',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Terms',
key: 'terms',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Footer',
key: 'footer',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
];

View File

@@ -0,0 +1,127 @@
import defineAction from '../../../../helpers/define-action';
import { filterProvidedFields } from '../../common/filter-provided-fields';
import { fields } from './fields';
export default defineAction({
name: 'Create invoice',
key: 'createInvoice',
description: 'Creates a new invoice.',
arguments: fields,
async run($) {
const {
clientId,
sendEmail,
markSent,
paid,
amountPaid,
number,
discount,
poNumber,
date,
dueDate,
taxRate1,
taxName1,
taxRate2,
taxName2,
taxRate3,
taxName3,
customField1,
customField2,
customField3,
customField4,
customSurcharge1,
customSurcharge2,
customSurcharge3,
customSurcharge4,
isAmountDiscount,
partialDeposit,
partialDueDate,
lineItemCost,
lineItemQuantity,
lineItemProduct,
lineItemDiscount,
lineItemDescription,
lineItemTaxRate1,
lineItemTaxName1,
lineItemTaxRate2,
lineItemTaxName2,
lineItemTaxRate3,
lineItemTaxName3,
lineItemCustomField1,
lineItemCustomField2,
lineItemCustomField3,
lineItemCustomField4,
lineItemProductCost,
publicNotes,
privateNotes,
terms,
footer,
} = $.step.parameters;
const paramFields = {
send_email: sendEmail,
mark_sent: markSent,
paid: paid,
amount_paid: amountPaid,
};
const params = filterProvidedFields(paramFields);
const bodyFields = {
client_id: clientId,
number: number,
discount: discount,
po_number: poNumber,
date: date,
due_date: dueDate,
tax_rate1: taxRate1,
tax_name1: taxName1,
tax_rate2: taxRate2,
tax_name2: taxName2,
tax_rate3: taxRate3,
tax_name3: taxName3,
custom_value1: customField1,
custom_value2: customField2,
custom_value3: customField3,
custom_value4: customField4,
custom_surcharge1: customSurcharge1,
custom_surcharge2: customSurcharge2,
custom_surcharge3: customSurcharge3,
custom_surcharge4: customSurcharge4,
is_amount_discount: Boolean(isAmountDiscount),
partial: partialDeposit,
partial_due_date: partialDueDate,
line_items: [
{
cost: lineItemCost,
quantity: lineItemQuantity,
product_key: lineItemProduct,
discount: lineItemDiscount,
notes: lineItemDescription,
tax_rate1: lineItemTaxRate1,
tax_name1: lineItemTaxName1,
tax_rate2: lineItemTaxRate2,
tax_name2: lineItemTaxName2,
tax_rate3: lineItemTaxRate3,
tax_name3: lineItemTaxName3,
custom_value1: lineItemCustomField1,
custom_value2: lineItemCustomField2,
custom_value3: lineItemCustomField3,
custom_value4: lineItemCustomField4,
product_cost: lineItemProductCost,
},
],
public_notes: publicNotes,
private_notes: privateNotes,
terms: terms,
footer: footer,
};
const body = filterProvidedFields(bodyFields);
const response = await $.http.post('/v1/invoices', body, { params });
$.setActionItem({ raw: response.data.data });
},
});

View File

@@ -0,0 +1,111 @@
export const fields = [
{
label: 'Client ID',
key: 'clientId',
type: 'dropdown' as const,
required: true,
description: 'The ID of the client, not the name or email address.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listClients',
},
],
},
},
{
label: 'Payment Date',
key: 'paymentDate',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Invoice',
key: 'invoiceId',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listInvoices',
},
],
},
},
{
label: 'Invoice Amount',
key: 'invoiceAmount',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Payment Type',
key: 'paymentType',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
options: [
{ label: 'Bank Transfer', value: '1' },
{ label: 'Cash', value: '2' },
{ label: 'Debit', value: '3' },
{ label: 'ACH', value: '4' },
{ label: 'Visa Card', value: '5' },
{ label: 'MasterCard', value: '6' },
{ label: 'American Express', value: '7' },
{ label: 'Discover Card', value: '8' },
{ label: 'Diners Card', value: '9' },
{ label: 'EuroCard', value: '10' },
{ label: 'Nova', value: '11' },
{ label: 'Credit Card Other', value: '12' },
{ label: 'PayPal', value: '13' },
{ label: 'Google Wallet', value: '14' },
{ label: 'Check', value: '15' },
{ label: 'Carte Blanche', value: '16' },
{ label: 'UnionPay', value: '17' },
{ label: 'JCB', value: '18' },
{ label: 'Laser', value: '19' },
{ label: 'Maestro', value: '20' },
{ label: 'Solo', value: '21' },
{ label: 'Switch', value: '22' },
{ label: 'iZettle', value: '23' },
{ label: 'Swish', value: '24' },
{ label: 'Venmo', value: '25' },
{ label: 'Money Order', value: '26' },
{ label: 'Alipay', value: '27' },
{ label: 'Sofort', value: '28' },
{ label: 'SEPA', value: '29' },
{ label: 'GoCardless', value: '30' },
{ label: 'Bitcoin', value: '31' },
],
},
{
label: 'Transfer Reference',
key: 'transferReference',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Private Notes',
key: 'privateNotes',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
];

View File

@@ -0,0 +1,42 @@
import defineAction from '../../../../helpers/define-action';
import { filterProvidedFields } from '../../common/filter-provided-fields';
import { fields } from './fields';
export default defineAction({
name: 'Create payment',
key: 'createPayment',
description: 'Creates a new payment.',
arguments: fields,
async run($) {
const {
clientId,
paymentDate,
invoiceId,
invoiceAmount,
paymentType,
transferReference,
privateNotes,
} = $.step.parameters;
const bodyFields = {
client_id: clientId,
date: paymentDate,
invoices: [
{
invoice_id: invoiceId,
amount: invoiceAmount,
},
],
type_id: paymentType,
transaction_reference: transferReference,
private_notes: privateNotes,
};
const body = filterProvidedFields(bodyFields);
const response = await $.http.post('/v1/payments', body);
$.setActionItem({ raw: response.data.data });
},
});

View File

@@ -0,0 +1,114 @@
export const fields = [
{
label: 'Product Key',
key: 'productKey',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Notes',
key: 'notes',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Price',
key: 'price',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Quantity',
key: 'quantity',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Rate 1',
key: 'taxRate1',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Name 1',
key: 'taxName1',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Rate 2',
key: 'taxRate2',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Name 2',
key: 'taxName2',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Rate 3',
key: 'taxRate3',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Tax Name 3',
key: 'taxName3',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Value 1',
key: 'customValue1',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Value 2',
key: 'customValue2',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Value 3',
key: 'customValue3',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Custom Value 4',
key: 'customValue4',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
];

View File

@@ -0,0 +1,52 @@
import defineAction from '../../../../helpers/define-action';
import { filterProvidedFields } from '../../common/filter-provided-fields';
import { fields } from './fields';
export default defineAction({
name: 'Create product',
key: 'createProduct',
description: 'Creates a new product.',
arguments: fields,
async run($) {
const {
productKey,
notes,
price,
quantity,
taxRate1,
taxName1,
taxRate2,
taxName2,
taxRate3,
taxName3,
customValue1,
customValue2,
customValue3,
customValue4,
} = $.step.parameters;
const bodyFields = {
product_key: productKey,
notes: notes,
price: price,
quantity: quantity,
tax_rate1: taxRate1,
tax_name1: taxName1,
tax_rate2: taxRate2,
tax_name2: taxName2,
tax_rate3: taxRate3,
tax_name3: taxName3,
custom_value1: customValue1,
custom_value2: customValue2,
custom_value3: customValue3,
custom_value4: customValue4,
};
const body = filterProvidedFields(bodyFields);
const response = await $.http.post('/v1/products', body);
$.setActionItem({ raw: response.data.data });
},
});

View File

@@ -0,0 +1,6 @@
import createClient from './create-client';
import createInvoice from './create-invoice';
import createPayment from './create-payment';
import createProduct from './create-product';
export default [createClient, createInvoice, createPayment, createProduct];

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg"><path d="M16.247 10.326a1.164 1.164 0 11-2.328 0 1.164 1.164 0 012.328 0zm-6.288 0a1.164 1.164 0 11-2.329 0 1.164 1.164 0 012.329 0zm-.14 13.52c-4.712-.98-8.227-4.257-9.482-8.842-.421-1.537-.421-4.49 0-6.027C1.506 4.709 4.73 1.485 8.997.316c1.538-.421 4.49-.421 6.028 0 4.267 1.169 7.492 4.393 8.66 8.66.24.874.294 1.43.294 3.014 0 1.584-.054 2.14-.293 3.014-1.17 4.271-4.439 7.536-8.661 8.65-1.391.367-3.916.46-5.206.192zm6.64-9.315c-3.047-1.348-4.054-1.737-4.5-1.737-.446 0-1.433.38-4.38 1.684-2.091.926-3.828 1.76-3.86 1.79h16.663zm-9.873-.361c1.621-.729 3.06-1.387 3.196-1.464.258-.145.337-.09-5.285-3.682-.56-.358-1.023-.698-1.025-.65V15.564a790.1 790.1 0 003.114-1.394zm14.078-2.194V8.417c0-.11-1.676.993-3.496 2.12-3 1.854-3.281 2.06-3.004 2.185 1.345.611 6.42 2.862 6.5 2.872zm-8.169.11c.545.125.643.104 1.226-.263.349-.22.655-.419.681-.442.026-.024-.05-.181-.167-.35-.118-.168-.215-.5-.215-.739V9.86l-.569.21c-.726.267-2.28.27-3 .005l-.556-.205.013.452c.007.26-.088.563-.225.715-.232.256-.22.276.45.726.64.432.725.455 1.23.327a2.349 2.349 0 011.132-.002zm-4.23-2.65c-.105-.113-2.97-.954-3.033-.891-.03.03.504.414 1.186.854l1.24.8.34-.344c.186-.188.307-.377.268-.42zm9.76-.373c.473-.306.8-.555.728-.555-.155 0-2.877.804-3.027.894-.057.034.033.229.2.433l.304.37.47-.293c.257-.162.854-.544 1.326-.85zm-1.636-.555c2.11-.59 3.867-1.102 3.904-1.139H3.59c.187.187 7.779 2.195 8.323 2.202.41.005 2.014-.376 4.476-1.063z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,33 @@
import verifyCredentials from './verify-credentials';
import isStillVerified from './is-still-verified';
export default {
fields: [
{
key: 'apiToken',
label: 'API Token',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description:
'Tokens can be created in the v5 app on Settings > Account Management',
clickToCopy: false,
},
{
key: 'instanceUrl',
label: 'Invoice Ninja instance URL (optional)',
type: 'string' as const,
required: false,
readOnly: false,
value: null,
placeholder: null,
description: "Leave this field blank if you're using hosted platform.",
clickToCopy: true,
},
],
verifyCredentials,
isStillVerified,
};

View File

@@ -0,0 +1,10 @@
import { IGlobalVariable } from '@automatisch/types';
import verifyCredentials from './verify-credentials';
const isStillVerified = async ($: IGlobalVariable) => {
await verifyCredentials($);
return true;
};
export default isStillVerified;

View File

@@ -0,0 +1,15 @@
import { IGlobalVariable } from '@automatisch/types';
const verifyCredentials = async ($: IGlobalVariable) => {
const { data } = await $.http.get('/v1/ping');
const screenName = [data.user_name, data.company_name]
.filter(Boolean)
.join(' @ ');
await $.auth.set({
screenName,
});
};
export default verifyCredentials;

View File

@@ -0,0 +1,20 @@
import { TBeforeRequest } from '@automatisch/types';
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
const { instanceUrl } = $.auth.data;
if (instanceUrl) {
requestConfig.baseURL = instanceUrl as string;
}
requestConfig.headers['X-API-TOKEN'] = $.auth.data.apiToken as string;
requestConfig.headers['X-Requested-With'] = 'XMLHttpRequest';
requestConfig.headers['Content-Type'] =
requestConfig.headers['Content-Type'] || 'application/json';
return requestConfig;
};
export default addAuthHeader;

View File

@@ -0,0 +1,18 @@
import isObject from 'lodash/isObject';
export function filterProvidedFields(body: Record<string, unknown>) {
return Object.keys(body).reduce<Record<string, unknown>>((result, key) => {
const value = body[key];
if (isObject(value)) {
const filteredNestedObj = filterProvidedFields(
value as Record<string, unknown>
);
if (Object.keys(filteredNestedObj).length > 0) {
result[key] = filteredNestedObj;
}
} else if (body[key]) {
result[key] = value;
}
return result;
}, {});
}

View File

@@ -0,0 +1,13 @@
import { TBeforeRequest } from '@automatisch/types';
const setBaseUrl: TBeforeRequest = ($, requestConfig) => {
const instanceUrl = $.auth.data.instanceUrl as string;
if (instanceUrl) {
requestConfig.baseURL = instanceUrl;
}
return requestConfig;
};
export default setBaseUrl;

View File

@@ -0,0 +1,4 @@
import listClients from './list-clients';
import listInvoices from './list-invoices';
export default [listClients, listInvoices];

View File

@@ -0,0 +1,35 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List clients',
key: 'listClients',
async run($: IGlobalVariable) {
const clients: {
data: IJSONObject[];
} = {
data: [],
};
const params = {
sort: 'created_at|desc',
};
const {
data: { data },
} = await $.http.get('/v1/clients', { params });
if (!data?.length) {
return;
}
for (const client of data) {
clients.data.push({
value: client.id,
name: client.name,
});
}
return clients;
},
};

View File

@@ -0,0 +1,35 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List invoices',
key: 'listInvoices',
async run($: IGlobalVariable) {
const invoices: {
data: IJSONObject[];
} = {
data: [],
};
const params = {
sort: 'created_at|desc',
};
const {
data: { data },
} = await $.http.get('/v1/invoices', { params });
if (!data?.length) {
return;
}
for (const invoice of data) {
invoices.data.push({
value: invoice.id,
name: invoice.number,
});
}
return invoices;
},
};

View File

View File

@@ -0,0 +1,23 @@
import defineApp from '../../helpers/define-app';
import setBaseUrl from './common/set-base-url';
import addAuthHeader from './common/add-auth-header';
import auth from './auth';
import triggers from './triggers';
import actions from './actions';
import dynamicData from './dynamic-data';
export default defineApp({
name: 'Invoice Ninja',
key: 'invoice-ninja',
baseUrl: 'https://invoiceninja.com',
apiBaseUrl: 'https://invoicing.co/api',
iconUrl: '{BASE_URL}/apps/invoice-ninja/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/invoice-ninja/connection',
primaryColor: '000000',
supportsConnections: true,
beforeRequest: [setBaseUrl, addAuthHeader],
auth,
triggers,
actions,
dynamicData,
});

View File

@@ -0,0 +1,15 @@
import newClients from './new-clients';
import newCredits from './new-credits';
import newInvoices from './new-invoices';
import newPayments from './new-payments';
import newProjects from './new-projects';
import newQuotes from './new-quotes';
export default [
newClients,
newCredits,
newInvoices,
newPayments,
newProjects,
newQuotes,
];

View File

@@ -0,0 +1,65 @@
import Crypto from 'crypto';
import isEmpty from 'lodash/isEmpty';
import defineTrigger from '../../../../helpers/define-trigger';
type Response = {
data: {
data: {
id: string;
event_id: string;
target_url: string;
format: string;
};
};
};
export default defineTrigger({
name: 'New clients',
key: 'newClients',
type: 'webhook',
description: 'Triggers when a new client is added.',
arguments: [],
async run($) {
const dataItem = {
raw: $.request.body,
meta: {
internalId: Crypto.randomUUID(),
},
};
$.pushTriggerItem(dataItem);
},
async testRun($) {
const lastExecutionStep = await $.getLastExecutionStep();
if (!isEmpty(lastExecutionStep?.dataOut)) {
$.pushTriggerItem({
raw: lastExecutionStep.dataOut,
meta: {
internalId: '',
},
});
}
},
async registerHook($) {
const CREATE_CLIENT_EVENT_ID = '1';
const payload = {
target_url: $.webhookUrl,
event_id: CREATE_CLIENT_EVENT_ID,
format: 'JSON',
rest_method: 'post',
};
const response: Response = await $.http.post('/v1/webhooks', payload);
await $.flow.setRemoteWebhookId(response.data.data.id);
},
async unregisterHook($) {
await $.http.delete(`/v1/webhooks/${$.flow.remoteWebhookId}`);
},
});

View File

@@ -0,0 +1,65 @@
import Crypto from 'crypto';
import isEmpty from 'lodash/isEmpty';
import defineTrigger from '../../../../helpers/define-trigger';
type Response = {
data: {
data: {
id: string;
event_id: string;
target_url: string;
format: string;
};
};
};
export default defineTrigger({
name: 'New credits',
key: 'newCredits',
type: 'webhook',
description: 'Triggers when a new credit is added.',
arguments: [],
async run($) {
const dataItem = {
raw: $.request.body,
meta: {
internalId: Crypto.randomUUID(),
},
};
$.pushTriggerItem(dataItem);
},
async testRun($) {
const lastExecutionStep = await $.getLastExecutionStep();
if (!isEmpty(lastExecutionStep?.dataOut)) {
$.pushTriggerItem({
raw: lastExecutionStep.dataOut,
meta: {
internalId: '',
},
});
}
},
async registerHook($) {
const CREATE_CREDIT_EVENT_ID = '27';
const payload = {
target_url: $.webhookUrl,
event_id: CREATE_CREDIT_EVENT_ID,
format: 'JSON',
rest_method: 'post',
};
const response: Response = await $.http.post('/v1/webhooks', payload);
await $.flow.setRemoteWebhookId(response.data.data.id);
},
async unregisterHook($) {
await $.http.delete(`/v1/webhooks/${$.flow.remoteWebhookId}`);
},
});

View File

@@ -0,0 +1,65 @@
import Crypto from 'crypto';
import isEmpty from 'lodash/isEmpty';
import defineTrigger from '../../../../helpers/define-trigger';
type Response = {
data: {
data: {
id: string;
event_id: string;
target_url: string;
format: string;
};
};
};
export default defineTrigger({
name: 'New invoices',
key: 'newInvoices',
type: 'webhook',
description: 'Triggers when a new invoice is added.',
arguments: [],
async run($) {
const dataItem = {
raw: $.request.body,
meta: {
internalId: Crypto.randomUUID(),
},
};
$.pushTriggerItem(dataItem);
},
async testRun($) {
const lastExecutionStep = await $.getLastExecutionStep();
if (!isEmpty(lastExecutionStep?.dataOut)) {
$.pushTriggerItem({
raw: lastExecutionStep.dataOut,
meta: {
internalId: '',
},
});
}
},
async registerHook($) {
const CREATE_INVOICE_EVENT_ID = '2';
const payload = {
target_url: $.webhookUrl,
event_id: CREATE_INVOICE_EVENT_ID,
format: 'JSON',
rest_method: 'post',
};
const response: Response = await $.http.post('/v1/webhooks', payload);
await $.flow.setRemoteWebhookId(response.data.data.id);
},
async unregisterHook($) {
await $.http.delete(`/v1/webhooks/${$.flow.remoteWebhookId}`);
},
});

View File

@@ -0,0 +1,65 @@
import Crypto from 'crypto';
import isEmpty from 'lodash/isEmpty';
import defineTrigger from '../../../../helpers/define-trigger';
type Response = {
data: {
data: {
id: string;
event_id: string;
target_url: string;
format: string;
};
};
};
export default defineTrigger({
name: 'New payments',
key: 'newPayments',
type: 'webhook',
description: 'Triggers when a new payment is added.',
arguments: [],
async run($) {
const dataItem = {
raw: $.request.body,
meta: {
internalId: Crypto.randomUUID(),
},
};
$.pushTriggerItem(dataItem);
},
async testRun($) {
const lastExecutionStep = await $.getLastExecutionStep();
if (!isEmpty(lastExecutionStep?.dataOut)) {
$.pushTriggerItem({
raw: lastExecutionStep.dataOut,
meta: {
internalId: '',
},
});
}
},
async registerHook($) {
const CREATE_PAYMENT_EVENT_ID = '4';
const payload = {
target_url: $.webhookUrl,
event_id: CREATE_PAYMENT_EVENT_ID,
format: 'JSON',
rest_method: 'post',
};
const response: Response = await $.http.post('/v1/webhooks', payload);
await $.flow.setRemoteWebhookId(response.data.data.id);
},
async unregisterHook($) {
await $.http.delete(`/v1/webhooks/${$.flow.remoteWebhookId}`);
},
});

View File

@@ -0,0 +1,65 @@
import Crypto from 'crypto';
import isEmpty from 'lodash/isEmpty';
import defineTrigger from '../../../../helpers/define-trigger';
type Response = {
data: {
data: {
id: string;
event_id: string;
target_url: string;
format: string;
};
};
};
export default defineTrigger({
name: 'New projects',
key: 'newProjects',
type: 'webhook',
description: 'Triggers when a new project is added.',
arguments: [],
async run($) {
const dataItem = {
raw: $.request.body,
meta: {
internalId: Crypto.randomUUID(),
},
};
$.pushTriggerItem(dataItem);
},
async testRun($) {
const lastExecutionStep = await $.getLastExecutionStep();
if (!isEmpty(lastExecutionStep?.dataOut)) {
$.pushTriggerItem({
raw: lastExecutionStep.dataOut,
meta: {
internalId: '',
},
});
}
},
async registerHook($) {
const CREATE_PROJECT_EVENT_ID = '25';
const payload = {
target_url: $.webhookUrl,
event_id: CREATE_PROJECT_EVENT_ID,
format: 'JSON',
rest_method: 'post',
};
const response: Response = await $.http.post('/v1/webhooks', payload);
await $.flow.setRemoteWebhookId(response.data.data.id);
},
async unregisterHook($) {
await $.http.delete(`/v1/webhooks/${$.flow.remoteWebhookId}`);
},
});

View File

@@ -0,0 +1,65 @@
import Crypto from 'crypto';
import isEmpty from 'lodash/isEmpty';
import defineTrigger from '../../../../helpers/define-trigger';
type Response = {
data: {
data: {
id: string;
event_id: string;
target_url: string;
format: string;
};
};
};
export default defineTrigger({
name: 'New quotes',
key: 'newQuotes',
type: 'webhook',
description: 'Triggers when a new quote is added.',
arguments: [],
async run($) {
const dataItem = {
raw: $.request.body,
meta: {
internalId: Crypto.randomUUID(),
},
};
$.pushTriggerItem(dataItem);
},
async testRun($) {
const lastExecutionStep = await $.getLastExecutionStep();
if (!isEmpty(lastExecutionStep?.dataOut)) {
$.pushTriggerItem({
raw: lastExecutionStep.dataOut,
meta: {
internalId: '',
},
});
}
},
async registerHook($) {
const CREATE_QUOTE_EVENT_ID = '3';
const payload = {
target_url: $.webhookUrl,
event_id: CREATE_QUOTE_EVENT_ID,
format: 'JSON',
rest_method: 'post',
};
const response: Response = await $.http.post('/v1/webhooks', payload);
await $.flow.setRemoteWebhookId(response.data.data.id);
},
async unregisterHook($) {
await $.http.delete(`/v1/webhooks/${$.flow.remoteWebhookId}`);
},
});

View File

@@ -0,0 +1,116 @@
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Copy board',
key: 'copyBoard',
description: 'Creates a copy of an existing board.',
arguments: [
{
label: 'Original board',
key: 'originalBoard',
type: 'dropdown' as const,
required: true,
description: 'The board that you want to copy.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listBoards',
},
],
},
},
{
label: 'Title',
key: 'title',
type: 'string' as const,
required: true,
description: 'Title for the board.',
variables: true,
},
{
label: 'Description',
key: 'description',
type: 'string' as const,
required: false,
description: 'Description of the board.',
variables: true,
},
{
label: 'Team Access',
key: 'teamAccess',
type: 'dropdown' as const,
required: false,
description:
'Team access to the board. Can be private, view, comment or edit. Default: private.',
variables: true,
options: [
{
label: 'Private - nobody in the team can find and access the board',
value: 'private',
},
{
label: 'View - any team member can find and view the board',
value: 'view',
},
{
label: 'Comment - any team member can find and comment the board',
value: 'comment',
},
{
label: 'Edit - any team member can find and edit the board',
value: 'edit',
},
],
},
{
label: 'Access Via Link',
key: 'accessViaLink',
type: 'dropdown' as const,
required: false,
description:
'Access to the board by link. Can be private, view, comment. Default: private.',
variables: true,
options: [
{
label: 'Private - only you have access to the board',
value: 'private',
},
{
label: 'View - can view, no sign-in required',
value: 'view',
},
{
label: 'Comment - can comment, no sign-in required',
value: 'comment',
},
],
},
],
async run($) {
const params = {
copy_from: $.step.parameters.originalBoard,
};
const body = {
name: $.step.parameters.title,
description: $.step.parameters.description,
policy: {
sharingPolicy: {
access: $.step.parameters.accessViaLink || 'private',
teamAccess: $.step.parameters.teamAccess || 'private',
},
},
};
const { data } = await $.http.put('/v2/boards', body, { params });
$.setActionItem({
raw: data,
});
},
});

View File

@@ -0,0 +1,94 @@
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Create board',
key: 'createBoard',
description: 'Creates a new board.',
arguments: [
{
label: 'Title',
key: 'title',
type: 'string' as const,
required: true,
description: 'Title for the board.',
variables: true,
},
{
label: 'Description',
key: 'description',
type: 'string' as const,
required: false,
description: 'Description of the board.',
variables: true,
},
{
label: 'Team Access',
key: 'teamAccess',
type: 'dropdown' as const,
required: false,
description:
'Team access to the board. Can be private, view, comment or edit. Default: private.',
variables: true,
options: [
{
label: 'Private - nobody in the team can find and access the board',
value: 'private',
},
{
label: 'View - any team member can find and view the board',
value: 'view',
},
{
label: 'Comment - any team member can find and comment the board',
value: 'comment',
},
{
label: 'Edit - any team member can find and edit the board',
value: 'edit',
},
],
},
{
label: 'Access Via Link',
key: 'accessViaLink',
type: 'dropdown' as const,
required: false,
description:
'Access to the board by link. Can be private, view, comment. Default: private.',
variables: true,
options: [
{
label: 'Private - only you have access to the board',
value: 'private',
},
{
label: 'View - can view, no sign-in required',
value: 'view',
},
{
label: 'Comment - can comment, no sign-in required',
value: 'comment',
},
],
},
],
async run($) {
const body = {
name: $.step.parameters.title,
description: $.step.parameters.description,
policy: {
sharingPolicy: {
access: $.step.parameters.accessViaLink || 'private',
teamAccess: $.step.parameters.teamAccess || 'private',
},
},
};
const { data } = await $.http.post('/v2/boards', body);
$.setActionItem({
raw: data,
});
},
});

View File

@@ -0,0 +1,168 @@
import defineAction from '../../../../helpers/define-action';
type Body = {
data: {
title: string;
description?: string;
dueDate?: string;
};
style?: {
cardTheme?: string;
};
parent: {
id: string;
};
};
export default defineAction({
name: 'Create card widget',
key: 'createCardWidget',
description: 'Creates a new card widget on an existing board.',
arguments: [
{
label: 'Board',
key: 'boardId',
type: 'dropdown' as const,
required: true,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listBoards',
},
],
},
},
{
label: 'Frame',
key: 'frameId',
type: 'dropdown' as const,
required: true,
dependsOn: ['parameters.boardId'],
description:
'You need to create a frame prior to this step. Switch frame to grid mode to avoid cards overlap.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listFrames',
},
{
name: 'parameters.boardId',
value: '{parameters.boardId}',
},
],
},
},
{
label: 'Card Title',
key: 'cardTitle',
type: 'string' as const,
required: true,
description: '',
variables: true,
},
{
label: 'Card Title Link',
key: 'cardTitleLink',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Card Description',
key: 'cardDescription',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Card Due Date',
key: 'cardDueDate',
type: 'string' as const,
required: false,
description:
'format: date-time. Example value: 2023-10-12 22:00:55+00:00',
variables: true,
},
{
label: 'Card Border Color',
key: 'cardBorderColor',
type: 'dropdown' as const,
required: false,
description: 'In hex format. Default is blue (#2399F3).',
variables: true,
options: [
{ label: 'white', value: '#FFFFFF' },
{ label: 'yellow', value: '#FEF445' },
{ label: 'orange', value: '#FAC710' },
{ label: 'red', value: '#F24726' },
{ label: 'bright red', value: '#DA0063' },
{ label: 'light gray', value: '#E6E6E6' },
{ label: 'gray', value: '#808080' },
{ label: 'black', value: '#1A1A1A' },
{ label: 'light green', value: '#CEE741' },
{ label: 'green', value: '#8FD14F' },
{ label: 'dark green', value: '#0CA789' },
{ label: 'light blue', value: '#12CDD4' },
{ label: 'blue', value: '#2D9BF0' },
{ label: 'dark blue', value: '#414BB2' },
{ label: 'purple', value: '#9510AC' },
{ label: 'dark purple', value: '#652CB3' },
],
},
],
async run($) {
const {
boardId,
frameId,
cardTitle,
cardTitleLink,
cardDescription,
cardDueDate,
cardBorderColor,
} = $.step.parameters;
let title;
if (cardTitleLink) {
title = `<a href='${cardTitleLink}'>${cardTitle}</a>`;
} else {
title = cardTitle;
}
const body: Body = {
data: {
title: title as string,
description: cardDescription as string,
},
style: {},
parent: {
id: frameId as string,
},
};
if (cardBorderColor) {
body.style.cardTheme = cardBorderColor as string;
}
if (cardDueDate) {
body.data.dueDate = cardDueDate as string;
}
const response = await $.http.post(`/v2/boards/${boardId}/cards`, body);
$.setActionItem({
raw: response.data,
});
},
});

View File

@@ -0,0 +1,5 @@
import copyBoard from './copy-board';
import createBoard from './create-board';
import createCardWidget from './create-card-widget';
export default [copyBoard, createBoard, createCardWidget];

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg" width="2500" height="2500"><path d="M0 0h48v48H0z" fill="#ffd02f"/><path d="M32.708 6.4h-5.124l4.549 7.05-9.617-7.05h-5.124l4.549 9.238L12.324 6.4H7.2l4.474 11.926L7.2 41.6h5.124l9.617-24.955L17.392 41.6h5.124l9.617-27.142-4.549 27.142h5.124L42.4 11.785z" fill="#050038"/></svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@@ -0,0 +1,20 @@
import { IField, IGlobalVariable } from '@automatisch/types';
import { URLSearchParams } from 'url';
export default async function generateAuthUrl($: IGlobalVariable) {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const searchParams = new URLSearchParams({
response_type: 'code',
client_id: $.auth.data.clientId as string,
redirect_uri: redirectUri,
});
const url = `https://miro.com/oauth/authorize?${searchParams.toString()}`;
await $.auth.set({
url,
});
}

View File

@@ -0,0 +1,48 @@
import generateAuthUrl from './generate-auth-url';
import verifyCredentials from './verify-credentials';
import refreshToken from './refresh-token';
import isStillVerified from './is-still-verified';
export default {
fields: [
{
key: 'oAuthRedirectUrl',
label: 'OAuth Redirect URL',
type: 'string' as const,
required: true,
readOnly: true,
value: '{WEB_APP_URL}/app/miro/connections/add',
placeholder: null,
description:
'When asked to input a redirect URL in Miro, enter the URL above.',
clickToCopy: true,
},
{
key: 'clientId',
label: 'Client ID',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
{
key: 'clientSecret',
label: 'Client Secret',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
],
generateAuthUrl,
verifyCredentials,
isStillVerified,
refreshToken,
};

View File

@@ -0,0 +1,9 @@
import { IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
const isStillVerified = async ($: IGlobalVariable) => {
const currentUser = await getCurrentUser($);
return !!currentUser;
};
export default isStillVerified;

View File

@@ -0,0 +1,23 @@
import { URLSearchParams } from 'node:url';
import { IGlobalVariable } from '@automatisch/types';
const refreshToken = async ($: IGlobalVariable) => {
const params = new URLSearchParams({
grant_type: 'refresh_token',
client_id: $.auth.data.clientId as string,
client_secret: $.auth.data.clientSecret as string,
refresh_token: $.auth.data.refreshToken as string,
});
const { data } = await $.http.post('/v1/oauth/token', params.toString());
await $.auth.set({
accessToken: data.access_token,
expiresIn: data.expires_in,
refreshToken: data.refresh_token,
scope: data.scope,
tokenType: data.token_type,
});
};
export default refreshToken;

View File

@@ -0,0 +1,40 @@
import { IField, IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
const verifyCredentials = async ($: IGlobalVariable) => {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const params = {
grant_type: 'authorization_code',
client_id: $.auth.data.clientId,
client_secret: $.auth.data.clientSecret,
code: $.auth.data.code,
redirect_uri: redirectUri,
};
const { data } = await $.http.post(`/v1/oauth/token`, null, {
params,
});
await $.auth.set({
accessToken: data.access_token,
tokenType: data.token_type,
});
const currentUser = await getCurrentUser($);
await $.auth.set({
userId: data.user_id,
refreshToken: data.refresh_token,
expiresIn: data.expires_in,
teamId: data.team_id,
scope: data.scope,
clientId: $.auth.data.clientId,
clientSecret: $.auth.data.clientSecret,
screenName: currentUser.name,
});
};
export default verifyCredentials;

View File

@@ -0,0 +1,11 @@
import { TBeforeRequest } from '@automatisch/types';
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
if ($.auth.data?.accessToken) {
requestConfig.headers.Authorization = `${$.auth.data.tokenType} ${$.auth.data.accessToken}`;
}
return requestConfig;
};
export default addAuthHeader;

View File

@@ -0,0 +1,10 @@
import { IGlobalVariable } from '@automatisch/types';
const getCurrentUser = async ($: IGlobalVariable) => {
const { data } = await $.http.get(
`https://api.miro.com/v1/oauth-token?access_token=${$.auth.data.accessToken}`
);
return data.user;
};
export default getCurrentUser;

View File

@@ -0,0 +1,4 @@
import listBoards from './list-boards';
import listFrames from './list-frames';
export default [listBoards, listFrames];

View File

@@ -0,0 +1,46 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
type ResponseBody = {
data: {
data: {
id: number;
name: string;
}[];
links: {
next: string;
};
};
};
export default {
name: 'List boards',
key: 'listBoards',
async run($: IGlobalVariable) {
const boards: {
data: IJSONObject[];
} = {
data: [],
};
let next;
do {
const {
data: { data, links },
}: ResponseBody = await $.http.get('/v2/boards');
next = links?.next;
if (data.length) {
for (const board of data) {
boards.data.push({
value: board.id,
name: board.name,
});
}
}
} while (next);
return boards;
},
};

View File

@@ -0,0 +1,44 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List frames',
key: 'listFrames',
async run($: IGlobalVariable) {
const frames: {
data: IJSONObject[];
} = {
data: [],
};
const boardId = $.step.parameters.boardId;
if (!boardId) {
return { data: [] };
}
let next;
do {
const {
data: { data, links },
} = await $.http.get(`/v2/boards/${boardId}/items`);
next = links?.next;
const allFrames = data.filter(
(item: IJSONObject) => item.type === 'frame'
);
if (allFrames.length) {
for (const frame of allFrames) {
frames.data.push({
value: frame.id,
name: frame.data.title,
});
}
}
} while (next);
return frames;
},
};

View File

View File

@@ -0,0 +1,20 @@
import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-header';
import auth from './auth';
import actions from './actions';
import dynamicData from './dynamic-data';
export default defineApp({
name: 'Miro',
key: 'miro',
baseUrl: 'https://miro.com',
apiBaseUrl: 'https://api.miro.com',
iconUrl: '{BASE_URL}/apps/miro/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/miro/connection',
primaryColor: 'F2CA02',
supportsConnections: true,
beforeRequest: [addAuthHeader],
auth,
actions,
dynamicData,
});

View File

@@ -0,0 +1,198 @@
import defineAction from '../../../../helpers/define-action';
import { filterProvidedFields } from '../../common/filter-provided-fields';
export default defineAction({
name: 'Create activity',
key: 'createActivity',
description: 'Creates a new activity.',
arguments: [
{
label: 'Subject',
key: 'subject',
type: 'string' as const,
required: true,
description: '',
variables: true,
},
{
label: 'Organization',
key: 'organizationId',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listOrganizations',
},
],
},
},
{
label: 'Assigned To',
key: 'userId',
type: 'dropdown' as const,
required: false,
description:
'If omitted, the activity will be assigned to the user of the connected account.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
],
},
},
{
label: 'Person',
key: 'personId',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listPersons',
},
],
},
},
{
label: 'Deal',
key: 'dealId',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listDeals',
},
],
},
},
{
label: 'Is done?',
key: 'isDone',
type: 'dropdown' as const,
required: false,
description: '',
options: [
{
label: 'No',
value: 0,
},
{
label: 'Yes',
value: 1,
},
],
},
{
label: 'Type',
key: 'type',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listActivityTypes',
},
],
},
},
{
label: 'Due Date',
key: 'dueDate',
type: 'string' as const,
required: false,
description: 'Format must be YYYY-MM-DD',
variables: true,
},
{
label: 'Due Time',
key: 'dueTime',
type: 'string' as const,
required: false,
description: 'Format must be HH:MM',
variables: true,
},
{
label: 'Duration',
key: 'duration',
type: 'string' as const,
required: false,
description: 'Format must be HH:MM',
variables: true,
},
{
label: 'Note',
key: 'note',
type: 'string' as const,
required: false,
description: 'Accepts HTML format',
variables: true,
},
],
async run($) {
const {
subject,
organizationId,
userId,
personId,
dealId,
isDone,
type,
dueTime,
dueDate,
duration,
note,
} = $.step.parameters;
const fields = {
subject: subject as string,
org_id: organizationId as number,
user_id: userId as number,
person_id: personId as number,
deal_id: dealId as number,
done: isDone as number,
type: type as string,
due_time: dueTime as string,
due_date: dueDate as string,
duration: duration as string,
note: note as string,
};
const body = filterProvidedFields(fields);
const {
data: { data },
} = await $.http.post('/api/v1/activities', body);
$.setActionItem({
raw: data,
});
},
});

View File

@@ -0,0 +1,237 @@
import defineAction from '../../../../helpers/define-action';
import { filterProvidedFields } from '../../common/filter-provided-fields';
export default defineAction({
name: 'Create deal',
key: 'createDeal',
description: 'Creates a new deal.',
arguments: [
{
label: 'Title',
key: 'title',
type: 'string' as const,
required: true,
description: '',
variables: true,
},
{
label: 'Creation Date',
key: 'addTime',
type: 'string' as const,
required: false,
description:
'Requires admin access to Pipedrive account. Format: YYYY-MM-DD HH:MM:SS',
variables: true,
},
{
label: 'Status',
key: 'status',
type: 'dropdown' as const,
required: false,
description: '',
options: [
{
label: 'Open',
value: 'open',
},
{
label: 'Won',
value: 'won',
},
{
label: 'Lost',
value: 'lost',
},
{
label: 'Deleted',
value: 'deleted',
},
],
},
{
label: 'Lost Reason',
key: 'lostReason',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
{
label: 'Stage',
key: 'stageId',
type: 'dropdown' as const,
required: false,
value: '1',
description:
'The ID of the stage this deal will be added to. If omitted, the deal will be placed in the first stage of the default pipeline.',
options: [
{
label: 'Qualified (Pipeline)',
value: 1,
},
{
label: 'Contact Made (Pipeline)',
value: 2,
},
{
label: 'Prospect Qualified (Pipeline)',
value: 3,
},
{
label: 'Needs Defined (Pipeline)',
value: 4,
},
{
label: 'Proposal Made (Pipeline)',
value: 5,
},
{
label: 'Negotiations Started (Pipeline)',
value: 6,
},
],
},
{
label: 'Owner',
key: 'userId',
type: 'dropdown' as const,
required: false,
description:
'Select user who will be marked as the owner of this deal. If omitted, the authorized user will be used.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
],
},
},
{
label: 'Organization',
key: 'organizationId',
type: 'dropdown' as const,
required: false,
description: 'Organization this deal will be associated with.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listOrganizations',
},
],
},
},
{
label: 'Person',
key: 'personId',
type: 'dropdown' as const,
required: false,
description: 'Person this deal will be associated with.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listPersons',
},
],
},
},
{
label: 'Probability',
key: 'probability',
type: 'string' as const,
required: false,
description:
'The success probability percentage of the deal. Used/shown only when deal_probability for the pipeline of the deal is enabled.',
variables: true,
},
{
label: 'Expected Close Date',
key: 'expectedCloseDate',
type: 'string' as const,
required: false,
description:
'The expected close date of the deal. In ISO 8601 format: YYYY-MM-DD.',
variables: true,
},
{
label: 'Value',
key: 'value',
type: 'string' as const,
required: false,
description: 'The value of the deal. If omitted, value will be set to 0.',
variables: true,
},
{
label: 'Currency',
key: 'currency',
type: 'dropdown' as const,
required: false,
description:
'The currency of the deal. Accepts a 3-character currency code. If omitted, currency will be set to the default currency of the authorized user.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listCurrencies',
},
],
},
},
],
async run($) {
const {
title,
addTime,
status,
lostReason,
stageId,
userId,
organizationId,
personId,
probability,
expectedCloseDate,
value,
currency,
} = $.step.parameters;
const fields = {
title: title as string,
value: value as string,
add_time: addTime as string,
status: status as string,
lost_reason: lostReason as string,
stage_id: stageId as number,
user_id: userId as number,
org_id: organizationId as number,
person_id: personId as number,
probability: probability as number,
expected_close_date: expectedCloseDate as string,
currency: currency as string,
};
const body = filterProvidedFields(fields);
const {
data: { data },
} = await $.http.post('/api/v1/deals', body);
$.setActionItem({
raw: data,
});
},
});

View File

@@ -0,0 +1,185 @@
import defineAction from '../../../../helpers/define-action';
import { filterProvidedFields } from '../../common/filter-provided-fields';
type LabelIds = { __id: string; leadLabelId: string }[];
type LabelValue = { amount?: number; currency?: string };
export default defineAction({
name: 'Create lead',
key: 'createLead',
description: 'Creates a new lead.',
arguments: [
{
label: 'Title',
key: 'title',
type: 'string' as const,
required: true,
description: '',
variables: true,
},
{
label: 'Person',
key: 'personId',
type: 'dropdown' as const,
required: false,
description:
'Lead must be associated with at least one person or organization.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listPersons',
},
],
},
},
{
label: 'Organization',
key: 'organizationId',
type: 'dropdown' as const,
required: false,
description:
'Lead must be associated with at least one person or organization.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listOrganizations',
},
],
},
},
{
label: 'Owner',
key: 'ownerId',
type: 'dropdown' as const,
required: false,
description:
'Select user who will be marked as the owner of this lead. If omitted, the authorized user will be used.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
],
},
},
{
label: 'Lead Labels',
key: 'labelIds',
type: 'dynamic' as const,
required: false,
description: '',
fields: [
{
label: 'Label',
key: 'leadLabelId',
type: 'dropdown' as const,
required: false,
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listLeadLabels',
},
],
},
},
],
},
{
label: 'Expected Close Date',
key: 'expectedCloseDate',
type: 'string' as const,
required: false,
description: 'E.g. 2023-10-23',
variables: true,
},
{
label: 'Lead Value',
key: 'value',
type: 'string' as const,
required: false,
description: 'E.g. 150',
variables: true,
},
{
label: 'Lead Value Currency',
key: 'currency',
type: 'dropdown' as const,
required: false,
description: 'This field is required if a Lead Value amount is provided.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listCurrencies',
},
],
},
},
],
async run($) {
const {
title,
personId,
organizationId,
ownerId,
labelIds,
expectedCloseDate,
value,
currency,
} = $.step.parameters;
const onlyLabelIds = (labelIds as LabelIds)
.map((labelId) => labelId.leadLabelId)
.filter(Boolean);
const labelValue: LabelValue = {};
if (value) {
labelValue.amount = Number(value);
}
if (currency) {
labelValue.currency = currency as string;
}
const fields = {
title: title as string,
person_id: Number(personId),
organization_id: Number(organizationId),
owner_id: Number(ownerId),
expected_close_date: expectedCloseDate as string,
label_ids: onlyLabelIds,
value: labelValue,
};
const body = filterProvidedFields(fields);
const {
data: { data },
} = await $.http.post('/api/v1/leads', body);
$.setActionItem({
raw: data,
});
},
});

View File

@@ -0,0 +1,198 @@
import defineAction from '../../../../helpers/define-action';
import { filterProvidedFields } from '../../common/filter-provided-fields';
export default defineAction({
name: 'Create note',
key: 'createNote',
description: 'Creates a new note.',
arguments: [
{
label: 'Content',
key: 'content',
type: 'string' as const,
required: true,
description: 'Supports some HTML formatting.',
variables: true,
},
{
label: 'Deal',
key: 'dealId',
type: 'dropdown' as const,
required: false,
description:
'Note must be associated with at least one deal, person, organization, or lead.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listDeals',
},
],
},
},
{
label: 'Pin note on specified deal?',
key: 'pinnedDeal',
type: 'dropdown' as const,
required: false,
description: '',
options: [
{
label: 'No',
value: 0,
},
{
label: 'Yes',
value: 1,
},
],
},
{
label: 'Person',
key: 'personId',
type: 'dropdown' as const,
required: false,
description:
'Note must be associated with at least one deal, person, organization, or lead.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listPersons',
},
],
},
},
{
label: 'Pin note on specified person?',
key: 'pinnedPerson',
type: 'dropdown' as const,
required: false,
description: '',
options: [
{
label: 'No',
value: 0,
},
{
label: 'Yes',
value: 1,
},
],
},
{
label: 'Organization',
key: 'organizationId',
type: 'dropdown' as const,
required: false,
description:
'Note must be associated with at least one deal, person, organization, or lead.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listOrganizations',
},
],
},
},
{
label: 'Pin note on specified organization?',
key: 'pinnedOrganization',
type: 'dropdown' as const,
required: false,
description: '',
options: [
{
label: 'No',
value: 0,
},
{
label: 'Yes',
value: 1,
},
],
},
{
label: 'Lead',
key: 'leadId',
type: 'dropdown' as const,
required: false,
description:
'Note must be associated with at least one deal, person, organization, or lead.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listLeads',
},
],
},
},
{
label: 'Pin note on specified lead?',
key: 'pinnedLead',
type: 'dropdown' as const,
required: false,
description: '',
options: [
{
label: 'No',
value: 0,
},
{
label: 'Yes',
value: 1,
},
],
},
],
async run($) {
const {
content,
dealId,
pinnedDeal,
personId,
pinnedPerson,
organizationId,
pinnedOrganization,
leadId,
pinnedLead,
} = $.step.parameters;
const fields = {
content: content as string,
deal_id: dealId as number,
pinned_to_deal_flag: pinnedDeal as number,
person_id: personId as number,
pinned_to_person_flag: pinnedPerson as number,
org_id: organizationId as number,
pinned_to_organization_flag: pinnedOrganization as number,
lead_id: leadId as string,
pinned_to_lead_flag: pinnedLead as number,
};
const body = filterProvidedFields(fields);
const {
data: { data },
} = await $.http.post('/api/v1/notes', body);
$.setActionItem({
raw: data,
});
},
});

View File

@@ -0,0 +1,74 @@
import defineAction from '../../../../helpers/define-action';
import { filterProvidedFields } from '../../common/filter-provided-fields';
export default defineAction({
name: 'Create organization',
key: 'createOrganization',
description: 'Creates a new organization.',
arguments: [
{
label: 'Name',
key: 'name',
type: 'string' as const,
required: true,
description: '',
variables: true,
},
{
label: 'Owner',
key: 'ownerId',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
],
},
},
{
label: 'Label',
key: 'labelId',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listOrganizationLabelField',
},
],
},
},
],
async run($) {
const { name, ownerId, labelId } = $.step.parameters;
const fields = {
name: name,
owner_id: ownerId,
label: labelId,
};
const body = filterProvidedFields(fields);
const {
data: { data },
} = await $.http.post('/api/v1/organizations', body);
$.setActionItem({
raw: data,
});
},
});

View File

@@ -0,0 +1,143 @@
import defineAction from '../../../../helpers/define-action';
import { filterProvidedFields } from '../../common/filter-provided-fields';
type TEmail = {
__id: string;
email: string;
}[];
type TPhone = {
__id: string;
phone: string;
}[];
export default defineAction({
name: 'Create person',
key: 'createPerson',
description: 'Creates a new person.',
arguments: [
{
label: 'Name',
key: 'name',
type: 'string' as const,
required: true,
description: '',
variables: true,
},
{
label: 'Owner',
key: 'ownerId',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
],
},
},
{
label: 'Organization',
key: 'organizationId',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listOrganizations',
},
],
},
},
{
label: 'Emails',
key: 'emails',
type: 'dynamic' as const,
required: false,
description: '',
fields: [
{
label: 'Email',
key: 'email',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
],
},
{
label: 'Phones',
key: 'phones',
type: 'dynamic' as const,
required: false,
description: '',
fields: [
{
label: 'Phone',
key: 'phone',
type: 'string' as const,
required: false,
description: '',
variables: true,
},
],
},
{
label: 'Label',
key: 'labelId',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listPersonLabelField',
},
],
},
},
],
async run($) {
const { name, ownerId, organizationId, labelId } = $.step.parameters;
const emails = $.step.parameters.emails as TEmail;
const emailValues = emails.map((entry) => entry.email);
const phones = $.step.parameters.phones as TPhone;
const phoneValues = phones.map((entry) => entry.phone);
const fields = {
name: name,
owner_id: ownerId,
org_id: organizationId,
email: emailValues,
phone: phoneValues,
label: labelId,
};
const body = filterProvidedFields(fields);
const {
data: { data },
} = await $.http.post('/api/v1/persons', body);
$.setActionItem({
raw: data,
});
},
});

View File

@@ -0,0 +1,15 @@
import createActivity from './create-activity';
import createDeal from './create-deal';
import createLead from './create-lead';
import createNote from './create-note';
import createOrganization from './create-organization';
import createPerson from './create-person';
export default [
createActivity,
createDeal,
createLead,
createNote,
createOrganization,
createPerson,
];

View File

@@ -0,0 +1 @@
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32" fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_503_588)" fill="#017737"><path d="M32 16C32 24.8366 24.8366 32 16 32C7.16344 32 0 24.8366 0 16C0 7.16344 7.16344 0 16 0C24.8366 0 32 7.16344 32 16Z" fill="#017737"></path><path d="M24.9842 13.4564C24.9842 17.8851 22.1247 20.914 18.036 20.914C16.0923 20.914 14.4903 20.1136 13.8906 19.1134L13.9189 20.142V26.4847H9.74512V10.0846C9.74512 9.85644 9.68836 9.79843 9.4304 9.79843H8V6.31321H11.4889C13.0896 6.31321 13.4907 7.68461 13.6042 8.28525C14.2337 7.22834 15.8911 6 18.2359 6C22.2679 5.99871 24.9842 8.99802 24.9842 13.4564ZM20.724 13.4847C20.724 11.1131 19.1801 9.48523 17.2351 9.48523C15.6344 9.48523 13.8325 10.5421 13.8325 13.5144C13.8325 15.4568 14.9186 17.4855 17.1783 17.4855C18.837 17.4842 20.724 16.2843 20.724 13.4847Z" fill="#FFFFFF"></path></g></svg>

After

Width:  |  Height:  |  Size: 929 B

View File

@@ -0,0 +1,19 @@
import { IField, IGlobalVariable } from '@automatisch/types';
import { URLSearchParams } from 'url';
export default async function generateAuthUrl($: IGlobalVariable) {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const searchParams = new URLSearchParams({
client_id: $.auth.data.clientId as string,
redirect_uri: redirectUri,
});
const url = `https://oauth.pipedrive.com/oauth/authorize?${searchParams.toString()}`;
await $.auth.set({
url,
});
}

View File

@@ -0,0 +1,48 @@
import generateAuthUrl from './generate-auth-url';
import verifyCredentials from './verify-credentials';
import refreshToken from './refresh-token';
import isStillVerified from './is-still-verified';
export default {
fields: [
{
key: 'oAuthRedirectUrl',
label: 'OAuth Redirect URL',
type: 'string' as const,
required: true,
readOnly: true,
value: '{WEB_APP_URL}/app/pipedrive/connections/add',
placeholder: null,
description:
'When asked to input a redirect URL in Pipedrive, enter the URL above.',
clickToCopy: true,
},
{
key: 'clientId',
label: 'Client ID',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
{
key: 'clientSecret',
label: 'Client Secret',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
],
generateAuthUrl,
verifyCredentials,
isStillVerified,
refreshToken,
};

Some files were not shown because too many files have changed in this diff Show More